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
@@ -0,0 +1,300 @@
namespace FlatRender.ContentSvc.Models.Requests;
// ── Taxonomy ─────────────────────────────────────────────────────────────────
public record CreateCategoryRequest(
Guid? ParentId,
string Name,
string Slug,
string? Description,
string? ImageUrl,
string? Icon,
string? MetaTitle,
string? MetaDescription,
string? MetaKeywords,
bool BotFollow,
int Sort,
bool IsActive
);
public record UpdateCategoryRequest(
Guid? ParentId,
string Name,
string Slug,
string? Description,
string? ImageUrl,
string? Icon,
string? MetaTitle,
string? MetaDescription,
string? MetaKeywords,
bool BotFollow,
int Sort,
bool IsActive
);
public record CreateTagRequest(
string Name,
string? LatinName,
string Slug,
string? AppliesToMode,
bool IsActive
);
public record UpdateTagRequest(
string Name,
string? LatinName,
string Slug,
string? AppliesToMode,
bool IsActive
);
public record CreateFontRequest(
string Name,
string? OriginalName,
string? SystemName,
string? Family,
int? Weight,
string? Style,
string Direction,
string? FileUrl,
string? SampleImageUrl,
bool IsPremium,
bool IsActive,
bool InstalledOnNodes,
int Sort
);
public record UpdateFontRequest(
string Name,
string? OriginalName,
string? SystemName,
string? Family,
int? Weight,
string? Style,
string Direction,
string? FileUrl,
string? SampleImageUrl,
bool IsPremium,
bool IsActive,
bool InstalledOnNodes,
int Sort
);
public record CreateMusicTrackRequest(
string Name,
string? Caption,
string? Keywords,
string Url,
string? WaveformData,
decimal DurationSec,
int? Bpm,
string? Genre,
string? Mood,
bool IsPremium,
bool IsActive,
int Sort
);
public record UpdateMusicTrackRequest(
string Name,
string? Caption,
string? Keywords,
string Url,
string? WaveformData,
decimal DurationSec,
int? Bpm,
string? Genre,
string? Mood,
bool IsPremium,
bool IsActive,
int Sort
);
// ── Templates ────────────────────────────────────────────────────────────────
public record CreateContainerRequest(
string Slug,
string Name,
string? Description,
string? Keywords,
string? NewsText,
string? Image,
string? Demo,
string? FullDemo,
string? MiniDemo,
string? DemoScriptTag,
bool IsPublished,
bool IsPremium,
bool IsMockup,
string PrimaryMode,
int Sort,
List<Guid> CategoryIds,
List<Guid> TagIds
);
public record UpdateContainerRequest(
string Slug,
string Name,
string? Description,
string? Keywords,
string? NewsText,
string? Image,
string? Demo,
string? FullDemo,
string? MiniDemo,
string? DemoScriptTag,
bool IsPublished,
bool IsPremium,
bool IsMockup,
string PrimaryMode,
int Sort,
List<Guid> CategoryIds,
List<Guid> TagIds
);
public record ContainerListRequest(
int Page = 1,
int PageSize = 20,
string? Search = null,
Guid? CategoryId = null,
string? TagSlug = null,
bool? IsPublished = null,
bool? IsPremium = null,
string? Mode = null,
string? Sort = "sort_date_desc"
);
public record CreateProjectRequest(
Guid ContainerId,
Guid? ProjectServerId,
string Name,
string? Description,
string? Image,
string? FullDemo,
string? DemoScriptTag,
string? DownloadLink,
string? Folder,
int OriginalWidth,
int OriginalHeight,
string? Aspect,
decimal ProjectDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int FreeFps,
string ChooseMode,
string Resolution,
decimal VipFactor,
string RenderAepComp,
bool IsPublished,
int Sort
);
public record UpdateProjectRequest(
string Name,
string? Description,
string? Image,
string? FullDemo,
string? DemoScriptTag,
string? DownloadLink,
string? Folder,
int OriginalWidth,
int OriginalHeight,
string? Aspect,
decimal ProjectDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int FreeFps,
string ChooseMode,
string Resolution,
decimal VipFactor,
string RenderAepComp,
string? SharedLayerImage,
string? SharedColorsSvg,
string? SharedColorPresetsSvg,
bool IsPublished,
int Sort
);
// ── CMS ──────────────────────────────────────────────────────────────────────
public record CreateBlogRequest(
string Slug,
string Title,
string? ShortDescription,
string Content,
string? MetaTitle,
string? MetaDescription,
string? MetaKeywords,
bool IncludeInSiteMap,
string? Image,
string? Cover,
string? AuthorDisplayName,
bool IsPublished,
DateTime? PublishDate,
string Kind = "Blog"
);
public record UpdateBlogRequest(
string Slug,
string Title,
string? ShortDescription,
string Content,
string? MetaTitle,
string? MetaDescription,
string? MetaKeywords,
bool IncludeInSiteMap,
string? Image,
string? Cover,
string? AuthorDisplayName,
bool IsPublished,
DateTime? PublishDate
);
public record BlogListRequest(
int Page = 1,
int PageSize = 20,
string? Search = null,
bool? IsPublished = null,
string Kind = "Blog"
);
public record CreateCommentRequest(
Guid? BlogId,
Guid? ContainerId,
Guid? ParentCommentId,
string Content,
decimal? Rate
);
public record CreateSlideRequest(
string? Keyword,
string? Title,
string? Image,
string? Parameter,
string SlideType,
DateTime? ExpireDate,
int Sort,
bool IsActive
);
public record UpdateSlideRequest(
string? Keyword,
string? Title,
string? Image,
string? Parameter,
string SlideType,
DateTime? ExpireDate,
int Sort,
bool IsActive
);
public record UpsertWebsiteSettingRequest(
string Key,
string Value,
string? Description,
bool IsSecret
);
public record CreateFavoriteFolderRequest(string Name, string? Description);
public record UpdateFavoriteFolderRequest(string Name, string? Description);
public record AddFavoriteContainerRequest(Guid ContainerId, Guid? FolderId, string? Note);
@@ -0,0 +1,452 @@
using FlatRender.ContentSvc.Domain.Enums;
namespace FlatRender.ContentSvc.Models.Responses;
// ── Pagination ───────────────────────────────────────────────────────────────
public record PagedResponse<T>(IEnumerable<T> Items, PaginationMeta Meta);
public record PaginationMeta(int Page, int PageSize, long Total, int TotalPages);
public record ApiError(string Code, string Message, string? TraceId = null);
// ── Taxonomy ─────────────────────────────────────────────────────────────────
public record CategoryResponse(
Guid Id,
Guid? ParentId,
string Name,
string Slug,
string? Description,
string? ImageUrl,
string? Icon,
bool IsActive,
int Sort,
List<CategoryResponse> Children
);
public record TagResponse(
Guid Id,
string Name,
string? LatinName,
string Slug,
string? AppliesToMode,
bool IsActive
);
public record FontResponse(
Guid Id,
string Name,
string? OriginalName,
string? SystemName,
string? Family,
int? Weight,
string? Style,
string Direction,
string? FileUrl,
string? SampleImageUrl,
bool IsPremium,
bool IsActive,
bool InstalledOnNodes
);
public record MusicTrackResponse(
Guid Id,
string Name,
string? Caption,
string Url,
string? WaveformData,
decimal DurationSec,
int? Bpm,
string? Genre,
string? Mood,
bool IsPremium
);
// ── Templates ────────────────────────────────────────────────────────────────
public record ContainerSummaryResponse(
Guid Id,
string Slug,
string Name,
string? Description,
string? Image,
string? Demo,
string? MiniDemo,
bool IsPublished,
bool IsPremium,
bool IsMockup,
string PrimaryMode,
decimal? RateAvg,
int RateCount,
long ViewCount,
long UseCount,
int Sort,
DateTime SortDate,
List<string> CategorySlugs,
List<string> Tags
);
public record ContainerDetailResponse(
Guid Id,
string Slug,
string Name,
string? Description,
string? Keywords,
string? NewsText,
string? Image,
string? Demo,
string? FullDemo,
string? MiniDemo,
string? DemoScriptTag,
bool IsPublished,
bool IsPremium,
bool IsMockup,
string PrimaryMode,
decimal? RateAvg,
int RateCount,
long ViewCount,
long UseCount,
int Sort,
DateTime SortDate,
List<ProjectResponse> Projects,
List<CategoryResponse> Categories,
List<TagResponse> Tags
);
public record ProjectResponse(
Guid Id,
Guid ContainerId,
string Name,
string? Image,
string? FullDemo,
int OriginalWidth,
int OriginalHeight,
string? Aspect,
decimal ProjectDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int FreeFps,
string ChooseMode,
string Resolution,
bool IsPublished,
int Sort
);
public record ProjectDetailResponse(
Guid Id,
Guid ContainerId,
string Name,
string? Description,
string? Image,
string? FullDemo,
string? DemoScriptTag,
string? DownloadLink,
int OriginalWidth,
int OriginalHeight,
string? Aspect,
decimal ProjectDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int FreeFps,
string ChooseMode,
string Resolution,
decimal VipFactor,
string RenderAepComp,
string? SharedLayerImage,
bool IsPublished,
int Sort,
List<SceneResponse> Scenes,
List<SharedColorResponse> SharedColors,
List<SharedLayerResponse> SharedLayers
);
// ── Scenes ───────────────────────────────────────────────────────────────────
public record SceneResponse(
Guid Id,
Guid ProjectId,
string Key,
string Title,
string? LocalizedTitle,
string SceneType,
string? Image,
string? Demo,
string? SnapshotUrl,
bool GenerateKf,
decimal? DefaultDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
decimal OverlapAtEndSec,
bool CanHandleDuration,
bool ManualColorSelection,
int Sort,
bool IsActive
);
public record SceneDetailResponse(
Guid Id,
Guid ProjectId,
string Key,
string Title,
string? LocalizedTitle,
string SceneType,
string? Image,
string? Demo,
string? SnapshotUrl,
bool GenerateKf,
decimal? DefaultDurationSec,
decimal? MinDurationSec,
decimal? MaxDurationSec,
decimal OverlapAtEndSec,
bool CanHandleDuration,
bool ManualColorSelection,
int Sort,
bool IsActive,
List<RepeaterItemResponse> RepeaterItems,
List<ContentElementResponse> ContentElements,
List<ColorElementResponse> ColorElements,
List<ColorPresetResponse> ColorPresets,
List<CharacterResponse> Characters
);
public record RepeaterItemResponse(
Guid Id,
string Title,
string RepeatBoxKey,
string RepeatItemKey,
int MaxRepeatCount,
bool UserCanChangeSort,
string RepeatSortStrategy,
int Sort,
List<ContentElementResponse> ContentElements
);
public record ContentElementResponse(
Guid Id,
Guid? RepeaterItemId,
string Key,
string Title,
string? LocalizedTitle,
string? Hint,
string Type,
string? DefaultValue,
Guid? FontId,
string? FontFace,
string? FontFaceName,
int? FontSize,
int? DefaultFontSize,
string? DefaultFontFace,
bool IsFontChangeable,
bool IsFontSizeChangeable,
string Justify,
bool CanJustify,
int PositionInContainer,
bool IsTextBox,
int? MaxSize,
string? DirectionLayerKey,
int DirectionLayerValue,
bool VideoSupport,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int? Width,
int? Height,
string? Thumbnail,
string? MappedList,
string? CounterMode,
string AiInputType,
bool IsHidden,
bool IsFocused,
string? OpacityControllerKey,
int VirtualCount,
int Sort
);
public record ColorElementResponse(
Guid Id,
string ElementKey,
string Title,
string? Icon,
string AttrValue,
string DefaultColor,
int Sort
);
public record ColorPresetResponse(
Guid Id,
string? Name,
int Sort,
List<ColorPresetItemResponse> Items
);
public record ColorPresetItemResponse(
Guid Id,
string ElementKey,
string Value,
int Sort
);
public record SharedColorResponse(
Guid Id,
string ElementKey,
string Title,
string? Icon,
string AttrValue,
string DefaultColor,
int Sort
);
public record SharedLayerResponse(
Guid Id,
string Key,
string Title,
string? LocalizedTitle,
string? Hint,
string Type,
string? DefaultValue,
Guid? FontId,
string? FontFace,
int? FontSize,
bool IsFontChangeable,
bool IsFontSizeChangeable,
string Justify,
bool CanJustify,
int PositionInContainer,
bool IsTextBox,
int? MaxSize,
bool VideoSupport,
decimal? MinDurationSec,
decimal? MaxDurationSec,
int? Width,
int? Height,
string? MappedList,
string AiInputType,
bool IsHidden,
bool IsFocused,
int VirtualCount,
int Sort
);
// ── Characters ───────────────────────────────────────────────────────────────
public record CharacterResponse(
Guid Id,
string Key,
string Name,
string? Icon,
int Sort,
List<CharacterControllerResponse> Controllers
);
public record CharacterControllerResponse(
Guid Id,
string Name,
string Key,
string? DefaultValue,
int Sort,
List<ControllerOptionResponse> Options
);
public record ControllerOptionResponse(
Guid Id,
string Name,
string? Icon,
string Value,
int Sort
);
// ── CMS ──────────────────────────────────────────────────────────────────────
public record BlogSummaryResponse(
Guid Id,
string Slug,
string Title,
string? ShortDescription,
string? Image,
string? Cover,
string? AuthorDisplayName,
bool IsPublished,
DateTime? PublishDate,
long ViewCount,
DateTime CreatedAt
);
public record BlogDetailResponse(
Guid Id,
string Slug,
string Title,
string? ShortDescription,
string Content,
string? MetaTitle,
string? MetaDescription,
string? MetaKeywords,
bool IncludeInSiteMap,
string? Image,
string? Cover,
Guid? AuthorUserId,
string? AuthorDisplayName,
bool IsPublished,
DateTime? PublishDate,
long ViewCount,
DateTime CreatedAt,
DateTime UpdatedAt
);
public record CommentResponse(
Guid Id,
Guid UserId,
Guid? BlogId,
Guid? ContainerId,
Guid? ParentCommentId,
string Content,
decimal? Rate,
bool IsApproved,
bool IsPinned,
DateTime CreatedAt
);
public record SlideResponse(
Guid Id,
string? Keyword,
string? Title,
string? Image,
string? Parameter,
string SlideType,
DateTime? ExpireDate,
int Sort,
bool IsActive
);
public record HomePageEventResponse(
Guid Id,
string? Title,
string? Subtitle,
string? Description,
string? Badge,
string? BadgeClass,
string? ButtonText,
string? ButtonUrl,
string? ButtonClass,
string? Color,
string? BackgroundColor,
string? TextColor,
string? Image,
bool IsActive,
int Sort,
DateTime? StartsAt,
DateTime? EndsAt
);
public record WebsiteSettingResponse(
Guid Id,
string Key,
string Value,
string? Description,
bool IsSecret
);
public record FavoriteFolderResponse(
Guid Id,
string Name,
string? Description,
int ContainerCount,
DateTime CreatedAt
);