diff --git a/.dockerignore b/.dockerignore index cd967fc3..b3053aa5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -21,5 +21,6 @@ **/obj **/secrets.dev.yaml **/values.dev.yaml +**/storage LICENSE README.md \ No newline at end of file diff --git a/Moonlight.ApiServer/Dockerfile b/Moonlight.ApiServer/Dockerfile new file mode 100644 index 00000000..016478a6 --- /dev/null +++ b/Moonlight.ApiServer/Dockerfile @@ -0,0 +1,27 @@ +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +USER $APP_UID +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src +COPY ["Moonlight.ApiServer/Moonlight.ApiServer.csproj", "Moonlight.ApiServer/"] +COPY ["Moonlight.Client/Moonlight.Client.csproj", "Moonlight.Client/"] +COPY ["Moonlight.Shared/Moonlight.Shared.csproj", "Moonlight.Shared/"] +RUN dotnet restore "Moonlight.ApiServer/Moonlight.ApiServer.csproj" +COPY . . +WORKDIR "/src/Moonlight.ApiServer" +RUN dotnet build "Moonlight.ApiServer.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "Moonlight.ApiServer.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Create storage directory a volume can bind to +RUN mkdir -p /app/storage + +ENTRYPOINT ["dotnet", "Moonlight.ApiServer.dll"] diff --git a/Moonlight.ApiServer/Helpers/ApplicationStateHelper.cs b/Moonlight.ApiServer/Helpers/ApplicationStateHelper.cs index d0ebd217..1bef88ab 100644 --- a/Moonlight.ApiServer/Helpers/ApplicationStateHelper.cs +++ b/Moonlight.ApiServer/Helpers/ApplicationStateHelper.cs @@ -5,7 +5,7 @@ namespace Moonlight.ApiServer.Helpers; public class ApplicationStateHelper { - public static ConfigService? Configuration { get; private set; } + public static AppConfiguration Configuration { get; private set; } - public static void SetConfiguration(ConfigService? configuration) => Configuration = configuration; + public static void SetConfiguration(AppConfiguration configuration) => Configuration = configuration; } \ No newline at end of file diff --git a/Moonlight.ApiServer/Helpers/DatabaseContext.cs b/Moonlight.ApiServer/Helpers/DatabaseContext.cs index 20185189..98c30d10 100644 --- a/Moonlight.ApiServer/Helpers/DatabaseContext.cs +++ b/Moonlight.ApiServer/Helpers/DatabaseContext.cs @@ -8,12 +8,12 @@ namespace Moonlight.ApiServer.Helpers; public abstract class DatabaseContext : DbContext { - private ConfigService? ConfigService; + private AppConfiguration? Configuration; public abstract string Prefix { get; } public DatabaseContext() { - ConfigService = ApplicationStateHelper.Configuration; + Configuration = ApplicationStateHelper.Configuration; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -23,14 +23,14 @@ public abstract class DatabaseContext : DbContext // If no config service has been configured, we are probably // in a EF Core migration, so we need to construct the config manually - if (ConfigService == null) + if (Configuration == null) { - ConfigService = new ConfigService( - PathBuilder.File("storage", "config.json") - ); + Configuration = new ConfigService( + PathBuilder.File("storage", "app.json") + ).Get(); } - var config = ConfigService.Get().Database; + var config = Configuration.Database; var connectionString = $"host={config.Host};" + $"port={config.Port};" + diff --git a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs index 302a795c..851d4a7d 100644 --- a/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Auth/AuthController.cs @@ -24,9 +24,9 @@ public class AuthController : Controller { private readonly OAuth2Service OAuth2Service; private readonly TokenHelper TokenHelper; - private readonly ConfigService ConfigService; private readonly DatabaseRepository UserRepository; private readonly ILogger Logger; + private readonly AppConfiguration Configuration; private readonly IOAuth2Provider[] OAuth2Providers; private readonly IAuthInterceptor[] AuthInterceptors; @@ -34,18 +34,18 @@ public class AuthController : Controller OAuth2Service oAuth2Service, TokenHelper tokenHelper, DatabaseRepository userRepository, - ConfigService configService, ILogger logger, IOAuth2Provider[] oAuth2Providers, - IAuthInterceptor[] authInterceptors) + IAuthInterceptor[] authInterceptors, + AppConfiguration configuration) { OAuth2Service = oAuth2Service; TokenHelper = tokenHelper; UserRepository = userRepository; - ConfigService = configService; Logger = logger; OAuth2Providers = oAuth2Providers; AuthInterceptors = authInterceptors; + Configuration = configuration; } [HttpGet] @@ -94,7 +94,7 @@ public class AuthController : Controller // Generate local token-pair for the authentication // between client and the api server - var authConfig = ConfigService.Get().Authentication; + var authConfig = Configuration.Authentication; var tokenPair = TokenHelper.GeneratePair( authConfig.AccessSecret, @@ -117,7 +117,7 @@ public class AuthController : Controller [HttpPost("refresh")] public async Task Refresh([FromBody] RefreshRequest request) { - var authConfig = ConfigService.Get().Authentication; + var authConfig = Configuration.Authentication; var tokenPair = TokenHelper.RefreshPair( request.RefreshToken, diff --git a/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs b/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs index c7aededc..ff72fd66 100644 --- a/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs +++ b/Moonlight.ApiServer/Http/Controllers/Swagger/SwaggerController.cs @@ -10,17 +10,17 @@ namespace Moonlight.ApiServer.Http.Controllers.Swagger; [Route("api/swagger")] public class SwaggerController : Controller { - private readonly ConfigService ConfigService; + private readonly AppConfiguration Configuration; - public SwaggerController(ConfigService configService) + public SwaggerController(AppConfiguration configuration) { - ConfigService = configService; + Configuration = configuration; } [HttpGet] public async Task Get() { - if (!ConfigService.Get().Development.EnableApiDocs) + if (!Configuration.Development.EnableApiDocs) return BadRequest("Api docs are disabled"); var options = new ApiDocsOptions(); diff --git a/Moonlight.ApiServer/Implementations/OAuth2/LocalOAuth2Provider.cs b/Moonlight.ApiServer/Implementations/OAuth2/LocalOAuth2Provider.cs index caf64368..505301fc 100644 --- a/Moonlight.ApiServer/Implementations/OAuth2/LocalOAuth2Provider.cs +++ b/Moonlight.ApiServer/Implementations/OAuth2/LocalOAuth2Provider.cs @@ -16,14 +16,13 @@ public class LocalOAuth2Provider : IOAuth2Provider try { - var configService = provider.GetRequiredService>(); - var config = configService.Get(); + var configuration = provider.GetRequiredService(); using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Add("Authorization", accessToken); - var response = await httpClient.GetAsync($"{config.PublicUrl}/oauth2/info"); + var response = await httpClient.GetAsync($"{configuration.PublicUrl}/oauth2/info"); await response.HandlePossibleApiError(); var info = await response.ParseAsJson(); diff --git a/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs b/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs index 001a1b91..390cde0b 100644 --- a/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs +++ b/Moonlight.ApiServer/Implementations/Startup/ApiDocsStartup.cs @@ -7,18 +7,18 @@ namespace Moonlight.ApiServer.Implementations.Startup; public class ApiDocsStartup : IAppStartup, IEndpointStartup { - private readonly ConfigService ConfigService; private readonly ILogger Logger; + private readonly AppConfiguration AppConfiguration; - public ApiDocsStartup(ConfigService configService, ILogger logger) + public ApiDocsStartup(ILogger logger, AppConfiguration appConfiguration) { - ConfigService = configService; Logger = logger; + AppConfiguration = appConfiguration; } public Task BuildApp(IHostApplicationBuilder builder) { - if(!ConfigService.Get().Development.EnableApiDocs) + if(!AppConfiguration.Development.EnableApiDocs) return Task.CompletedTask; builder.Services.AddEndpointsApiExplorer(); @@ -36,7 +36,7 @@ public class ApiDocsStartup : IAppStartup, IEndpointStartup public Task ConfigureEndpoints(IEndpointRouteBuilder routeBuilder) { - if(!ConfigService.Get().Development.EnableApiDocs) + if(!AppConfiguration.Development.EnableApiDocs) return Task.CompletedTask; routeBuilder.MapSwagger("/api/swagger/{documentName}"); diff --git a/Moonlight.ApiServer/Moonlight.ApiServer.csproj b/Moonlight.ApiServer/Moonlight.ApiServer.csproj index 39105cf8..c657f43b 100644 --- a/Moonlight.ApiServer/Moonlight.ApiServer.csproj +++ b/Moonlight.ApiServer/Moonlight.ApiServer.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + Linux @@ -12,9 +13,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + @@ -27,7 +28,12 @@ - + + + + + .dockerignore + diff --git a/Moonlight.ApiServer/Program.cs b/Moonlight.ApiServer/Program.cs index 330d0249..9834d12b 100644 --- a/Moonlight.ApiServer/Program.cs +++ b/Moonlight.ApiServer/Program.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Reflection; using MoonCore.Extended.Helpers; using MoonCore.Extensions; @@ -32,15 +33,6 @@ Console.WriteLine(); // Storage i guess Directory.CreateDirectory(PathBuilder.Dir("storage")); -// Configuration -var configService = new ConfigService( - PathBuilder.File("storage", "config.json") -); - -var config = configService.Get(); - -ApplicationStateHelper.SetConfiguration(configService); - // TODO: Load plugin/module assemblies // Configure startup logger @@ -60,7 +52,14 @@ var startupLogger = startupLoggerFactory.CreateLogger("Startup"); // Configure startup interfaces var startupServiceCollection = new ServiceCollection(); -startupServiceCollection.AddSingleton(configService); + +startupServiceCollection.AddConfiguration(options => +{ + options.UsePath(PathBuilder.Dir("storage")); + options.UseEnvironmentPrefix("MOONLIGHT"); + + options.AddConfiguration("app"); +}); startupServiceCollection.AddLogging(loggingBuilder => { loggingBuilder.AddProviders(providers); }); @@ -73,12 +72,15 @@ startupServiceCollection.AddPlugins(configuration => // Configure assemblies to scan configuration.AddAssembly(Assembly.GetEntryAssembly()!); -}, startupLogger); +}); var startupServiceProvider = startupServiceCollection.BuildServiceProvider(); var appStartupInterfaces = startupServiceProvider.GetRequiredService(); +var config = startupServiceProvider.GetRequiredService(); +ApplicationStateHelper.SetConfiguration(config); + // Start the actual app var builder = WebApplication.CreateBuilder(args); @@ -108,7 +110,7 @@ foreach (var startupInterface in appStartupInterfaces) } builder.Services.AddControllers(); -builder.Services.AddSingleton(configService); +builder.Services.AddSingleton(config); builder.Services.AutoAddServices(); builder.Services.AddSingleton(); builder.Services.AddHttpClient(); @@ -123,7 +125,7 @@ builder.Services.AddPlugins(configuration => configuration.AddInterface(); configuration.AddAssembly(Assembly.GetEntryAssembly()!); -}, startupLogger); +}); var app = builder.Build(); diff --git a/Moonlight.ApiServer/Services/AuthService.cs b/Moonlight.ApiServer/Services/AuthService.cs index 92538e59..793d92c7 100644 --- a/Moonlight.ApiServer/Services/AuthService.cs +++ b/Moonlight.ApiServer/Services/AuthService.cs @@ -12,14 +12,12 @@ namespace Moonlight.ApiServer.Services; public class AuthService { private readonly DatabaseRepository UserRepository; - private readonly ConfigService ConfigService; + private readonly AppConfiguration Configuration; - public AuthService( - DatabaseRepository userRepository, - ConfigService configService) + public AuthService(DatabaseRepository userRepository, AppConfiguration configuration) { UserRepository = userRepository; - ConfigService = configService; + Configuration = configuration; } public Task Register(string username, string email, string password) diff --git a/Moonlight.ApiServer/Startup.cs b/Moonlight.ApiServer/Startup.cs index de29b816..b97d4568 100644 --- a/Moonlight.ApiServer/Startup.cs +++ b/Moonlight.ApiServer/Startup.cs @@ -18,6 +18,9 @@ public static class Startup public static async Task ConfigureLogging(IHostApplicationBuilder builder) { + // Create logging path + Directory.CreateDirectory(PathBuilder.Dir("storage", "logs")); + // Configure application logging builder.Logging.ClearProviders(); diff --git a/Moonlight.Client/Moonlight.Client.csproj b/Moonlight.Client/Moonlight.Client.csproj index df387267..8ef7a1f1 100644 --- a/Moonlight.Client/Moonlight.Client.csproj +++ b/Moonlight.Client/Moonlight.Client.csproj @@ -11,9 +11,9 @@ - + - + diff --git a/Moonlight.Client/Program.cs b/Moonlight.Client/Program.cs index cd4229e8..1b175154 100644 --- a/Moonlight.Client/Program.cs +++ b/Moonlight.Client/Program.cs @@ -114,7 +114,7 @@ builder.Services.AddPlugins(configuration => configuration.AddInterface(); configuration.AddInterface(); -}, logger); +}); var app = builder.Build(); diff --git a/compose.yml b/compose.yml new file mode 100644 index 00000000..691f572e --- /dev/null +++ b/compose.yml @@ -0,0 +1,41 @@ +services: + api-server: + image: moonlightpanel/panel:custom + build: + context: . + dockerfile: ./Moonlight.ApiServer/Dockerfile + ports: + - "9069:8080" + depends_on: + db: + condition: service_healthy + environment: + - MOONLIGHT_APP_DATABASE_HOST=db + - MOONLIGHT_APP_DATABASE_PORT=3306 + - MOONLIGHT_APP_DATABASE_USERNAME=moonlight + - MOONLIGHT_APP_DATABASE_PASSWORD=s3cret + - MOONLIGHT_APP_DATABASE_DATABASE=moonlight + - MOONLIGHT_APP_PUBLICURL=http://localhost:9069 + volumes: + - api_data:/app/storage + links: + - db + pull_policy: build + + db: + image: mysql:latest + environment: + - MYSQL_ROOT_PASSWORD=s3cret + - MYSQL_PASSWORD=s3cret + - MYSQL_DATABASE=moonlight + - MYSQL_USER=moonlight + volumes: + - db_data:/var/lib/mysql + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ] + timeout: 1s + retries: 10 + +volumes: + api_data: + db_data: \ No newline at end of file