d3ro-voice/apps/api-server/Data/AppDbContext.cs
2026-08-29 18:33:45 +09:00

296 lines
7.6 KiB
C#

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
namespace D3ROVoice.Api.Data;
public class User
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(150)]
public string Email { get; set; } = string.Empty;
[Required]
public string PasswordHash { get; set; } = string.Empty;
[Required]
[MaxLength(50)]
public string Role { get; set; } = "User"; // "Admin" or "User"
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? LastLoginAt { get; set; }
}
public class ServiceModelEndpoint
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string ModelId { get; set; } = string.Empty;
[Required]
[MaxLength(150)]
public string ModelName { get; set; } = string.Empty;
[Required]
[MaxLength(50)]
public string Provider { get; set; } = "OpenAI"; // OpenAI, Anthropic, Custom
[Required]
[MaxLength(500)]
public string EndpointUrl { get; set; } = string.Empty;
[MaxLength(500)]
public string ApiKey { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 6)")]
public decimal CostPer1kPromptTokens { get; set; } = 0.00015m;
[Column(TypeName = "decimal(18, 6)")]
public decimal CostPer1kCompletionTokens { get; set; } = 0.00060m;
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class ApiUsageLog
{
[Key]
public int Id { get; set; }
public int UserId { get; set; }
[MaxLength(150)]
public string UserEmail { get; set; } = string.Empty;
public int ModelEndpointId { get; set; }
[MaxLength(100)]
public string ModelId { get; set; } = string.Empty;
public int PromptTokens { get; set; }
public int CompletionTokens { get; set; }
public int TotalTokens { get; set; }
[Column(TypeName = "decimal(18, 6)")]
public decimal CalculatedCost { get; set; }
public int RequestDurationMs { get; set; }
public int StatusCode { get; set; } = 200;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class ServerErrorLog
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(100)]
public string ErrorType { get; set; } = string.Empty;
[Required]
public string Message { get; set; } = string.Empty;
public string? StackTrace { get; set; }
[MaxLength(250)]
public string? Endpoint { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class SttProviderEndpoint
{
[Key]
public int Id { get; set; }
[Required]
[MaxLength(150)]
public string Name { get; set; } = string.Empty;
[Required]
[MaxLength(50)]
public string ProviderType { get; set; } = "groq"; // groq, openai, deepgram, google, assemblyai, azure, custom, local-sidecar
[Required]
[MaxLength(500)]
public string EndpointUrl { get; set; } = string.Empty;
[MaxLength(500)]
public string ApiKey { get; set; } = string.Empty;
[Required]
[MaxLength(100)]
public string ModelId { get; set; } = "whisper-large-v3-turbo";
[Required]
[MaxLength(50)]
public string Method { get; set; } = "multipart"; // multipart, binary-stream, json-base64, custom-rest
[MaxLength(20)]
public string Language { get; set; } = "ko";
[MaxLength(1000)]
public string? Prompt { get; set; }
public double Temperature { get; set; } = 0.0;
[Column(TypeName = "decimal(18, 6)")]
public decimal CostPerMinute { get; set; } = 0.000500m;
[Column(TypeName = "decimal(18, 6)")]
public decimal CostPerSecond { get; set; } = 0.000008m;
public bool IsDefault { get; set; } = false;
public bool IsActive { get; set; } = true;
public int FallbackPriority { get; set; } = 1;
[MaxLength(1000)]
public string? ExtraHeadersJson { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
}
public class SttUsageLog
{
[Key]
public int Id { get; set; }
public int UserId { get; set; }
[MaxLength(150)]
public string UserEmail { get; set; } = string.Empty;
public int EndpointId { get; set; }
[MaxLength(50)]
public string Provider { get; set; } = string.Empty;
[MaxLength(100)]
public string ModelId { get; set; } = string.Empty;
public double AudioDurationSeconds { get; set; }
[Column(TypeName = "decimal(18, 6)")]
public decimal CalculatedCost { get; set; }
public int LatencyMs { get; set; }
public int StatusCode { get; set; } = 200;
[MaxLength(1000)]
public string TranscriptPreview { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class AdminOperationRequest
{
[Key]
public long Id { get; set; }
[Required, MaxLength(150)]
public string ActorEmail { get; set; } = string.Empty;
[Required, MaxLength(36)]
public string IdempotencyKey { get; set; } = string.Empty;
[Required, MaxLength(100)]
public string Operation { get; set; } = string.Empty;
[Required, MaxLength(64)]
public string RequestHash { get; set; } = string.Empty;
[Required]
public string ResponseJson { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class AdminAuditEntry
{
[Key]
public long Id { get; set; }
[Required, MaxLength(150)]
public string ActorEmail { get; set; } = string.Empty;
[Required, MaxLength(100)]
public string Action { get; set; } = string.Empty;
[Required, MaxLength(80)]
public string TargetType { get; set; } = string.Empty;
[Required, MaxLength(200)]
public string TargetId { get; set; } = string.Empty;
public string? BeforeJson { get; set; }
public string? AfterJson { get; set; }
[Required, MaxLength(1000)]
public string Memo { get; set; } = string.Empty;
[Required, MaxLength(36)]
public string IdempotencyKey { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<User> Users => Set<User>();
public DbSet<ServiceModelEndpoint> ModelEndpoints => Set<ServiceModelEndpoint>();
public DbSet<ApiUsageLog> UsageLogs => Set<ApiUsageLog>();
public DbSet<ServerErrorLog> ErrorLogs => Set<ServerErrorLog>();
public DbSet<SttProviderEndpoint> SttProviderEndpoints => Set<SttProviderEndpoint>();
public DbSet<SttUsageLog> SttUsageLogs => Set<SttUsageLog>();
public DbSet<AdminOperationRequest> AdminOperationRequests => Set<AdminOperationRequest>();
public DbSet<AdminAuditEntry> AdminAuditEntries => Set<AdminAuditEntry>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasIndex(u => u.Email)
.IsUnique();
modelBuilder.Entity<ServiceModelEndpoint>()
.HasIndex(m => m.ModelId)
.IsUnique();
modelBuilder.Entity<SttProviderEndpoint>()
.HasIndex(s => s.Name);
modelBuilder.Entity<SttProviderEndpoint>()
.HasIndex(s => s.IsDefault);
modelBuilder.Entity<AdminOperationRequest>()
.HasIndex(operation => new { operation.ActorEmail, operation.IdempotencyKey })
.IsUnique();
modelBuilder.Entity<AdminAuditEntry>()
.HasIndex(entry => entry.CreatedAt);
}
}