d3ro-voice/apps/api-server/Program.cs
Yun Chan 708e20f747
Some checks failed
CI Pipeline / Code Quality & Typecheck (push) Waiting to run
CI Pipeline / Test Suite (macos-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (ubuntu-latest) (push) Blocked by required conditions
CI Pipeline / Test Suite (windows-latest) (push) Blocked by required conditions
CI Pipeline / Build Validation (admin) (push) Blocked by required conditions
CI Pipeline / Build Validation (desktop) (push) Blocked by required conditions
Deploy Landing Page / deploy (push) Blocked by required conditions
Deploy Landing Page / build (push) Waiting to run
Release & Packaging Pipeline / Build & Publish Admin Docker Image (push) Failing after 8s
Release & Code Signing CA Pipeline / build-and-sign-windows (push) Failing after 1m51s
Build macOS / Build & Package (macOS) (push) Failing after 4s
Build macOS / Build & Package (macOS)-1 (push) Failing after 5s
Release & Code Signing CA Pipeline / build-and-sign-macos (push) Failing after 3s
Release & Packaging Pipeline / Package macOS Desktop App (push) Failing after 4s
Release & Packaging Pipeline / Package Windows Desktop App (push) Failing after 2m28s
Release & Packaging Pipeline / Publish Official GitHub Release (push) Has been skipped
feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
2026-08-20 11:12:05 +09:00

278 lines
9.2 KiB
C#

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<AppDbContext>(options =>
options.UseSqlite($"Data Source={dbPath}"));
// Services Registration
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddScoped<ILlmProxyService, LlmProxyService>();
builder.Services.AddScoped<ISttProxyService, SttProxyService>();
// 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<AppDbContext>();
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();