using System; using Microsoft.AspNetCore.Builder; namespace Abstractions; /// /// Derives the same versioned plugin id used both for UI asset routing /// (/pluginAssets/{pluginUiId}/..., see Server.Api.PluginsApi.DerivePluginUiId) and for /// this file's plugin-owned API route prefix - single source of truth so the two never drift /// apart. GameCode alone isn't always unique: PluginRegistry keys slots by /// (GameCode, MinVer, MaxVer), so two simultaneously-loaded plugins for the same game but /// different version windows need distinct ids to avoid colliding on the same route/asset path. /// public static class PluginIdentity { public static string DeriveId(IMedusaPlugin plugin) => (plugin.MinVer, plugin.MaxVer) switch { (null, null) => plugin.GameCode, (not null, null) => $"{plugin.GameCode}-{plugin.MinVer}", (null, not null) => $"{plugin.GameCode}-{plugin.MaxVer}", (not null, not null) => $"{plugin.GameCode}-{plugin.MinVer}-{plugin.MaxVer}", }; } /// /// REST endpoint registration for plugins. Call from /// only - the host never calls AddFastEndpoints()/UseFastEndpoints(), so a FastEndpoints /// Endpoint<T> class is never reached; these are plain minimal-API routes instead, and /// (per OnAppInitialize's own doc remarks) get re-registered on every hot-reload along with /// everything else OnAppInitialize does. /// public static class PluginEndpointExtensions { // "/api/plugins/{pluginId}" rather than bare "/api/{pluginId}" so plugin-owned routes live // under the same namespace the host already reserves for plugin concerns (/api/plugins/ui, // /api/plugins/events - see Server.Api.PluginsApi) instead of squatting directly on /api/. private static string BuildPath(IMedusaPlugin plugin, string path) { var relative = path.StartsWith('/') ? path : $"/{path}"; return $"/api/plugins/{PluginIdentity.DeriveId(plugin)}{relative}"; } public static RouteHandlerBuilder MapPluginGet( this WebApplication app, IMedusaPlugin plugin, string path, Delegate handler) => app.MapGet(BuildPath(plugin, path), handler); public static RouteHandlerBuilder MapPluginPost( this WebApplication app, IMedusaPlugin plugin, string path, Delegate handler) => app.MapPost(BuildPath(plugin, path), handler); public static RouteHandlerBuilder MapPluginPut( this WebApplication app, IMedusaPlugin plugin, string path, Delegate handler) => app.MapPut(BuildPath(plugin, path), handler); public static RouteHandlerBuilder MapPluginDelete( this WebApplication app, IMedusaPlugin plugin, string path, Delegate handler) => app.MapDelete(BuildPath(plugin, path), handler); }