feat(ee): personal spaces (#2298)

* feat(ee): personal spaces

* pref

* feat: on-demand only

* error notification
This commit is contained in:
Philip Okugbe
2026-06-20 14:27:41 +01:00
committed by GitHub
parent 510199cf04
commit d68e241f45
23 changed files with 366 additions and 9 deletions
@@ -0,0 +1,24 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('spaces')
.addColumn('is_personal', 'boolean', (col) =>
col.notNull().defaultTo(false),
)
.execute();
await sql`
CREATE UNIQUE INDEX spaces_personal_creator_unique
ON spaces (creator_id)
WHERE is_personal = true AND deleted_at IS NULL
`.execute(db);
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.dropIndex('spaces_personal_creator_unique')
.ifExists()
.execute();
await db.schema.alterTable('spaces').dropColumn('is_personal').execute();
}
@@ -57,6 +57,22 @@ export class SpaceRepo {
.executeTakeFirst();
}
async findPersonalSpace(
userId: string,
workspaceId: string,
trx?: KyselyTransaction,
): Promise<Space | undefined> {
const db = dbOrTx(this.db, trx);
return db
.selectFrom('spaces')
.selectAll('spaces')
.where('workspaceId', '=', workspaceId)
.where('creatorId', '=', userId)
.where('isPersonal', '=', true)
.where('deletedAt', 'is', null)
.executeTakeFirst();
}
async slugExists(
slug: string,
workspaceId: string,
@@ -251,4 +251,24 @@ export class WorkspaceRepo {
.executeTakeFirst();
}
async updateSpaceSettings(
workspaceId: string,
prefKey: string,
prefValue: string | boolean,
trx?: KyselyTransaction,
) {
const db = dbOrTx(this.db, trx);
return db
.updateTable('workspaces')
.set({
settings: sql`COALESCE(settings, '{}'::jsonb)
|| jsonb_build_object('spaces', COALESCE(settings->'spaces', '{}'::jsonb)
|| jsonb_build_object('${sql.raw(prefKey)}', ${sql.lit(prefValue)}))`,
updatedAt: new Date(),
})
.where('id', '=', workspaceId)
.returning(this.baseFields)
.executeTakeFirst();
}
}
+1
View File
@@ -322,6 +322,7 @@ export interface Spaces {
deletedAt: Timestamp | null;
description: string | null;
id: Generated<string>;
isPersonal: Generated<boolean>;
logo: string | null;
name: string | null;
settings: Json | null;