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([