feat: complete TASK-WP2-07 latest exports

This commit is contained in:
suyx
2026-08-03 01:43:09 +08:00
parent 2bb4f86d04
commit 1b0a05bbcf
20 changed files with 1791 additions and 18 deletions
+40
View File
@@ -72,6 +72,17 @@ interface ProjectStateRow {
state_version: number;
}
interface LatestExportRow {
byte_size: number;
created_at: number;
export_id: string;
format: "jpg" | "png";
pixel_height: number;
pixel_width: number;
sha256: string;
state_version: number;
}
function isProjectRatio(value: string): value is ProjectRatio {
return projectRatios.includes(value as ProjectRatio);
}
@@ -510,6 +521,10 @@ export class ProjectService {
const images = this.database.prepare(`
SELECT image_id, generation_id, created_at FROM project_images WHERE project_id = ? ORDER BY created_at, rowid
`).all(projectId) as ImageRow[];
const latestExports = this.database.prepare(`
SELECT export_id, format, sha256, byte_size, pixel_width, pixel_height, state_version, created_at
FROM latest_exports WHERE project_id = ? ORDER BY format
`).all(projectId) as LatestExportRow[];
const summary = this.projectSummary(row, images.length, generations.at(-1)?.status ?? null);
const projectState = this.readProjectState(projectId);
return {
@@ -519,6 +534,17 @@ export class ProjectService {
draftPrompt: row.draft_prompt,
generations: generations.map((generation) => this.generationView(generation)),
images: images.map((image) => ({ createdAt: iso(image.created_at), generationId: image.generation_id, imageId: image.image_id })),
latestExports: latestExports.map((item) => ({
byteSize: item.byte_size,
createdAt: iso(item.created_at),
downloadUrl: `/api/v1/projects/${projectId}/latest-exports/${item.format}`,
exportId: item.export_id,
format: item.format,
pixelHeight: item.pixel_height,
pixelWidth: item.pixel_width,
sha256: item.sha256,
stateVersion: item.state_version,
})),
pixelHeight: row.pixel_height,
pixelWidth: row.pixel_width,
saveStatus: "saved" as const,
@@ -748,6 +774,20 @@ export class ProjectService {
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
);
CREATE INDEX IF NOT EXISTS project_resource_files_managed ON project_resource_files(managed_file_id, project_id);
CREATE TABLE IF NOT EXISTS latest_exports (
project_id TEXT NOT NULL,
format TEXT NOT NULL CHECK (format IN ('jpg', 'png')),
export_id TEXT NOT NULL UNIQUE,
managed_file_id TEXT NOT NULL,
state_version INTEGER NOT NULL CHECK (state_version >= 1),
sha256 TEXT NOT NULL CHECK (length(sha256) = 64),
byte_size INTEGER NOT NULL CHECK (byte_size > 0),
pixel_width INTEGER NOT NULL CHECK (pixel_width > 0),
pixel_height INTEGER NOT NULL CHECK (pixel_height > 0),
created_at INTEGER NOT NULL,
PRIMARY KEY (project_id, format),
FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS project_cleanup_queue (
cleanup_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL UNIQUE,