Files
probably-rendering-one-more…/apps/waterfall-wiki/src/lib/studio-design-posts.ts
T
codex-bot dba9785379
studio CI and deployment / verify-and-deploy (pull_request) Successful in 5m33s
waterfall-wiki CI and deployment / verify-and-deploy (pull_request) Successful in 16m52s
Build Studio visual layout library with recoverable design CRUD
2026-10-28 14:45:37 +08:00

26 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {randomUUID} from 'node:crypto';
import type {PoolClient} from 'pg';
import type {Parameters} from './layout-parameters';
export type DesignRow={id:string;layout_id:string;name:string;parameters:Parameters;revision:number;post_id:string|null;archived?:boolean;created_at:Date;updated_at:Date};
export const studioPageUrl=(design:Pick<DesignRow,'id'|'layout_id'>)=>`/studio/layouts/${design.layout_id}?id=${design.id}`;
function postDescription(design:DesignRow){return `已保存的 ${design.layout_id} 排版页面,可直接打开并继续编辑。`}
/** Caller holds the design row lock. All writes share the design's transaction. */
export async function ensureDesignPost(client:PoolClient,design:DesignRow):Promise<DesignRow>{
if(design.post_id){
// A manually edited Wiki note keeps its own title, body, tags and cover.
await client.query('UPDATE posts SET title=$2,description=$3,updated_at=now() WHERE id=$1 AND edited_at IS NULL',[design.post_id,design.name,postDescription(design)]);
return design;
}
const postId=randomUUID(),textId=randomUUID(),imageIds=[...new Set(Object.values(design.parameters.images||{}).flatMap(image=>image.assetId?[image.assetId]:[]))];
const images=imageIds.length?(await client.query("SELECT id FROM assets WHERE id=ANY($1::uuid[]) AND kind='image'",[imageIds])).rows.map(row=>row.id as string):[];
const body=`[打开页面并继续编辑](${studioPageUrl(design)})\n\n排版:${design.layout_id}\n\n页面 ID:\`${design.id}\`\n\n此笔记关联独立保存的页面;页面内容以链接中的保存记录为准。`;
await client.query("INSERT INTO posts(id,slug,kind,title,description,cover_asset_id) VALUES($1,$2,'article',$3,$4,$5)",[postId,'studio-design-'+design.id,design.name,postDescription(design),images[0]||null]);
await client.query("INSERT INTO assets(id,kind,body) VALUES($1,'text',$2)",[textId,body]);
await client.query("INSERT INTO post_assets(post_id,asset_id,position,role) VALUES($1,$2,0,'caption')",[postId,textId]);
for(const [index,imageId] of images.entries())await client.query("INSERT INTO post_assets(post_id,asset_id,position,role) VALUES($1,$2,$3,'content')",[postId,imageId,index+1]);
await client.query("INSERT INTO post_tags(post_id,tag_id) SELECT $1,id FROM tags WHERE name='高级感排版' AND kind='normal' ON CONFLICT DO NOTHING",[postId]);
await client.query('UPDATE studio_designs SET post_id=$2 WHERE id=$1',[design.id,postId]);
return {...design,post_id:postId};
}