// SPDX-FileCopyrightText: 2026 Progmasoft // SPDX-License-Identifier: AGPL-3.0-or-later WITH AdditionRef-Progmasoft-Patent-Grant-1.1 namespace Progmasoft.Progmaweb.Api.ViGet; /// The read endpoints of the Tazel plugin catalog. /// /// /// The pages of ViGet read the catalog from these endpoints on the loopback address of the API; Nginx does not pass /// /api/ of the ViGet host on. What the public reaches is the index of a plugin, which Tazel reads at /// https://viget.progmasoft.com/dslplugins/tazel/<Publisher>/<Name>/index.json: Nginx passes /// that address to /api/v1/viget/dslplugins/tazel/<Publisher>/<Name>/index.json, and serves the /// JARs from the files of ViGet itself. /// /// internal static class ViGetEndpoints { /// Maps the endpoints. /// The routes of the API. public static void MapViGetEndpoints(this IEndpointRouteBuilder app) { RouteGroupBuilder tazel = app.MapGroup("/api/v1/viget/dslplugins/tazel"); tazel.MapGet("/", async (ITazelPluginCatalog catalog, CancellationToken cancellationToken) => { IReadOnlyList plugins = await catalog.ListAsync(cancellationToken); return Results.Ok(new { plugins = plugins.Select(plugin => new { plugin.Publisher, plugin.Name, plugin.Description, plugin.License, latestVersion = plugin.Latest.Version, updatedAt = plugin.Versions[0].PublishedAt }) }); }); tazel.MapGet("/{publisher}/{name}", async ( string publisher, string name, ITazelPluginCatalog catalog, CancellationToken cancellationToken) => { TazelPlugin? plugin = await FindAsync(catalog, publisher, name, cancellationToken); return plugin is null ? Results.NotFound() : Results.Ok(new { plugin.Publisher, plugin.Name, plugin.Description, plugin.Authors, plugin.License, plugin.Homepage, plugin.Repository, latestVersion = plugin.Latest.Version, versions = plugin.Versions.Select(version => new { version.Version, version.Sha256, version.ApiVersion, version.Size, version.PublishedAt, version.Yanked, path = $"/dslplugins/tazel/{plugin.Publisher}/{plugin.Name}/{version.Version}/" + $"{plugin.Name}-{version.Version}.jar" }) }); }); // The index Tazel reads to choose a version and check the JAR it downloads. tazel.MapGet("/{publisher}/{name}/index.json", async ( string publisher, string name, ITazelPluginCatalog catalog, CancellationToken cancellationToken) => { TazelPlugin? plugin = await FindAsync(catalog, publisher, name, cancellationToken); return plugin is null ? Results.NotFound() : Results.Ok(new { plugin.Publisher, plugin.Name, versions = plugin.Versions.Select(version => new { version.Version, version.Sha256, version.ApiVersion, version.Yanked }) }); }); } /// A plugin of the catalog, or when a route value is not an identifier. /// The catalog. /// The publisher from the route. /// The name from the route. /// Cancels the operation. /// The plugin, if there is one. private static ValueTask FindAsync( ITazelPluginCatalog catalog, string publisher, string name, CancellationToken cancellationToken) => TazelPluginDescriptor.IsIdentifier(publisher) && TazelPluginDescriptor.IsIdentifier(name) ? catalog.FindAsync(publisher, name, cancellationToken) : ValueTask.FromResult(null); }