feat: complete release preparation, 10+ ad mediation, CI/CD, and docker deployment
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
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
This commit is contained in:
parent
5cd1de6859
commit
708e20f747
406 changed files with 42464 additions and 6199 deletions
278
apps/api-server/Program.cs
Normal file
278
apps/api-server/Program.cs
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
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();
|
||||
Loading…
Add table
Add a link
Reference in a new issue