Kysely - WIP

* create database migration files
* kysely codegen
* kysely migrate
This commit is contained in:
Philipinho
2024-03-24 16:59:26 +00:00
parent 7d56920ad5
commit d855152dda
19 changed files with 2634 additions and 643 deletions

View File

@ -23,32 +23,42 @@
"migration:create": "cd ./src/database/migrations/ && typeorm-ts-node-commonjs migration:create", "migration:create": "cd ./src/database/migrations/ && typeorm-ts-node-commonjs migration:create",
"migration:run": "pnpm run typeorm migration:run", "migration:run": "pnpm run typeorm migration:run",
"migration:revert": "pnpm run typeorm migration:revert", "migration:revert": "pnpm run typeorm migration:revert",
"migration:show": "pnpm run typeorm migration:show" "migration:show": "pnpm run typeorm migration:show",
"migration:make": "tsx ./src/kysely/migrate.ts create",
"migration:up": "tsx ./src/kysely/migrate.ts up",
"migration:down": "tsx ./src/kysely/migrate.ts down",
"migration:latest": "tsx ./src/kysely/migrate.ts latest",
"migration:reset": "tsx ./src/kysely/migrate.ts redo",
"migration:codegen": "kysely-codegen --dialect=postgres --env-file=../../.env --out-file=./src/kysely/types/db.d.ts"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.535.0", "@aws-sdk/client-s3": "^3.540.0",
"@aws-sdk/s3-request-presigner": "^3.535.0", "@aws-sdk/s3-request-presigner": "^3.540.0",
"@casl/ability": "^6.7.0", "@casl/ability": "^6.7.0",
"@fastify/multipart": "^8.1.0", "@fastify/multipart": "^8.2.0",
"@fastify/static": "^7.0.1", "@fastify/static": "^7.0.1",
"@nestjs/common": "^10.3.3", "@nestjs/common": "^10.3.5",
"@nestjs/config": "^3.2.0", "@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.3.3", "@nestjs/core": "^10.3.5",
"@nestjs/jwt": "^10.2.0", "@nestjs/jwt": "^10.2.0",
"@nestjs/mapped-types": "^2.0.5", "@nestjs/mapped-types": "^2.0.5",
"@nestjs/passport": "^10.0.3", "@nestjs/passport": "^10.0.3",
"@nestjs/platform-fastify": "^10.3.3", "@nestjs/platform-fastify": "^10.3.5",
"@nestjs/platform-socket.io": "^10.3.3", "@nestjs/platform-socket.io": "^10.3.5",
"@nestjs/serve-static": "^4.0.1", "@nestjs/serve-static": "^4.0.1",
"@nestjs/typeorm": "^10.0.2", "@nestjs/typeorm": "^10.0.2",
"@nestjs/websockets": "^10.3.3", "@nestjs/websockets": "^10.3.5",
"@types/pg": "^8.11.4",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bytes": "^3.1.2", "bytes": "^3.1.2",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"class-validator": "^0.14.1", "class-validator": "^0.14.1",
"fastify": "^4.26.2", "fastify": "^4.26.2",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"kysely": "^0.27.3",
"kysely-migration-cli": "^0.4.0",
"mime-types": "^2.1.35", "mime-types": "^2.1.35",
"nestjs-kysely": "^0.1.6",
"passport-jwt": "^4.0.1", "passport-jwt": "^4.0.1",
"pg": "^8.11.3", "pg": "^8.11.3",
"pg-tsquery": "^8.4.2", "pg-tsquery": "^8.4.2",
@ -64,24 +74,25 @@
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^10.3.2", "@nestjs/cli": "^10.3.2",
"@nestjs/schematics": "^10.1.1", "@nestjs/schematics": "^10.1.1",
"@nestjs/testing": "^10.3.3", "@nestjs/testing": "^10.3.5",
"@types/bcrypt": "^5.0.2", "@types/bcrypt": "^5.0.2",
"@types/bytes": "^3.1.4", "@types/bytes": "^3.1.4",
"@types/debounce": "^1.2.4", "@types/debounce": "^1.2.4",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/jest": "^29.5.12", "@types/jest": "^29.5.12",
"@types/mime-types": "^2.1.4", "@types/mime-types": "^2.1.4",
"@types/node": "^20.11.28", "@types/node": "^20.11.30",
"@types/passport-jwt": "^4.0.1", "@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.2", "@types/supertest": "^6.0.2",
"@types/uuid": "^9.0.8", "@types/uuid": "^9.0.8",
"@types/ws": "^8.5.10", "@types/ws": "^8.5.10",
"@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.2.0", "@typescript-eslint/parser": "^7.3.1",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3", "eslint-plugin-prettier": "^5.1.3",
"jest": "^29.7.0", "jest": "^29.7.0",
"kysely-codegen": "^0.14.1",
"prettier": "^3.2.5", "prettier": "^3.2.5",
"source-map-support": "^0.5.21", "source-map-support": "^0.5.21",
"supertest": "^6.3.4", "supertest": "^6.3.4",
@ -89,7 +100,7 @@
"ts-loader": "^9.5.1", "ts-loader": "^9.5.1",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0", "tsconfig-paths": "^4.2.0",
"typescript": "^5.4.2" "typescript": "^5.4.3"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [

View File

@ -8,6 +8,10 @@ import { DatabaseModule } from './database/database.module';
import { WsModule } from './ws/ws.module'; import { WsModule } from './ws/ws.module';
import { ServeStaticModule } from '@nestjs/serve-static'; import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path'; import { join } from 'path';
import { KyselyModule } from 'nestjs-kysely';
import { EnvironmentService } from './integrations/environment/environment.service';
import { PostgresDialect } from 'kysely';
import { Pool } from 'pg';
@Module({ @Module({
imports: [ imports: [
@ -19,6 +23,19 @@ import { join } from 'path';
ServeStaticModule.forRoot({ ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', '..', '..', 'client/dist'), rootPath: join(__dirname, '..', '..', '..', 'client/dist'),
}), }),
KyselyModule.forRootAsync({
imports: [],
inject: [EnvironmentService],
useFactory: (envService: EnvironmentService) => {
return {
dialect: new PostgresDialect({
pool: new Pool({
connectionString: envService.getDatabaseURL(),
}) as any,
}),
};
},
}),
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],

View File

@ -0,0 +1,35 @@
import * as path from 'path';
import { promises as fs } from 'fs';
import pg from 'pg';
import {
Kysely,
Migrator,
PostgresDialect,
FileMigrationProvider,
} from 'kysely';
import { run } from 'kysely-migration-cli';
import * as dotenv from 'dotenv';
import { envPath } from '../helpers/utils';
dotenv.config({ path: envPath });
const migrationFolder = path.join(__dirname, './migrations');
const db = new Kysely<any>({
dialect: new PostgresDialect({
pool: new pg.Pool({
connectionString: process.env.DATABASE_URL,
}) as any,
}),
});
const migrator = new Migrator({
db,
provider: new FileMigrationProvider({
fs,
path,
migrationFolder,
}),
});
run(db, migrator, migrationFolder);

View File

@ -0,0 +1,41 @@
import { Kysely, sql } from 'kysely';
import { UserRole } from '../../helpers/types/permission';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('workspaces')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('name', 'varchar', (col) => col)
.addColumn('description', 'text', (col) => col)
.addColumn('logo', 'varchar', (col) => col)
.addColumn('hostname', 'varchar', (col) => col)
.addColumn('customDomain', 'varchar', (col) => col)
.addColumn('enableInvite', 'boolean', (col) => col.notNull())
.addColumn('inviteCode', 'varchar', (col) => col)
.addColumn('settings', 'jsonb', (col) => col)
.addColumn('defaultRole', 'varchar', (col) =>
col.defaultTo(UserRole.MEMBER).notNull(),
)
.addColumn('creatorId', 'uuid', (col) => col)
.addColumn('defaultSpaceId', 'uuid', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('deletedAt', 'timestamp', (col) => col)
.addUniqueConstraint('UQ_workspaces_hostname', ['hostname'])
.addUniqueConstraint('UQ_workspaces_inviteCode', ['inviteCode'])
.addUniqueConstraint('UQ_workspaces_inviteCode', ['inviteCode'])
.execute();
// CONSTRAINT "REL_workspaces_creatorId" UNIQUE ("creatorId"),
// CONSTRAINT "REL_workspaces_defaultSpaceId" UNIQUE ("defaultSpaceId"),
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropTable('workspaces').execute();
}

View File

@ -0,0 +1,50 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('users')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('name', 'varchar', (col) => col)
.addColumn('email', 'varchar', (col) => col.notNull())
.addColumn('emailVerifiedAt', 'timestamp', (col) => col)
.addColumn('password', 'varchar', (col) => col.notNull())
.addColumn('avatarUrl', 'varchar', (col) => col)
.addColumn('role', 'varchar', (col) => col)
.addColumn('workspaceId', 'uuid', (col) => col)
.addColumn('locale', 'varchar', (col) => col)
.addColumn('timezone', 'varchar', (col) => col)
.addColumn('settings', 'jsonb', (col) => col)
.addColumn('lastLoginAt', 'timestamp', (col) => col)
.addColumn('lastLoginIp', 'varchar', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addUniqueConstraint('UQ_users_email_workspaceId', ['email', 'workspaceId'])
.execute();
// foreign key relations
await db.schema
.alterTable('users')
.addForeignKeyConstraint(
'FK_users_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('users')
.dropConstraint('FK_users_workspaces_workspaceId')
.execute();
await db.schema.dropTable('users').execute();
}

View File

@ -0,0 +1,59 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('groups')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('name', 'varchar', (col) => col.notNull())
.addColumn('description', 'text', (col) => col)
.addColumn('isDefault', 'boolean', (col) => col.notNull())
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('creatorId', 'uuid', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addUniqueConstraint('UQ_groups_name_workspaceId', ['name', 'workspaceId'])
.execute();
// foreign key relations
await db.schema
.alterTable('groups')
.addForeignKeyConstraint(
'FK_groups_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('groups')
.addForeignKeyConstraint(
'FK_groups_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('groups')
.dropConstraint('FK_groups_workspaces_workspaceId')
.execute();
await db.schema
.alterTable('groups')
.dropConstraint('FK_groups_users_creatorId')
.execute();
await db.schema.dropTable('groups').execute();
}

View File

@ -0,0 +1,56 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('group_users')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('userId', 'uuid', (col) => col.notNull())
.addColumn('groupId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addUniqueConstraint('UQ_group_users_groupId_userId', ['groupId', 'userId'])
.execute();
// foreign key relations
await db.schema
.alterTable('group_users')
.addForeignKeyConstraint(
'FK_group_users_users_userId',
['userId'],
'users',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('group_users')
.addForeignKeyConstraint(
'FK_group_users_groups_groupId',
['groupId'],
'groups',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('group_users')
.dropConstraint('FK_group_users_users_userId')
.execute();
await db.schema
.alterTable('group_users')
.dropConstraint('FK_group_users_groups_groupId')
.execute();
await db.schema.dropTable('group_users').execute();
}

View File

@ -0,0 +1,66 @@
import { Kysely, sql } from 'kysely';
import { SpaceRole, SpaceVisibility } from '../../helpers/types/permission';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('spaces')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('name', 'varchar', (col) => col)
.addColumn('description', 'text', (col) => col)
.addColumn('slug', 'varchar', (col) => col)
.addColumn('icon', 'varchar', (col) => col)
.addColumn('visibility', 'varchar', (col) =>
col.defaultTo(SpaceVisibility.OPEN).notNull(),
)
.addColumn('defaultRole', 'varchar', (col) =>
col.defaultTo(SpaceRole.WRITER).notNull(),
)
.addColumn('creatorId', 'uuid', (col) => col)
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addUniqueConstraint('UQ_spaces_slug_workspaceId', ['slug', 'workspaceId'])
.execute();
// foreign key relations
await db.schema
.alterTable('spaces')
.addForeignKeyConstraint(
'FK_spaces_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('spaces')
.addForeignKeyConstraint(
'FK_spaces_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('spaces')
.dropConstraint('FK_spaces_users_creatorId')
.execute();
await db.schema
.alterTable('spaces')
.dropConstraint('FK_spaces_workspaces_workspaceId')
.execute();
await db.schema.dropTable('spaces').execute();
}

View File

@ -0,0 +1,100 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('space_members')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('userId', 'uuid', (col) => col)
.addColumn('groupId', 'uuid', (col) => col)
.addColumn('spaceId', 'uuid', (col) => col.notNull())
.addColumn('role', 'varchar', (col) => col.notNull())
.addColumn('creatorId', 'uuid', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addUniqueConstraint('UQ_space_members_spaceId_userId', [
'spaceId',
'userId',
])
.addUniqueConstraint('UQ_space_members_spaceId_groupId', [
'spaceId',
'groupId',
])
.addCheckConstraint(
'CHK_allow_userId_or_groupId',
sql`(("userId" IS NOT NULL AND "groupId" IS NULL) OR ("userId" IS NULL AND "groupId" IS NOT NULL))`,
)
.execute();
// foreign key relations
await db.schema
.alterTable('space_members')
.addForeignKeyConstraint(
'FK_space_members_users_userId',
['userId'],
'users',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('space_members')
.addForeignKeyConstraint(
'FK_space_members_groups_groupId',
['groupId'],
'groups',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('space_members')
.addForeignKeyConstraint(
'FK_space_members_spaces_spaceId',
['spaceId'],
'spaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('space_members')
.addForeignKeyConstraint(
'FK_space_members_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('space_members')
.dropConstraint('FK_space_members_users_userId')
.execute();
await db.schema
.alterTable('space_members')
.dropConstraint('FK_space_members_groups_groupId')
.execute();
await db.schema
.alterTable('space_members')
.dropConstraint('FK_space_members_spaces_spaceId')
.execute();
await db.schema
.alterTable('space_members')
.dropConstraint('FK_space_members_users_creatorId')
.execute();
await db.schema.dropTable('space_members').execute();
}

View File

@ -0,0 +1,36 @@
import { Kysely } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('workspaces')
.addForeignKeyConstraint(
'FK_workspaces_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('workspaces')
.addForeignKeyConstraint(
'FK_workspaces_spaces_defaultSpaceId',
['defaultSpaceId'],
'spaces',
['id'],
)
.onDelete('set null')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('workspaces')
.dropConstraint('FK_workspaces_users_creatorId')
.execute();
await db.schema
.alterTable('workspaces')
.dropConstraint('FK_workspaces_spaces_defaultSpaceId')
.execute();
}

View File

@ -0,0 +1,56 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('workspace_invitations')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('invitedById', 'uuid', (col) => col.notNull())
.addColumn('email', 'varchar', (col) => col.notNull())
.addColumn('role', 'varchar', (col) => col.notNull())
.addColumn('status', 'varchar', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.execute();
// foreign key relations
await db.schema
.alterTable('workspace_invitations')
.addForeignKeyConstraint(
'FK_workspace_invitations_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('workspace_invitations')
.addForeignKeyConstraint(
'FK_workspace_invitations_users_invitedById',
['invitedById'],
'users',
['id'],
)
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('workspace_invitations')
.dropConstraint('FK_workspace_invitations_workspaces_workspaceId')
.execute();
await db.schema
.alterTable('workspace_invitations')
.dropConstraint('FK_workspace_invitations_users_invitedById')
.execute();
await db.schema.dropTable('workspace_invitations').execute();
}

View File

@ -0,0 +1,49 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('pages')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('title', 'varchar', (col) => col)
.addColumn('icon', 'varchar', (col) => col)
.addColumn('content', 'jsonb', (col) => col)
.addColumn('html', 'text', (col) => col)
.addColumn('textContent', 'text', (col) => col)
.addColumn('tsv', sql`tsvector`, (col) => col)
.addColumn('ydoc', 'bytea', (col) => col)
.addColumn('slug', 'varchar', (col) => col)
.addColumn('coverPhoto', 'varchar', (col) => col)
.addColumn('editor', 'varchar', (col) => col)
.addColumn('shareId', 'varchar', (col) => col)
.addColumn('parentPageId', 'uuid', (col) => col)
.addColumn('creatorId', 'uuid', (col) => col.notNull())
.addColumn('lastUpdatedById', 'uuid', (col) => col)
.addColumn('deletedById', 'uuid', (col) => col)
.addColumn('spaceId', 'uuid', (col) => col.notNull())
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('isLocked', 'boolean', (col) => col.notNull())
.addColumn('status', 'varchar', (col) => col)
.addColumn('publishedAt', 'date', (col) => col)
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('deletedAt', 'timestamp', (col) => col)
.execute();
await db.schema
.createIndex('IDX_pages_tsv')
.on('pages')
.using('GIN')
.column('tsv')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema.dropIndex('IDX_pages_tsv').on('pages').execute();
await db.schema.dropTable('pages').execute();
}

View File

@ -0,0 +1,94 @@
import { Kysely } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('pages')
.addForeignKeyConstraint(
'FK_pages_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('pages')
.addForeignKeyConstraint(
'FK_pages_users_lastUpdatedById',
['lastUpdatedById'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('pages')
.addForeignKeyConstraint(
'FK_pages_users_deletedById',
['deletedById'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('pages')
.addForeignKeyConstraint('FK_pages_spaces_spaceId', ['spaceId'], 'spaces', [
'id',
])
.onDelete('cascade')
.execute();
await db.schema
.alterTable('pages')
.addForeignKeyConstraint(
'FK_pages_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('pages')
.addForeignKeyConstraint(
'FK_pages_pages_parentPageId',
['parentPageId'],
'pages',
['id'],
)
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_users_creatorId')
.execute();
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_users_lastUpdatedById')
.execute();
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_users_deletedById')
.execute();
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_spaces_spaceId')
.execute();
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_workspaces_workspaceId')
.execute();
await db.schema
.alterTable('pages')
.dropConstraint('FK_pages_pages_parentPageId')
.execute();
}

View File

@ -0,0 +1,95 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('page_history')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('pageId', 'uuid', (col) => col.notNull())
.addColumn('title', 'varchar', (col) => col)
.addColumn('content', 'jsonb', (col) => col)
.addColumn('slug', 'varchar', (col) => col)
.addColumn('icon', 'varchar', (col) => col)
.addColumn('coverPhoto', 'varchar', (col) => col)
.addColumn('version', 'int4', (col) => col.notNull())
.addColumn('lastUpdatedById', 'uuid', (col) => col.notNull())
.addColumn('spaceId', 'uuid', (col) => col.notNull())
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.execute();
// foreign key relations
await db.schema
.alterTable('page_history')
.addForeignKeyConstraint(
'FK_page_history_pages_pageId',
['pageId'],
'pages',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('page_history')
.addForeignKeyConstraint(
'FK_page_history_users_lastUpdatedById',
['lastUpdatedById'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('page_history')
.addForeignKeyConstraint(
'FK_page_history_spaces_spaceId',
['spaceId'],
'spaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('page_history')
.addForeignKeyConstraint(
'FK_page_history_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.onUpdate('no action')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('page_history')
.dropConstraint('FK_page_history_pages_pageId')
.execute();
await db.schema
.alterTable('page_history')
.dropConstraint('FK_page_history_users_lastUpdatedById')
.execute();
await db.schema
.alterTable('page_history')
.dropConstraint('FK_page_history_spaces_spaceId')
.execute();
await db.schema
.alterTable('page_history')
.dropConstraint('FK_page_history_workspaces_workspaceId')
.execute();
await db.schema.dropTable('page_history').execute();
}

View File

@ -0,0 +1,63 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('page_ordering')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('entityId', 'uuid', (col) => col.notNull())
.addColumn('entityType', 'varchar', (col) => col.notNull())
.addColumn('childrenIds', sql`uuid[]`, (col) => col.notNull())
.addColumn('spaceId', 'uuid', (col) => col.notNull())
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('deletedAt', 'timestamp', (col) => col)
.addUniqueConstraint('UQ_page_ordering_entityId_entityType', [
'entityId',
'entityType',
])
.execute();
// foreign key relations
await db.schema
.alterTable('page_ordering')
.addForeignKeyConstraint(
'FK_page_ordering_spaces_spaceId',
['spaceId'],
'spaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('page_ordering')
.addForeignKeyConstraint(
'FK_page_ordering_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('page_ordering')
.dropConstraint('FK_page_ordering_spaces_spaceId')
.execute();
await db.schema
.alterTable('page_ordering')
.dropConstraint('FK_page_ordering_workspaces_workspaceId')
.execute();
await db.schema.dropTable('page_ordering').execute();
}

View File

@ -0,0 +1,121 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('comments')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('content', 'jsonb', (col) => col)
.addColumn('selection', 'varchar', (col) => col)
.addColumn('type', 'varchar', (col) => col)
.addColumn('creatorId', 'uuid', (col) => col.notNull())
.addColumn('pageId', 'uuid', (col) => col.notNull())
.addColumn('parentCommentId', 'uuid', (col) => col)
.addColumn('resolvedById', 'uuid', (col) => col)
.addColumn('resolvedAt', 'timestamp', (col) => col)
.addColumn('spaceId', 'uuid', (col) => col.notNull())
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('editedAt', 'timestamp', (col) => col)
.addColumn('deletedAt', 'timestamp', (col) => col)
.execute();
// foreign key relations
await db.schema
.alterTable('comments')
.addForeignKeyConstraint(
'FK_comments_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('comments')
.addForeignKeyConstraint('FK_comments_pages_pageId', ['pageId'], 'pages', [
'id',
])
.onDelete('cascade')
.execute();
await db.schema
.alterTable('comments')
.addForeignKeyConstraint(
'FK_comments_comments_parentCommentId',
['parentCommentId'],
'comments',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('comments')
.addForeignKeyConstraint(
'FK_comments_users_resolvedById',
['resolvedById'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('comments')
.addForeignKeyConstraint(
'FK_comments_spaces_spaceId',
['spaceId'],
'spaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('comments')
.addForeignKeyConstraint(
'FK_comments_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_users_creatorId')
.execute();
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_pages_pageId')
.execute();
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_comments_parentCommentId')
.execute();
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_users_resolvedById')
.execute();
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_spaces_spaceId')
.execute();
await db.schema
.alterTable('comments')
.dropConstraint('FK_comments_workspaces_workspaceId')
.execute();
await db.schema.dropTable('comments').execute();
}

View File

@ -0,0 +1,94 @@
import { Kysely, sql } from 'kysely';
export async function up(db: Kysely<any>): Promise<void> {
await db.schema
.createTable('attachments')
.addColumn('id', 'uuid', (col) =>
col.primaryKey().defaultTo(sql`gen_random_uuid()`),
)
.addColumn('fileName', 'varchar', (col) => col.notNull())
.addColumn('filePath', 'varchar', (col) => col.notNull())
.addColumn('fileSize', 'int8', (col) => col)
.addColumn('fileExt', 'varchar', (col) => col.notNull())
.addColumn('mimeType', 'varchar', (col) => col)
.addColumn('type', 'varchar', (col) => col)
.addColumn('creatorId', 'uuid', (col) => col.notNull())
.addColumn('pageId', 'uuid', (col) => col)
.addColumn('spaceId', 'uuid', (col) => col)
.addColumn('workspaceId', 'uuid', (col) => col.notNull())
.addColumn('createdAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('updatedAt', 'timestamp', (col) =>
col.notNull().defaultTo(sql`now()`),
)
.addColumn('deletedAt', 'timestamp', (col) => col)
.execute();
// foreign key relations
await db.schema
.alterTable('attachments')
.addForeignKeyConstraint(
'FK_attachments_users_creatorId',
['creatorId'],
'users',
['id'],
)
.execute();
await db.schema
.alterTable('attachments')
.addForeignKeyConstraint(
'FK_attachments_pages_pageId',
['pageId'],
'pages',
['id'],
)
.execute();
await db.schema
.alterTable('attachments')
.addForeignKeyConstraint(
'FK_attachments_spaces_spaceId',
['spaceId'],
'spaces',
['id'],
)
.onDelete('cascade')
.execute();
await db.schema
.alterTable('attachments')
.addForeignKeyConstraint(
'FK_attachments_workspaces_workspaceId',
['workspaceId'],
'workspaces',
['id'],
)
.onDelete('cascade')
.execute();
}
export async function down(db: Kysely<any>): Promise<void> {
await db.schema
.alterTable('attachments')
.dropConstraint('FK_attachments_users_creatorId')
.execute();
await db.schema
.alterTable('attachments')
.dropConstraint('FK_attachments_pages_pageId')
.execute();
await db.schema
.alterTable('attachments')
.dropConstraint('FK_attachments_spaces_spaceId')
.execute();
await db.schema
.alterTable('attachments')
.dropConstraint('FK_attachments_workspaces_workspaceId')
.execute();
await db.schema.dropTable('attachments').execute();
}

221
apps/server/src/kysely/types/db.d.ts vendored Normal file
View File

@ -0,0 +1,221 @@
import type { ColumnType } from 'kysely';
export type Generated<T> =
T extends ColumnType<infer S, infer I, infer U>
? ColumnType<S, I | undefined, U>
: ColumnType<T, T | undefined, T>;
export type Int8 = ColumnType<
string,
bigint | number | string,
bigint | number | string
>;
export type Json = JsonValue;
export type JsonArray = JsonValue[];
export type JsonObject = {
[K in string]?: JsonValue;
};
export type JsonPrimitive = boolean | number | string | null;
export type JsonValue = JsonArray | JsonObject | JsonPrimitive;
export type Timestamp = ColumnType<Date, Date | string, Date | string>;
export interface Attachments {
createdAt: Generated<Timestamp>;
creatorId: string;
deletedAt: Timestamp | null;
fileExt: string;
fileName: string;
filePath: string;
fileSize: Int8 | null;
id: Generated<string>;
mimeType: string | null;
pageId: string | null;
spaceId: string | null;
type: string | null;
updatedAt: Generated<Timestamp>;
workspaceId: string;
}
export interface Comments {
content: Json | null;
createdAt: Generated<Timestamp>;
creatorId: string;
deletedAt: Timestamp | null;
editedAt: Timestamp | null;
id: Generated<string>;
pageId: string;
parentCommentId: string | null;
resolvedAt: Timestamp | null;
resolvedById: string | null;
selection: string | null;
spaceId: string;
type: string | null;
workspaceId: string;
}
export interface Groups {
createdAt: Generated<Timestamp>;
creatorId: string | null;
description: string | null;
id: Generated<string>;
isDefault: boolean;
name: string;
updatedAt: Generated<Timestamp>;
workspaceId: string;
}
export interface GroupUsers {
createdAt: Generated<Timestamp>;
groupId: string;
id: Generated<string>;
updatedAt: Generated<Timestamp>;
userId: string;
}
export interface PageHistory {
content: Json | null;
coverPhoto: string | null;
createdAt: Generated<Timestamp>;
icon: string | null;
id: Generated<string>;
lastUpdatedById: string;
pageId: string;
slug: string | null;
spaceId: string;
title: string | null;
updatedAt: Generated<Timestamp>;
version: number;
workspaceId: string;
}
export interface PageOrdering {
childrenIds: string[];
createdAt: Generated<Timestamp>;
deletedAt: Timestamp | null;
entityId: string;
entityType: string;
id: Generated<string>;
spaceId: string;
updatedAt: Generated<Timestamp>;
workspaceId: string;
}
export interface Pages {
content: Json | null;
coverPhoto: string | null;
createdAt: Generated<Timestamp>;
creatorId: string;
deletedAt: Timestamp | null;
deletedById: string | null;
editor: string | null;
html: string | null;
icon: string | null;
id: Generated<string>;
isLocked: boolean;
lastUpdatedById: string | null;
parentPageId: string | null;
publishedAt: Timestamp | null;
shareId: string | null;
slug: string | null;
spaceId: string;
status: string | null;
textContent: string | null;
title: string | null;
tsv: string | null;
updatedAt: Generated<Timestamp>;
workspaceId: string;
ydoc: Buffer | null;
}
export interface SpaceMembers {
createdAt: Generated<Timestamp>;
creatorId: string | null;
groupId: string | null;
id: Generated<string>;
role: string;
spaceId: string;
updatedAt: Generated<Timestamp>;
userId: string | null;
}
export interface Spaces {
createdAt: Generated<Timestamp>;
creatorId: string | null;
defaultRole: Generated<string>;
description: string | null;
icon: string | null;
id: Generated<string>;
name: string | null;
slug: string | null;
updatedAt: Generated<Timestamp>;
visibility: Generated<string>;
workspaceId: string;
}
export interface Users {
avatarUrl: string | null;
createdAt: Generated<Timestamp>;
email: string;
emailVerifiedAt: Timestamp | null;
id: Generated<string>;
lastLoginAt: Timestamp | null;
lastLoginIp: string | null;
locale: string | null;
name: string | null;
password: string;
role: string | null;
settings: Json | null;
timezone: string | null;
updatedAt: Generated<Timestamp>;
workspaceId: string | null;
}
export interface WorkspaceInvitations {
createdAt: Generated<Timestamp>;
email: string;
id: Generated<string>;
invitedById: string;
role: string;
status: string | null;
updatedAt: Generated<Timestamp>;
workspaceId: string;
}
export interface Workspaces {
createdAt: Generated<Timestamp>;
creatorId: string | null;
customDomain: string | null;
defaultRole: Generated<string>;
defaultSpaceId: string | null;
deletedAt: Timestamp | null;
description: string | null;
enableInvite: boolean;
hostname: string | null;
id: Generated<string>;
inviteCode: string | null;
logo: string | null;
name: string | null;
settings: Json | null;
updatedAt: Generated<Timestamp>;
}
export interface DB {
attachments: Attachments;
comments: Comments;
group_users: GroupUsers;
groups: Groups;
page_history: PageHistory;
page_ordering: PageOrdering;
pages: Pages;
space_members: SpaceMembers;
spaces: Spaces;
users: Users;
workspace_invitations: WorkspaceInvitations;
workspaces: Workspaces;
}

1985
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff