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
278 lines
9.2 KiB
C#
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();
|