feat: favorites (#2103)

* feat: favorites and templates(ee)

* rename migrations

* fix sidebar

* cleanup tabs

* fix

* turn off templates

* cleanup

* uuid validation
This commit is contained in:
Philip Okugbe
2026-04-12 22:06:25 +01:00
committed by GitHub
parent 57efb91bd3
commit d42091ccb1
90 changed files with 4557 additions and 187 deletions

View File

@@ -0,0 +1,76 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('templates')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
)
.addColumn('title', 'varchar')
.addColumn('description', 'text')
.addColumn('content', 'jsonb')
.addColumn('ydoc', 'bytea')
.addColumn('icon', 'varchar')
.addColumn('space_id', 'uuid', (col) =>
col.references('spaces.id').onDelete('cascade'),
)
.addColumn('workspace_id', 'uuid', (col) =>
col.notNull().references('workspaces.id').onDelete('cascade'),
)
.addColumn('creator_id', 'uuid', (col) =>
col.references('users.id').onDelete('set null'),
)
.addColumn('last_updated_by_id', 'uuid', (col) =>
col.references('users.id').onDelete('set null'),
)
.addColumn('collaborator_ids', sql`uuid[]`)
.addColumn('text_content', 'text', (col) => col)
.addColumn('tsv', sql`tsvector`, (col) => col)
.addColumn('created_at', 'timestamptz', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updated_at', 'timestamptz', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('deleted_at', 'timestamptz')
.execute();
await db.schema
.createIndex('idx_templates_workspace_id')
.on('templates')
.columns(['workspace_id'])
.execute();
await db.schema
.createIndex('idx_templates_space_id')
.on('templates')
.columns(['space_id'])
.execute();
await db.schema
.createIndex('templates_tsv_idx')
.on('templates')
.using('GIN')
.column('tsv')
.execute();
await sql`
CREATE OR REPLACE FUNCTION templates_tsvector_trigger() RETURNS trigger AS $$
begin
new.tsv :=
setweight(to_tsvector('english', f_unaccent(coalesce(new.title, ''))), 'A') ||
setweight(to_tsvector('english', f_unaccent(substring(coalesce(new.text_content, ''), 1, 1000000))), 'B');
return new;
end;
$$ LANGUAGE plpgsql;
`.execute(db);
await sql`CREATE OR REPLACE TRIGGER templates_tsvector_update BEFORE INSERT OR UPDATE
ON templates FOR EACH ROW EXECUTE FUNCTION templates_tsvector_trigger();`.execute(db);
}
export async function down(db: Kysely<any>): Promise<void> {
await sql`DROP TRIGGER IF EXISTS templates_tsvector_update ON templates`.execute(db);
await sql`DROP FUNCTION IF EXISTS templates_tsvector_trigger`.execute(db);
await db.schema.dropTable('templates').execute();
}

View File

@@ -0,0 +1,63 @@
import { type Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('favorites')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_uuid_v7()`),
)
.addColumn('user_id', 'uuid', (col) =>
col.references('users.id').onDelete('cascade').notNull(),
)
.addColumn('page_id', 'uuid', (col) =>
col.references('pages.id').onDelete('cascade'),
)
.addColumn('space_id', 'uuid', (col) =>
col.references('spaces.id').onDelete('cascade'),
)
.addColumn('template_id', 'uuid', (col) =>
col.references('templates.id').onDelete('cascade'),
)
.addColumn('type', 'varchar', (col) => col.notNull())
.addColumn('workspace_id', 'uuid', (col) =>
col.references('workspaces.id').onDelete('cascade').notNull(),
)
.addColumn('created_at', 'timestamptz', (col) =>
col.defaultTo(sql`now()`).notNull(),
)
.execute();
await db.schema
.createIndex('idx_favorites_user_page')
.on('favorites')
.columns(['user_id', 'page_id'])
.unique()
.where('page_id', 'is not', null)
.execute();
await db.schema
.createIndex('idx_favorites_user_space')
.on('favorites')
.columns(['user_id', 'space_id'])
.unique()
.where('space_id', 'is not', null)
.execute();
await db.schema
.createIndex('idx_favorites_user_template')
.on('favorites')
.columns(['user_id', 'template_id'])
.unique()
.where('template_id', 'is not', null)
.execute();
await db.schema
.createIndex('idx_favorites_user_workspace_type')
.on('favorites')
.columns(['user_id', 'workspace_id', 'type'])
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable('favorites').execute();
}