diff --git a/.vscode/launch.json b/.vscode/launch.json index 9ad0e4a1..da579d08 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -11,7 +11,7 @@ "cwd": "${workspaceFolder}/SnakeAid.Api", "stopAtEntry": false, "serverReadyAction": { - "pattern": "\\bNow listening on:\\s+(https?://\\S+)", + "pattern": "Now listening on:\\s+(https?://[^\\s]+)", "uriFormat": "%s", "action": "openExternally" }, @@ -33,7 +33,7 @@ "cwd": "${workspaceFolder}/SnakeAid.Api", "stopAtEntry": false, "serverReadyAction": { - "pattern": "\\bNow listening on:\\s+(https://.*:7026)", + "pattern": "Now listening on:\\s+(https://[^\\s]+:7026)", "uriFormat": "%s", "action": "openExternally" }, @@ -58,7 +58,7 @@ "cwd": "${workspaceFolder}/SnakeAid.Api", "stopAtEntry": false, "serverReadyAction": { - "pattern": "\\bNow listening on:\\s+(http://.*:5009)", + "pattern": "Now listening on:\\s+(http://[^\\s]+:5009)", "uriFormat": "%s", "action": "openExternally" }, @@ -83,7 +83,7 @@ "cwd": "${workspaceFolder}/SnakeAid.Api", "stopAtEntry": false, "serverReadyAction": { - "pattern": "\\bNow listening on:\\s+(https://.*:7026)", + "pattern": "Now listening on:\\s+(https://[^\\s]+:7026)", "uriFormat": "%s", "action": "openExternally" }, diff --git a/.vscode/settings.json b/.vscode/settings.json index fe46a187..2937b7b8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,41 @@ { - "dotnet.defaultSolution": "SnakeAid.Api/SnakeAid.Api.sln" + "dotnet.defaultSolution": "SnakeAid.Backend.sln", + "files.exclude": { + "**/bin": false, + "**/obj": false, + "**/Properties": false, + ".vscode": false, + "**/node_modules": false, + "**/packages": false, + "**/.vs": false, + "**/dist": false, + "**/coverage": false, + "**/artifacts": false, + "**/logs": false, + "**/*.dll": false, + "**/*.pdb": false, + "**/*.exe": false, + "**/*.nupkg": false, + "**/*.zip": false, + "**/*.tar.gz": false, + "**/*.so": false, + "**/*.dylib": false, + "**/*.pyc": false, + "**/*.db": false, + "**/*.sqlite": false + }, + "search.exclude": { + "**/bin": true, + "**/obj": true, + "**/node_modules": true, + "**/.vs": true, + "**/dist": true + }, + "files.watcherExclude": { + "**/bin/**": true, + "**/obj/**": true, + "**/.git/**": true, + "**/node_modules/**": true, + "**/.vs/**": true + } } \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 68dcef3a..6d8c3c25 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,6 +3,7 @@ true + diff --git a/SnakeAid.Api/DI/DependencyInjection.cs b/SnakeAid.Api/DI/DependencyInjection.cs index 093f3551..dd6045fa 100644 --- a/SnakeAid.Api/DI/DependencyInjection.cs +++ b/SnakeAid.Api/DI/DependencyInjection.cs @@ -13,12 +13,54 @@ using System.Security.Claims; using System.Text; +using Serilog; +using Doppler.Extensions.Configuration; // Ensure this is available + namespace SnakeAid.Api.DI; public static class DependencyInjection { #region service he thong + #region Doppler + public static WebApplicationBuilder AddConfigurationFromDopplerCloud(this WebApplicationBuilder builder) + { + // 1. Load Doppler Token (Process -> User) + var token = Environment.GetEnvironmentVariable("DOPPLER_TOKEN"); + + if (string.IsNullOrEmpty(token)) + { + token = Environment.GetEnvironmentVariable("DOPPLER_TOKEN", EnvironmentVariableTarget.User); + if (!string.IsNullOrEmpty(token)) + { + // Propagate User token and other User env vars to Process + Environment.SetEnvironmentVariable("DOPPLER_TOKEN", token, EnvironmentVariableTarget.Process); + foreach (System.Collections.DictionaryEntry userEnvVar in Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)) + { + if (userEnvVar.Key is string key && userEnvVar.Value is string value && key != "DOPPLER_TOKEN") + { + Environment.SetEnvironmentVariable(key, value, EnvironmentVariableTarget.Process); + } + } + } + } + + // 2. Register and Log results + if (!string.IsNullOrEmpty(token)) + { + builder.Configuration.AddDoppler(token); + Serilog.Log.Information("🚀 Configuration: Doppler Cloud (ENABLED)"); + } + else + { + Serilog.Log.Information("â„šī¸ Configuration: Local Settings (AppSettings.json & Env Vars)"); + } + + return builder; + } + #endregion + + #region Services public static IServiceCollection AddServices(this IServiceCollection services, IConfiguration configuration) { var cloudinarySection = configuration.GetSection("Cloudinary"); @@ -77,6 +119,8 @@ private static IAsyncPolicy GetCircuitBreakerPolicy() #endregion + #endregion + #region authen vs author public static IServiceCollection AddAuthenticateAuthor(this IServiceCollection services, diff --git a/SnakeAid.Api/Program.cs b/SnakeAid.Api/Program.cs index 914685d8..d76b4fec 100644 --- a/SnakeAid.Api/Program.cs +++ b/SnakeAid.Api/Program.cs @@ -17,6 +17,7 @@ using SQLitePCL; using Swashbuckle.AspNetCore.SwaggerUI; using System.Text.Json.Serialization; +using Doppler.Extensions.Configuration; namespace SnakeAid.Api { @@ -33,6 +34,8 @@ public static async Task Main(string[] args) { var builder = WebApplication.CreateBuilder(args); + builder.AddConfigurationFromDopplerCloud(); + Batteries_V2.Init(); var sqliteLogPath = Path.Combine(builder.Environment.ContentRootPath, "logs", "logs.db"); @@ -149,7 +152,7 @@ public static async Task Main(string[] args) }); builder.Services.AddControllers(); - + // Add Razor Pages for lightweight UI admin pages builder.Services.AddRazorPages(); diff --git a/SnakeAid.Api/Properties/launchSettings.json b/SnakeAid.Api/Properties/launchSettings.json index 88511877..97da9ec0 100644 --- a/SnakeAid.Api/Properties/launchSettings.json +++ b/SnakeAid.Api/Properties/launchSettings.json @@ -13,8 +13,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "http://localhost:5009", + "launchUrl": "", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -23,8 +22,7 @@ "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, - "launchUrl": "swagger", - "applicationUrl": "https://localhost:7026;http://localhost:5009", + "launchUrl": "", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } @@ -32,7 +30,7 @@ "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, - "launchUrl": "swagger", + "launchUrl": "", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } diff --git a/SnakeAid.Api/SnakeAid.Api.csproj b/SnakeAid.Api/SnakeAid.Api.csproj index c6c4f4f6..63f99af7 100644 --- a/SnakeAid.Api/SnakeAid.Api.csproj +++ b/SnakeAid.Api/SnakeAid.Api.csproj @@ -9,6 +9,7 @@ + diff --git a/SnakeAid.Core/Exceptions/ApiException.cs b/SnakeAid.Core/Exceptions/ApiException.cs index 9c353ff5..9c9f8bea 100644 --- a/SnakeAid.Core/Exceptions/ApiException.cs +++ b/SnakeAid.Core/Exceptions/ApiException.cs @@ -111,4 +111,12 @@ public TooManyRequestsException(string reason, DateTime? retryAfter, int? limit Period = period; Endpoint = endpoint; } +} + +public class ConfigurationException : ApiException +{ + public ConfigurationException(string reason) + : base(reason, HttpStatusCode.InternalServerError) + { + } } \ No newline at end of file diff --git a/SnakeAid.Core/Settings/AppSettings.cs b/SnakeAid.Core/Settings/AppSettings.cs index 99b2df50..acb77e17 100644 --- a/SnakeAid.Core/Settings/AppSettings.cs +++ b/SnakeAid.Core/Settings/AppSettings.cs @@ -43,4 +43,5 @@ public class SnakeAISettings public bool SaveImage { get; set; } = false; public float Confidence { get; set; } = 0.25f; public int TimeoutSeconds { get; set; } = 30; -} \ No newline at end of file +} +