28 lines
2.4 KiB
TypeScript
28 lines
2.4 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { z } from "zod";
|
|
import { transaction } from "./db";
|
|
import { works } from "../content/registry";
|
|
export const postInput=z.object({
|
|
slug:z.string().regex(/^[a-z0-9][a-z0-9-]{0,119}$/).optional(),
|
|
kind:z.enum(["image","video","audio","article","motion"]),title:z.string().trim().min(1).max(200),
|
|
assets:z.array(z.object({id:z.uuid(),role:z.enum(["content","caption"]).default("content")})).max(100).default([]),
|
|
text:z.string().trim().max(100000).optional(),tags:z.array(z.uuid()).max(50).default([]),
|
|
coverAssetId:z.uuid().optional(),pageKey:z.string().max(120).optional(),
|
|
});
|
|
export async function createPost(input:z.infer<typeof postInput>) {
|
|
const id=randomUUID(); const slug=input.slug||id;
|
|
if(["article","motion"].includes(input.kind)&&(!input.pageKey||works[input.pageKey]?.kind!==input.kind)) throw new Error("Register the coded page before creating its post");
|
|
return transaction(async client=>{
|
|
const ids=[...new Set(input.assets.map(a=>a.id))];
|
|
const assets=ids.length?(await client.query('SELECT id,kind FROM assets WHERE id=ANY($1::uuid[])',[ids])).rows:[];
|
|
if(assets.length!==ids.length) throw new Error("Referenced asset does not exist");
|
|
if(["image","video","audio"].includes(input.kind)&&!input.assets.some(link=>link.role==='content'&&assets.some(a=>a.id===link.id&&a.kind===input.kind))) throw new Error("A media post needs matching content");
|
|
if(input.coverAssetId && !(await client.query("SELECT 1 FROM assets WHERE id=$1 AND kind='image'",[input.coverAssetId])).rowCount) throw new Error("Cover must reference an image");
|
|
await client.query('INSERT INTO posts(id,slug,kind,title,page_key,cover_asset_id) VALUES($1,$2,$3,$4,$5,$6)',[id,slug,input.kind,input.title,input.pageKey||null,input.coverAssetId||null]);
|
|
for(const [position,a] of input.assets.entries()) await client.query('INSERT INTO post_assets(post_id,asset_id,position,role) VALUES($1,$2,$3,$4)',[id,a.id,position,a.role]);
|
|
if(input.text){const textId=randomUUID();await client.query("INSERT INTO assets(id,kind,body) VALUES($1,'text',$2)",[textId,input.text]);await client.query("INSERT INTO post_assets(post_id,asset_id,position,role) VALUES($1,$2,$3,'caption')",[id,textId,input.assets.length]);}
|
|
for(const tag of new Set(input.tags)) await client.query('INSERT INTO post_tags(post_id,tag_id) VALUES($1,$2)',[id,tag]);
|
|
return {id,slug};
|
|
});
|
|
}
|