feat: V2 microservices stack — backend services, gateway, JWT auth

Add full V2 architecture: identity, content, studio (.NET 10) and file,
render, notification, gateway (Go) services with vendored deps, plus DB
migrations, event/API contracts, and an init-db script.

Wire the Next.js frontend to the gateway: server-side JWT auth routes
(login/register/refresh/logout/me), gateway fetch helper, and session/
cookie/jwt helpers under src/lib.

Containerize the stack via docker-compose.v2.yml and per-service
Dockerfiles. Base images resolve through a Nexus mirror (Docker Hub) and
MCR directly; npm/NuGet pull from Nexus groups. Self-host fonts via
next/font/local to avoid Google Fonts (geo-blocked).

Add CI workflow and ignore .env.v2, *.stackdump, and .NET bin/obj.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-05-29 23:29:31 +03:30
parent 53ea78a00d
commit 90ac0b81d1
7636 changed files with 3707504 additions and 240 deletions
+247
View File
@@ -0,0 +1,247 @@
package models
import (
"time"
"github.com/google/uuid"
)
// ── Enums ─────────────────────────────────────────────────────────────────────
type FileKind string
const (
FileKindVideo FileKind = "Video"
FileKindImage FileKind = "Image"
FileKindAudio FileKind = "Audio"
FileKindVoiceover FileKind = "Voiceover"
FileKindDocument FileKind = "Document"
FileKindOther FileKind = "Other"
)
type FolderKind string
const (
FolderKindSystem FolderKind = "System"
FolderKindUser FolderKind = "User"
FolderKindShared FolderKind = "Shared"
FolderKindTenant FolderKind = "Tenant"
)
type UploadStatus string
const (
UploadStatusPending UploadStatus = "Pending"
UploadStatusUploading UploadStatus = "Uploading"
UploadStatusProcessing UploadStatus = "Processing"
UploadStatusReady UploadStatus = "Ready"
UploadStatusFailed UploadStatus = "Failed"
UploadStatusQuarantined UploadStatus = "Quarantined"
)
type CleanupEntityType string
const (
CleanupEntityExport CleanupEntityType = "Export"
CleanupEntityTempRenderFolder CleanupEntityType = "TempRenderFolder"
CleanupEntityOrphanedFile CleanupEntityType = "OrphanedFile"
CleanupEntityUnusedUpload CleanupEntityType = "UnusedUpload"
CleanupEntitySnapshotExpired CleanupEntityType = "SnapshotExpired"
)
type CleanupStatus string
const (
CleanupStatusScheduled CleanupStatus = "Scheduled"
CleanupStatusNotified CleanupStatus = "Notified"
CleanupStatusProcessing CleanupStatus = "Processing"
CleanupStatusDone CleanupStatus = "Done"
CleanupStatusSkipped CleanupStatus = "Skipped"
CleanupStatusFailed CleanupStatus = "Failed"
)
// ── Domain ────────────────────────────────────────────────────────────────────
type UserFolder struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenant_id"`
UserID uuid.UUID `json:"user_id"`
Name string `json:"name"`
FolderType FolderKind `json:"folder_type"`
ParentFolderID *uuid.UUID `json:"parent_folder_id,omitempty"`
FileCount int `json:"file_count"`
TotalSizeBytes int64 `json:"total_size_bytes"`
Sort int `json:"sort"`
IsShared bool `json:"is_shared"`
ShareToken *string `json:"share_token,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
type UserFile struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenant_id"`
UserID uuid.UUID `json:"user_id"`
UserFolderID *uuid.UUID `json:"user_folder_id,omitempty"`
Name string `json:"name"`
OriginalFilename *string `json:"original_filename,omitempty"`
FileExtension *string `json:"file_extension,omitempty"`
MimeType *string `json:"mime_type,omitempty"`
FileType FileKind `json:"file_type"`
MinioBucket string `json:"minio_bucket"`
MinioKey string `json:"minio_key"`
CdnURL *string `json:"cdn_url,omitempty"`
FileAddress string `json:"file_address"`
SizeBytes int64 `json:"size_bytes"`
Md5Hash *string `json:"md5_hash,omitempty"`
Sha256Hash *string `json:"sha256_hash,omitempty"`
DurationSec *float64 `json:"duration_sec,omitempty"`
Width *int `json:"width,omitempty"`
Height *int `json:"height,omitempty"`
Fps *float64 `json:"fps,omitempty"`
BitrateKbps *int `json:"bitrate_kbps,omitempty"`
Codec *string `json:"codec,omitempty"`
HasAudio *bool `json:"has_audio,omitempty"`
HasVideo *bool `json:"has_video,omitempty"`
ThumbnailURL *string `json:"thumbnail_url,omitempty"`
WaveformData *string `json:"waveform_data,omitempty"`
UploadStatus UploadStatus `json:"upload_status"`
UploadProgress int `json:"upload_progress"`
Source *string `json:"source,omitempty"`
ExportID *uuid.UUID `json:"export_id,omitempty"`
ParentFileID *uuid.UUID `json:"parent_file_id,omitempty"`
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
UseCount int `json:"use_count"`
IsPublic bool `json:"is_public"`
ShareToken *string `json:"share_token,omitempty"`
Metadata string `json:"metadata"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt *time.Time `json:"deleted_at,omitempty"`
}
type StorageQuota struct {
UserID uuid.UUID `json:"user_id"`
TenantID uuid.UUID `json:"tenant_id"`
PlanQuotaBytes int64 `json:"plan_quota_bytes"`
BonusQuotaBytes int64 `json:"bonus_quota_bytes"`
UsedBytes int64 `json:"used_bytes"`
VideoCount int `json:"video_count"`
ImageCount int `json:"image_count"`
AudioCount int `json:"audio_count"`
VideoBytes int64 `json:"video_bytes"`
ImageBytes int64 `json:"image_bytes"`
AudioBytes int64 `json:"audio_bytes"`
Last90PctNotifiedAt *time.Time `json:"last_90pct_notified_at,omitempty"`
Last100PctNotifiedAt *time.Time `json:"last_100pct_notified_at,omitempty"`
UpdatedAt time.Time `json:"updated_at"`
}
type UploadSession struct {
ID uuid.UUID `json:"id"`
TenantID uuid.UUID `json:"tenant_id"`
UserID uuid.UUID `json:"user_id"`
MinioBucket string `json:"minio_bucket"`
MinioKey string `json:"minio_key"`
MinioUploadID string `json:"minio_upload_id"`
Filename string `json:"filename"`
MimeType *string `json:"mime_type,omitempty"`
TotalSizeBytes int64 `json:"total_size_bytes"`
ChunksReceived int `json:"chunks_received"`
BytesReceived int64 `json:"bytes_received"`
ChunkSizeBytes int `json:"chunk_size_bytes"`
TargetFolderID *uuid.UUID `json:"target_folder_id,omitempty"`
TargetFileID *uuid.UUID `json:"target_file_id,omitempty"`
Status UploadStatus `json:"status"`
ErrorMessage *string `json:"error_message,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type MinioBucket struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Region string `json:"region"`
Endpoint string `json:"endpoint"`
Purpose string `json:"purpose"`
IsPublic bool `json:"is_public"`
CdnBaseURL *string `json:"cdn_base_url,omitempty"`
IsActive bool `json:"is_active"`
CreatedAt time.Time `json:"created_at"`
}
// ── Request / Response ────────────────────────────────────────────────────────
type CreateFolderRequest struct {
Name string `json:"name" binding:"required"`
ParentFolderID *uuid.UUID `json:"parent_folder_id"`
}
type MoveFolderRequest struct {
ParentFolderID *uuid.UUID `json:"parent_folder_id"`
}
type RenameFolderRequest struct {
Name string `json:"name" binding:"required"`
}
type InitiateUploadRequest struct {
Filename string `json:"filename" binding:"required"`
MimeType *string `json:"mime_type"`
TotalSizeBytes int64 `json:"total_size_bytes" binding:"required,min=1"`
ChunkSizeBytes int `json:"chunk_size_bytes"`
TargetFolderID *uuid.UUID `json:"target_folder_id"`
}
type PresignedUploadRequest struct {
Filename string `json:"filename" binding:"required"`
MimeType *string `json:"mime_type"`
SizeBytes int64 `json:"size_bytes" binding:"required,min=1"`
TargetFolderID *uuid.UUID `json:"target_folder_id"`
}
type PresignedUploadResponse struct {
UploadURL string `json:"upload_url"`
FileID uuid.UUID `json:"file_id"`
ExpiresAt time.Time `json:"expires_at"`
}
type FileListRequest struct {
Page int `form:"page,default=1"`
PageSize int `form:"page_size,default=20"`
FolderID *uuid.UUID `form:"folder_id"`
FileType *FileKind `form:"file_type"`
Search *string `form:"search"`
}
type PagedResponse[T any] struct {
Items []T `json:"items"`
Meta PaginationMeta `json:"meta"`
}
type PaginationMeta struct {
Page int `json:"page"`
PageSize int `json:"page_size"`
Total int64 `json:"total"`
TotalPages int `json:"total_pages"`
}
type APIError struct {
Code string `json:"code"`
Message string `json:"message"`
}
type ErrorResponse struct {
Error APIError `json:"error"`
}
// ── JWT Claims ────────────────────────────────────────────────────────────────
type Claims struct {
Sub string `json:"sub"`
TenantID string `json:"tenant_id"`
IsAdmin bool `json:"is_admin"`
}