feat(admin): category SEO fields, Templates admin, safe project PATCH
Build backend images / build content-svc (push) Failing after 21s
Build backend images / build file-svc (push) Failing after 3m49s
Build backend images / build gateway (push) Failing after 1m2s
Build backend images / build identity-svc (push) Failing after 1m1s
Build backend images / build notification-svc (push) Failing after 1m2s
Build backend images / build render-svc (push) Failing after 1m0s
Build backend images / build studio-svc (push) Failing after 58s
Build backend images / build content-svc (push) Failing after 21s
Build backend images / build file-svc (push) Failing after 3m49s
Build backend images / build gateway (push) Failing after 1m2s
Build backend images / build identity-svc (push) Failing after 1m1s
Build backend images / build notification-svc (push) Failing after 1m2s
Build backend images / build render-svc (push) Failing after 1m0s
Build backend images / build studio-svc (push) Failing after 58s
- categories/tags admin forms: add meta title/description/keywords, bot-follow,
sort, is_active (backend already supported these)
- new Templates admin (/admin/templates): container CRUD with description,
keywords, publishing, premium, primary mode, category/tag assignment, plus
editable per-variant aspect & resolution
- content-svc: PATCH /v1/projects/{id} partial update so aspect/resolution edits
never wipe render/colour data (SharedColorsSvg, RenderAepComp, Folder)
- admin resource proxy: add PATCH passthrough
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -199,6 +199,42 @@ public class TemplateService(ContentDbContext db)
|
||||
return await GetProjectDetailAsync(project.Id);
|
||||
}
|
||||
|
||||
/// <summary>Partial update — only applies supplied (non-null) fields, so editing an
|
||||
/// aspect/resolution never clears render/colour data the full update would require.</summary>
|
||||
public async Task<ProjectDetailResponse> PatchProjectAsync(Guid id, PatchProjectRequest req)
|
||||
{
|
||||
var project = await db.Projects.FindAsync(id)
|
||||
?? throw new KeyNotFoundException($"Project {id} not found");
|
||||
|
||||
if (req.Name != null) project.Name = req.Name;
|
||||
if (req.Description != null) project.Description = req.Description;
|
||||
if (req.Aspect != null) project.Aspect = req.Aspect;
|
||||
if (req.Resolution != null)
|
||||
{
|
||||
if (!Enum.TryParse<ResolutionKind>(req.Resolution, true, out var resolution))
|
||||
throw new ArgumentException($"Invalid Resolution: {req.Resolution}");
|
||||
project.Resolution = resolution;
|
||||
}
|
||||
if (req.ChooseMode != null)
|
||||
{
|
||||
if (!Enum.TryParse<ChooseMode>(req.ChooseMode, true, out var chooseMode))
|
||||
throw new ArgumentException($"Invalid ChooseMode: {req.ChooseMode}");
|
||||
project.ChooseMode = chooseMode;
|
||||
}
|
||||
if (req.OriginalWidth.HasValue) project.OriginalWidth = req.OriginalWidth.Value;
|
||||
if (req.OriginalHeight.HasValue) project.OriginalHeight = req.OriginalHeight.Value;
|
||||
if (req.ProjectDurationSec.HasValue) project.ProjectDurationSec = req.ProjectDurationSec.Value;
|
||||
if (req.MinDurationSec.HasValue) project.MinDurationSec = req.MinDurationSec;
|
||||
if (req.MaxDurationSec.HasValue) project.MaxDurationSec = req.MaxDurationSec;
|
||||
if (req.FreeFps.HasValue) project.FreeFps = req.FreeFps.Value;
|
||||
if (req.IsPublished.HasValue) project.IsPublished = req.IsPublished.Value;
|
||||
if (req.Sort.HasValue) project.Sort = req.Sort.Value;
|
||||
project.UpdatedAt = DateTime.UtcNow;
|
||||
|
||||
await db.SaveChangesAsync();
|
||||
return await GetProjectDetailAsync(project.Id);
|
||||
}
|
||||
|
||||
public async Task DeleteProjectAsync(Guid id)
|
||||
{
|
||||
var project = await db.Projects.FindAsync(id)
|
||||
|
||||
Reference in New Issue
Block a user