diff --git a/src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs b/src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs
index b0a142cf..1c9e7b4b 100644
--- a/src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs
+++ b/src/Managing.Api/HealthChecks/CandleDataDetailedHealthCheck.cs
@@ -140,3 +140,4 @@ namespace Managing.Api.HealthChecks
}
}
+
diff --git a/src/Managing.Api/Models/Requests/IndicatorRequestDto.cs b/src/Managing.Api/Models/Requests/IndicatorRequestDto.cs
index 76ae1f10..694c1e85 100644
--- a/src/Managing.Api/Models/Requests/IndicatorRequestDto.cs
+++ b/src/Managing.Api/Models/Requests/IndicatorRequestDto.cs
@@ -1,43 +1,23 @@
-#nullable enable
-
using System.ComponentModel.DataAnnotations;
namespace Managing.Api.Models.Requests;
-///
-/// Request model for submitting indicator requests to N8n webhook
-///
public class IndicatorRequestDto
{
- ///
- /// Name of the indicator (e.g., "MACD", "RSI", "Bollinger Bands")
- ///
[Required]
[StringLength(200, MinimumLength = 4, ErrorMessage = "Indicator name must be between 4 and 200 characters.")]
public string IndicatorName { get; set; } = string.Empty;
- ///
- /// Strategy or description of how the indicator is used (e.g., "MACD Cross", "RSI Divergence")
- ///
[Required]
[StringLength(1000, MinimumLength = 10, ErrorMessage = "Strategy description must be between 10 and 1000 characters.")]
public string StrategyDescription { get; set; } = string.Empty;
- ///
- /// Primary documentation URL for the indicator
- ///
[Url(ErrorMessage = "Documentation URL must be a valid URL.")]
public string? DocumentationUrl { get; set; }
- ///
- /// Image URL for the indicator (optional) - can be a chart, diagram, or visual representation
- ///
[Url(ErrorMessage = "Image URL must be a valid URL.")]
public string? ImageUrl { get; set; }
- ///
- /// Telegram account handle of the person requesting the indicator (e.g., "cryptooda")
- ///
[Required]
[RegularExpression(@"^(?=(?:[0-9_]*[a-z]){3})[a-z0-9_]{5,}$",
ErrorMessage = "Requester name must be a valid Telegram handle (lowercase letters, numbers, underscores only, minimum 5 characters with at least 3 letters).")]
diff --git a/src/Managing.Api/Program.cs b/src/Managing.Api/Program.cs
index ffab5f75..e1a34789 100644
--- a/src/Managing.Api/Program.cs
+++ b/src/Managing.Api/Program.cs
@@ -243,16 +243,33 @@ builder.Services
};
});
-builder.Services.AddCors(o => o.AddPolicy("CorsPolicy", builder =>
+// Configure CORS from configuration (appsettings.json)
+var allowedCorsOrigins = builder.Configuration
+ .GetSection("Cors:AllowedOrigins")
+ .Get() ?? Array.Empty();
+
+builder.Services.AddCors(options =>
{
- builder
- .SetIsOriginAllowed((host) => true)
- .AllowAnyOrigin()
- .WithOrigins("http://localhost:3000/")
- .AllowAnyMethod()
- .AllowAnyHeader()
- .AllowCredentials();
-}));
+ options.AddPolicy("CorsPolicy", policy =>
+ {
+ if (allowedCorsOrigins.Length > 0)
+ {
+ policy
+ .WithOrigins(allowedCorsOrigins)
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .AllowCredentials();
+ }
+ else
+ {
+ // Fallback for development if no origins configured
+ policy
+ .AllowAnyMethod()
+ .AllowAnyHeader()
+ .SetIsOriginAllowed(_ => true);
+ }
+ });
+});
builder.Services.AddSignalR().AddJsonProtocol();
builder.Services.AddScoped();
@@ -262,47 +279,51 @@ builder.Services.RegisterApiDependencies(builder.Configuration);
// Orleans is always configured, but grains can be controlled
builder.Host.ConfigureOrleans(builder.Configuration, builder.Environment.IsProduction());
builder.Services.AddHostedServices();
-builder.Services.AddEndpointsApiExplorer();
-builder.Services.AddOpenApiDocument(document =>
+var enableSwagger = builder.Configuration.GetValue("EnableSwagger", builder.Environment.IsDevelopment());
+if (enableSwagger)
{
- document.AddSecurity("JWT", Enumerable.Empty(), new OpenApiSecurityScheme
- {
- Type = OpenApiSecuritySchemeType.ApiKey,
- Name = "Authorization",
- In = OpenApiSecurityApiKeyLocation.Header,
- Description = "Type into the textbox: Bearer {your JWT token}."
- });
-
- document.OperationProcessors.Add(
- new AspNetCoreOperationSecurityScopeProcessor("JWT"));
-});
-builder.Services.AddSwaggerGen(options =>
-{
- options.SchemaFilter();
- options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
- {
- Description = "Please insert your JWT Token into field : Bearer {your_token}",
- Name = "Authorization",
- Type = SecuritySchemeType.Http,
- In = ParameterLocation.Header,
- Scheme = "Bearer",
- BearerFormat = "JWT"
- });
- options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ builder.Services.AddEndpointsApiExplorer();
+ builder.Services.AddOpenApiDocument(document =>
{
+ document.AddSecurity("JWT", Enumerable.Empty(), new OpenApiSecurityScheme
{
- new Microsoft.OpenApi.Models.OpenApiSecurityScheme
- {
- Reference = new OpenApiReference
- {
- Type = ReferenceType.SecurityScheme,
- Id = "Bearer"
- }
- },
- new string[] { }
- }
+ Type = OpenApiSecuritySchemeType.ApiKey,
+ Name = "Authorization",
+ In = OpenApiSecurityApiKeyLocation.Header,
+ Description = "Type into the textbox: Bearer {your JWT token}."
+ });
+
+ document.OperationProcessors.Add(
+ new AspNetCoreOperationSecurityScopeProcessor("JWT"));
});
-});
+ builder.Services.AddSwaggerGen(options =>
+ {
+ options.SchemaFilter();
+ options.AddSecurityDefinition("Bearer,", new Microsoft.OpenApi.Models.OpenApiSecurityScheme
+ {
+ Description = "Please insert your JWT Token into field : Bearer {your_token}",
+ Name = "Authorization",
+ Type = SecuritySchemeType.Http,
+ In = ParameterLocation.Header,
+ Scheme = "Bearer",
+ BearerFormat = "JWT"
+ });
+ options.AddSecurityRequirement(new OpenApiSecurityRequirement
+ {
+ {
+ new Microsoft.OpenApi.Models.OpenApiSecurityScheme
+ {
+ Reference = new OpenApiReference
+ {
+ Type = ReferenceType.SecurityScheme,
+ Id = "Bearer"
+ }
+ },
+ new string[] { }
+ }
+ });
+ });
+}
builder.WebHost.SetupDiscordBot();
@@ -310,12 +331,15 @@ builder.WebHost.SetupDiscordBot();
var app = builder.Build();
app.UseSerilogRequestLogging();
-app.UseOpenApi();
-app.UseSwaggerUI(c =>
+if (enableSwagger)
{
- c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing API v1");
- c.RoutePrefix = string.Empty;
-});
+ app.UseOpenApi();
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "Managing API v1");
+ c.RoutePrefix = string.Empty;
+ });
+}
app.UseCors("CorsPolicy");
diff --git a/src/Managing.Api/appsettings.Production.json b/src/Managing.Api/appsettings.Production.json
index d8c948a2..9a86722c 100644
--- a/src/Managing.Api/appsettings.Production.json
+++ b/src/Managing.Api/appsettings.Production.json
@@ -35,5 +35,11 @@
"SentryEnabled": false,
"LoopDetectionEnabled": true,
"LogErrorsOnly": true
+ },
+ "Cors": {
+ "AllowedOrigins": [
+ "https://app.kaigen.ai",
+ "https://api.kaigen.ai"
+ ]
}
}
\ No newline at end of file
diff --git a/src/Managing.Api/appsettings.json b/src/Managing.Api/appsettings.json
index 730fd4f1..cf1bef0c 100644
--- a/src/Managing.Api/appsettings.json
+++ b/src/Managing.Api/appsettings.json
@@ -101,5 +101,6 @@
"LogSlowQueriesOnly": false,
"LogErrorsOnly": false,
"DataRetentionMinutes": 300
- }
+ },
+ "EnableSwagger": false
}
\ No newline at end of file
diff --git a/src/Managing.WebApp/src/hooks/useClaimUiFees.ts b/src/Managing.WebApp/src/hooks/useClaimUiFees.ts
index ce76c782..5c4232a9 100644
--- a/src/Managing.WebApp/src/hooks/useClaimUiFees.ts
+++ b/src/Managing.WebApp/src/hooks/useClaimUiFees.ts
@@ -5,7 +5,7 @@ import {parseAbi} from 'viem'
import {GmxSdk} from '@gmx-io/sdk'
// ExchangeRouter contract address on Arbitrum
-const EXCHANGE_ROUTER_ADDRESS = '0x5aC4e27341e4cCcb3e5FD62f9E62db2Adf43dd57'
+const EXCHANGE_ROUTER_ADDRESS = '0x87d66368cD08a7Ca42252f5ab44B2fb6d1Fb8d15'
// ABI for the claimUiFees function
const CLAIM_UI_FEES_ABI = parseAbi([