feat(studio B1): persist input edits to content elements (render-binding foundation)
Build backend images / build content-svc (push) Failing after 1m3s
Build backend images / build file-svc (push) Failing after 1m5s
Build backend images / build gateway (push) Failing after 1m0s
Build backend images / build identity-svc (push) Failing after 1m8s
Build backend images / build notification-svc (push) Failing after 57s
Build backend images / build render-svc (push) Failing after 1m6s
Build backend images / build studio-svc (push) Failing after 1m3s
Build backend images / build content-svc (push) Failing after 1m3s
Build backend images / build file-svc (push) Failing after 1m5s
Build backend images / build gateway (push) Failing after 1m0s
Build backend images / build identity-svc (push) Failing after 1m8s
Build backend images / build notification-svc (push) Failing after 57s
Build backend images / build render-svc (push) Failing after 1m6s
Build backend images / build studio-svc (push) Failing after 1m3s
Studio edits previously went only to edit_state; the render binds saved_scene_contents,
so edits never reached the MP4. Add studio-svc PATCH /v1/saved-projects/{id}/contents
(update saved_scene_contents.value/value_file_id by content key, ExecuteSqlInterpolated
for null-safe params) + Next /api/projects/[id]/contents route + persistence hook pushes
edited values (from bridged c-<key> layers) alongside the scene_data save. Verified
text persists incl. UTF-8 Persian (9 chars/17 bytes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -230,6 +230,31 @@ public class StudioService(StudioDbContext db)
|
||||
return await GetProjectAsync(id, userId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update individual scene-input values by content key (the studio editor's live
|
||||
/// edits). Writes to saved_scene_contents.value so the render binder uses them.
|
||||
/// </summary>
|
||||
public async Task<int> UpdateSceneContentsAsync(Guid projectId, Guid userId, List<UpdateContentItem> items)
|
||||
{
|
||||
var owns = await db.SavedProjects.AnyAsync(x => x.Id == projectId && x.UserId == userId);
|
||||
if (!owns) throw new KeyNotFoundException($"Project {projectId} not found");
|
||||
|
||||
var updated = 0;
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(item.Key)) continue;
|
||||
// ExecuteSqlInterpolated maps C# null → typed SQL NULL (raw DBNull params throw).
|
||||
updated += await db.Database.ExecuteSqlInterpolatedAsync($@"
|
||||
UPDATE studio.saved_scene_contents c
|
||||
SET value = {item.Value}, value_file_id = COALESCE({item.ValueFileId}, c.value_file_id), updated_at = now()
|
||||
FROM studio.saved_scenes s
|
||||
WHERE c.saved_scene_id = s.id AND s.saved_project_id = {projectId} AND c.key = {item.Key}");
|
||||
}
|
||||
await db.Database.ExecuteSqlInterpolatedAsync(
|
||||
$"UPDATE studio.saved_projects SET last_edit_date = now(), updated_at = now() WHERE id = {projectId}");
|
||||
return updated;
|
||||
}
|
||||
|
||||
public async Task DeleteProjectAsync(Guid id, Guid userId)
|
||||
{
|
||||
var project = await db.SavedProjects
|
||||
|
||||
@@ -55,6 +55,11 @@ public class StudioController(StudioService svc) : ControllerBase
|
||||
public async Task<IActionResult> SaveScenes(Guid id, [FromBody] List<SaveSceneRequest> scenes) =>
|
||||
Ok(await svc.SaveScenesAsync(id, UserId, scenes));
|
||||
|
||||
/// <summary>Update individual scene-input values by content key (live studio edits).</summary>
|
||||
[HttpPatch("{id:guid}/contents")]
|
||||
public async Task<IActionResult> UpdateContents(Guid id, [FromBody] UpdateContentsRequest req) =>
|
||||
Ok(new { updated = await svc.UpdateSceneContentsAsync(id, UserId, req.Items ?? new List<UpdateContentItem>()) });
|
||||
|
||||
/// <summary>Internal endpoint: get project for render service (no user-ownership check).</summary>
|
||||
[HttpGet("{id:guid}/render-payload")]
|
||||
[Authorize(Roles = "Service")]
|
||||
|
||||
@@ -123,3 +123,8 @@ public record SavedProjectListRequest(
|
||||
string? Q = null,
|
||||
string? Type = null
|
||||
);
|
||||
|
||||
/// <summary>Lightweight update of individual scene-input values — the studio editor
|
||||
/// writes the user's edits here (by content key) so the render binder picks them up.</summary>
|
||||
public record UpdateContentsRequest(List<UpdateContentItem> Items);
|
||||
public record UpdateContentItem(string Key, string? Value, Guid? ValueFileId);
|
||||
|
||||
Reference in New Issue
Block a user