using System.Text; using D3ROVoice.Api.Data; using D3ROVoice.Api.Services; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi; var builder = WebApplication.CreateBuilder(args); var serverStartTime = DateTime.UtcNow; // Add Services to Container builder.Services.AddControllers(); builder.Services.AddHttpClient(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "D3RO Voice Cloud API & BackOffice", Version = "v1", Description = "D3RO Voice Self-Hosted Cloud Backend for NAS & Docker" }); }); // Database Connection (SQLite with configurable NAS volume directory) var dataDir = builder.Configuration["DATA_DIR"] ?? (Directory.Exists("/app/data") ? "/app/data" : Path.Combine(AppContext.BaseDirectory, "data")); var dbPath = builder.Configuration["DB_PATH"] ?? builder.Configuration["DATABASE_PATH"] ?? Path.Combine(dataDir, "d3ro_api.db"); var dbDirectory = Path.GetDirectoryName(dbPath); if (!string.IsNullOrEmpty(dbDirectory) && !Directory.Exists(dbDirectory)) { Directory.CreateDirectory(dbDirectory); } builder.Services.AddDbContext(options => options.UseSqlite($"Data Source={dbPath}")); // Services Registration builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); // JWT Authentication Configuration var secretKey = builder.Configuration["Jwt:SecretKey"] ?? builder.Configuration["JWT_SECRET"] ?? "D3ROVoice_Super_Secure_Secret_Key_2026_Key!"; var keyBytes = Encoding.UTF8.GetBytes(secretKey); builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.RequireHttpsMetadata = false; options.SaveToken = true; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(keyBytes), ValidateIssuer = false, ValidateAudience = false, ClockSkew = TimeSpan.Zero }; }); builder.Services.AddCors(options => { options.AddPolicy("AllowAll", policy => { policy.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader(); }); }); var app = builder.Build(); // Ensure Database is Created & Initialized with default data using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); db.Database.EnsureCreated(); // Default Model Endpoints if empty if (!db.ModelEndpoints.Any()) { db.ModelEndpoints.AddRange( new ServiceModelEndpoint { ModelId = "d3ro-gpt4o-mini", ModelName = "D3RO Standard Model (GPT-4o Mini)", Provider = "OpenAI", EndpointUrl = "https://api.openai.com/v1/chat/completions", CostPer1kPromptTokens = 0.00015m, CostPer1kCompletionTokens = 0.00060m, IsActive = true }, new ServiceModelEndpoint { ModelId = "d3ro-claude-35-sonnet", ModelName = "D3RO Pro Model (Claude 3.5 Sonnet)", Provider = "Anthropic", EndpointUrl = "https://api.anthropic.com/v1/messages", CostPer1kPromptTokens = 0.00300m, CostPer1kCompletionTokens = 0.01500m, IsActive = true } ); db.SaveChanges(); } // Default STT Provider Endpoints if empty if (!db.SttProviderEndpoints.Any()) { db.SttProviderEndpoints.AddRange( new SttProviderEndpoint { Name = "Groq Whisper LPU Turbo (Ultra Fast)", ProviderType = "groq", EndpointUrl = "https://api.groq.com/openai/v1/audio/transcriptions", ApiKey = builder.Configuration["GROQ_API_KEY"] ?? "", ModelId = "whisper-large-v3-turbo", Method = "multipart", Language = "ko", CostPerMinute = 0.000500m, CostPerSecond = 0.000008m, IsDefault = true, IsActive = true, FallbackPriority = 1, CreatedAt = DateTime.UtcNow }, new SttProviderEndpoint { Name = "OpenAI Whisper Official", ProviderType = "openai", EndpointUrl = "https://api.openai.com/v1/audio/transcriptions", ApiKey = builder.Configuration["OPENAI_API_KEY"] ?? "", ModelId = "whisper-1", Method = "multipart", Language = "ko", CostPerMinute = 0.006000m, CostPerSecond = 0.000100m, IsDefault = false, IsActive = true, FallbackPriority = 2, CreatedAt = DateTime.UtcNow }, new SttProviderEndpoint { Name = "Deepgram Nova-3 Industry Standard", ProviderType = "deepgram", EndpointUrl = "https://api.deepgram.com/v1/listen", ApiKey = builder.Configuration["DEEPGRAM_API_KEY"] ?? "", ModelId = "nova-3", Method = "binary-stream", Language = "ko", CostPerMinute = 0.004300m, CostPerSecond = 0.000072m, IsDefault = false, IsActive = true, FallbackPriority = 3, CreatedAt = DateTime.UtcNow }, new SttProviderEndpoint { Name = "Google Gemini 2.0 Flash / Cloud STT", ProviderType = "google", EndpointUrl = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent", ApiKey = builder.Configuration["GEMINI_API_KEY"] ?? builder.Configuration["GOOGLE_API_KEY"] ?? "", ModelId = "gemini-2.0-flash", Method = "json-base64", Language = "ko", CostPerMinute = 0.001000m, CostPerSecond = 0.000017m, IsDefault = false, IsActive = true, FallbackPriority = 4, CreatedAt = DateTime.UtcNow }, new SttProviderEndpoint { Name = "Local Sidecar / Self-Hosted Whisper", ProviderType = "local-sidecar", EndpointUrl = "http://localhost:8971/stt/transcribe", ApiKey = "", ModelId = "whisper-large-v3-turbo", Method = "multipart", Language = "ko", CostPerMinute = 0.000000m, CostPerSecond = 0.000000m, IsDefault = false, IsActive = true, FallbackPriority = 5, CreatedAt = DateTime.UtcNow } ); db.SaveChanges(); } // Default Admin User seed & update password to Test1234! var adminUser = db.Users.FirstOrDefault(u => u.Email == "admin" || u.Email == "admin@d3ro.voice"); var passwordHash = AuthService.HashPassword("Test1234!"); if (adminUser == null) { db.Users.AddRange( new User { Email = "admin", PasswordHash = passwordHash, Role = "SuperAdmin", CreatedAt = DateTime.UtcNow, IsActive = true }, new User { Email = "admin@d3ro.voice", PasswordHash = passwordHash, Role = "SuperAdmin", CreatedAt = DateTime.UtcNow, IsActive = true } ); db.SaveChanges(); } else { adminUser.PasswordHash = passwordHash; adminUser.Role = "SuperAdmin"; adminUser.IsActive = true; db.SaveChanges(); } } app.UseSwagger(); app.UseSwaggerUI(); app.UseCors("AllowAll"); app.UseDefaultFiles(); app.UseStaticFiles(); app.UseAuthentication(); app.UseAuthorization(); // Health Check Endpoints for Docker & NAS Container Monitoring app.MapGet("/health", () => Results.Ok(new { status = "Healthy", service = "D3RO Voice Cloud API", version = "1.0.0", uptimeSeconds = (DateTime.UtcNow - serverStartTime).TotalSeconds, database = File.Exists(dbPath) ? "Connected" : "Initializing", timestamp = DateTime.UtcNow })); app.MapGet("/api/health", () => Results.Ok(new { status = "Healthy", service = "D3RO Voice Cloud API", version = "1.0.0", uptimeSeconds = (DateTime.UtcNow - serverStartTime).TotalSeconds, database = File.Exists(dbPath) ? "Connected" : "Initializing", timestamp = DateTime.UtcNow })); app.MapControllers(); // Fallback to Admin BackOffice UI index.html app.MapFallbackToFile("/admin/{*path}", "admin/index.html"); app.Run();