diff --git a/apps/waterfall-wiki/db/009_studio_designs.sql b/apps/waterfall-wiki/db/009_studio_designs.sql index 4372f64..b5b36f8 100644 --- a/apps/waterfall-wiki/db/009_studio_designs.sql +++ b/apps/waterfall-wiki/db/009_studio_designs.sql @@ -1,4 +1,5 @@ -CREATE TABLE studio_designs ( +BEGIN; +CREATE TABLE IF NOT EXISTS studio_designs ( id uuid PRIMARY KEY, layout_id text NOT NULL CHECK (layout_id ~ '^ref-(0[1-9]|1[0-9]|2[0-7])$'), name text NOT NULL CHECK (char_length(name) BETWEEN 1 AND 120), @@ -7,4 +8,6 @@ CREATE TABLE studio_designs ( created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); -CREATE INDEX studio_designs_layout_created ON studio_designs(layout_id,created_at DESC,id DESC); +CREATE INDEX IF NOT EXISTS studio_designs_layout_created ON studio_designs(layout_id,created_at DESC,id DESC); +INSERT INTO schema_migrations(version) VALUES('009_studio_designs'); +COMMIT; diff --git a/apps/waterfall-wiki/tests/integration.test.ts b/apps/waterfall-wiki/tests/integration.test.ts index fc6d581..5afa168 100644 --- a/apps/waterfall-wiki/tests/integration.test.ts +++ b/apps/waterfall-wiki/tests/integration.test.ts @@ -57,6 +57,7 @@ test('real PostgreSQL + S3 notes, flat tags, resource sharing and video processi assert.equal((await db().query("SELECT count(*)::integer AS n FROM assets WHERE kind='audio' AND derivation='audio_track'")).rows[0].n,1,'Video soundtracks are ordinary reusable assets'); const audio=join(dir,'audio.wav');await run('ffmpeg',['-y','-v','error','-f','lavfi','-i','sine=frequency=220','-t','0.2',audio]);await upload('audio',[{data:await readFile(audio),name:'audio.wav',mime:'audio/wav'}]);assert.equal((await request('/api/feed?section=audio')).posts.length,1); for(const removed of ['/library/assets','/library/tags'])assert.equal((await fetch(base+removed)).status,404); + assert.equal((await db().query("SELECT 1 FROM schema_migrations WHERE version='009_studio_designs'")).rowCount,1,'Studio migration must be recorded for repeat deployments'); // Studio owns no second database: designs use this same isolated PostgreSQL. for(let i=1;i<=27;i++){const layoutId='ref-'+String(i).padStart(2,'0'),saved=await request('/api/studio/designs',{layoutId,name:'布局 '+i,parameters:{}});assert.equal(saved.design.layoutId,layoutId);assert.equal(saved.design.revision,1);const read=await request('/api/studio/designs/'+saved.design.id);assert.deepEqual(read.design.parameters,{texts:{},images:{},decorations:{}});} const design=await request('/api/studio/designs',{layoutId:'ref-01',name:'可修改效果',parameters:{texts:{T01:{value:'新的标题'}},images:{I01:{assetId:first.assets[0],fit:'contain'}}}});