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
+}
+