Files
soroush.asadi 7fe5f8a563
Build backend images / build content-svc (push) Failing after 1m36s
Build backend images / build file-svc (push) Failing after 1m28s
Build backend images / build gateway (push) Failing after 2m11s
Build backend images / build identity-svc (push) Failing after 2m11s
Build backend images / build notification-svc (push) Failing after 3m46s
Build backend images / build render-svc (push) Failing after 55s
Build backend images / build studio-svc (push) Failing after 1m2s
feat(admin): standalone Projects page + per-project asset manager
- content-svc: GET /v1/projects (browse/search all projects across containers,
  paginated, admin) returning template name/slug + AE status; project_assets
  table (mig 23) + entity; GET/POST/DELETE /v1/projects/{id}/assets
- /admin/projects: searchable, paginated list of every renderable project with
  thumbnail, template, aspect/resolution, AE-file + publish status
- ProjectAssets component: list/upload/delete named footage/image/audio/font
  files per project (reused in the projects page; AE file upload alongside)
- nav + fa/en "Projects" label

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 00:39:33 +03:30

119 lines
4.4 KiB
C#

using FlatRender.ContentSvc.Application.Services;
using FlatRender.ContentSvc.Models.Requests;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace FlatRender.ContentSvc.Controllers;
[ApiController]
[Route("v1/templates")]
public class TemplatesController(TemplateService svc) : ControllerBase
{
// ── Containers ────────────────────────────────────────────────────────────
[HttpGet]
public async Task<IActionResult> ListContainers([FromQuery] ContainerListRequest req) =>
Ok(await svc.GetContainersAsync(req));
[HttpGet("{slug}")]
public async Task<IActionResult> GetContainer(string slug)
{
await svc.IncrementContainerViewAsync(
(await svc.GetContainerBySlugAsync(slug)).Id);
return Ok(await svc.GetContainerBySlugAsync(slug));
}
[Authorize(Roles = "Admin")]
[HttpPost]
public async Task<IActionResult> CreateContainer([FromBody] CreateContainerRequest req) =>
Ok(await svc.CreateContainerAsync(req));
[Authorize(Roles = "Admin")]
[HttpPut("{id:guid}")]
public async Task<IActionResult> UpdateContainer(Guid id, [FromBody] UpdateContainerRequest req) =>
Ok(await svc.UpdateContainerAsync(id, req));
[Authorize(Roles = "Admin")]
[HttpDelete("{id:guid}")]
public async Task<IActionResult> DeleteContainer(Guid id)
{
await svc.DeleteContainerAsync(id);
return NoContent();
}
// ── Ranking: set manual sort weight (feature / pin) ─────────────────────────
[Authorize(Roles = "Admin")]
[HttpPatch("{id:guid}/sort")]
public async Task<IActionResult> SetSort(Guid id, [FromBody] SetSortRequest req)
{
await svc.SetContainerSortAsync(id, req.Sort);
return Ok(new { ok = true });
}
}
public record SetSortRequest(int Sort);
[ApiController]
[Route("v1/projects")]
public class ProjectsController(TemplateService svc) : ControllerBase
{
// Browse/search all projects across templates (admin).
[Authorize(Roles = "Admin")]
[HttpGet]
public async Task<IActionResult> List([FromQuery] string? q, [FromQuery] Guid? containerId,
[FromQuery] int page = 1, [FromQuery] int pageSize = 30) =>
Ok(await svc.ListProjectsAsync(q, containerId, page, pageSize));
[HttpGet("{id:guid}")]
public async Task<IActionResult> GetProject(Guid id) =>
Ok(await svc.GetProjectDetailAsync(id));
// ── Per-project assets (footage / images / audio / fonts) ──────────────────
[HttpGet("{id:guid}/assets")]
public async Task<IActionResult> ListAssets(Guid id) =>
Ok(await svc.ListAssetsAsync(id));
[Authorize(Roles = "Admin")]
[HttpPost("{id:guid}/assets")]
public async Task<IActionResult> AddAsset(Guid id, [FromBody] CreateAssetRequest req) =>
Ok(await svc.AddAssetAsync(id, req));
[Authorize(Roles = "Admin")]
[HttpDelete("{id:guid}/assets/{assetId:guid}")]
public async Task<IActionResult> DeleteAsset(Guid id, Guid assetId)
{
await svc.DeleteAssetAsync(assetId);
return NoContent();
}
[Authorize(Roles = "Admin")]
[HttpPost]
public async Task<IActionResult> CreateProject([FromBody] CreateProjectRequest req) =>
Ok(await svc.CreateProjectAsync(req));
[Authorize(Roles = "Admin")]
[HttpPut("{id:guid}")]
public async Task<IActionResult> UpdateProject(Guid id, [FromBody] UpdateProjectRequest req) =>
Ok(await svc.UpdateProjectAsync(id, req));
// Partial update (aspect / resolution / dimensions / duration) without wiping other fields.
[Authorize(Roles = "Admin")]
[HttpPatch("{id:guid}")]
public async Task<IActionResult> PatchProject(Guid id, [FromBody] PatchProjectRequest req) =>
Ok(await svc.PatchProjectAsync(id, req));
// Attach an uploaded After Effects file (.aep) + render composition to a project.
[Authorize(Roles = "Admin")]
[HttpPatch("{id:guid}/aep")]
public async Task<IActionResult> SetAep(Guid id, [FromBody] SetAepRequest req) =>
Ok(await svc.SetProjectAepAsync(id, req));
[Authorize(Roles = "Admin")]
[HttpDelete("{id:guid}")]
public async Task<IActionResult> DeleteProject(Guid id)
{
await svc.DeleteProjectAsync(id);
return NoContent();
}
}