diff --git a/package.json b/package.json index 3a4adf2..02c4be9 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@types/file-saver": "^2.0.7", "@types/ms": "^0.7.34", "@types/node-schedule": "^2.1.7", + "@types/pg": "^8.11.10", "autoprefixer": "^10.4.20", "axios": "^1.7.9", "dexie": "^4.0.10", @@ -56,8 +57,10 @@ "argon2": "^0.41.1", "better-sqlite3": "^11.7.2", "drizzle-orm": "^0.33.0", + "kysely": "^0.27.5", "ms": "^2.1.3", "node-schedule": "^2.1.1", + "pg": "^8.13.1", "uuid": "^11.0.4", "zod": "^3.24.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ae948dc..3dd6588 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,13 +19,19 @@ importers: version: 11.7.2 drizzle-orm: specifier: ^0.33.0 - version: 0.33.0(@types/better-sqlite3@7.6.12)(better-sqlite3@11.7.2) + version: 0.33.0(@types/better-sqlite3@7.6.12)(@types/pg@8.11.10)(better-sqlite3@11.7.2)(kysely@0.27.5)(pg@8.13.1) + kysely: + specifier: ^0.27.5 + version: 0.27.5 ms: specifier: ^2.1.3 version: 2.1.3 node-schedule: specifier: ^2.1.1 version: 2.1.1 + pg: + specifier: ^8.13.1 + version: 8.13.1 uuid: specifier: ^11.0.4 version: 11.0.4 @@ -60,6 +66,9 @@ importers: '@types/node-schedule': specifier: ^2.1.7 version: 2.1.7 + '@types/pg': + specifier: ^8.11.10 + version: 8.11.10 autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.49) @@ -872,6 +881,9 @@ packages: '@types/node@22.10.5': resolution: {integrity: sha512-F8Q+SeGimwOo86fiovQh8qiXfFEh2/ocYv7tU5pJ3EXMSSxk1Joj5wefpFK2fHTf/N6HKGSxIDBT9f3gCxXPkQ==} + '@types/pg@8.11.10': + resolution: {integrity: sha512-LczQUW4dbOQzsH2RQ5qoeJ6qJPdrcM/DcMLoqWQkMLMsq83J5lAX3LXjdkWdpscFy67JSOWDnh7Ny/sPFykmkg==} + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} @@ -1602,6 +1614,10 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + kysely@0.27.5: + resolution: {integrity: sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==} + engines: {node: '>=14.0.0'} + levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -1749,6 +1765,9 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} + obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -1796,6 +1815,48 @@ packages: pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + + pg-connection-string@2.7.0: + resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + + pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + + pg-pool@3.7.0: + resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} + peerDependencies: + pg: '>=8.0' + + pg-protocol@1.7.0: + resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} + + pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + + pg-types@4.0.2: + resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + engines: {node: '>=10'} + + pg@8.13.1: + resolution: {integrity: sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + + pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1883,6 +1944,41 @@ packages: resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==} engines: {node: ^10 || ^12 || >=14} + postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + + postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + + postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + + postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + + postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + + postgres-date@2.1.0: + resolution: {integrity: sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==} + engines: {node: '>=12'} + + postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + + postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + + postgres-range@1.1.4: + resolution: {integrity: sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==} + prebuild-install@7.1.2: resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} @@ -2065,6 +2161,10 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -2298,6 +2398,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} @@ -2843,6 +2947,12 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/pg@8.11.10': + dependencies: + '@types/node': 22.10.5 + pg-protocol: 1.7.0 + pg-types: 4.0.2 + '@types/resolve@1.20.2': {} '@typescript-eslint/eslint-plugin@8.19.1(@typescript-eslint/parser@8.19.1(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.17.0(jiti@1.21.7))(typescript@5.7.3)': @@ -3138,10 +3248,13 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.33.0(@types/better-sqlite3@7.6.12)(better-sqlite3@11.7.2): + drizzle-orm@0.33.0(@types/better-sqlite3@7.6.12)(@types/pg@8.11.10)(better-sqlite3@11.7.2)(kysely@0.27.5)(pg@8.13.1): optionalDependencies: '@types/better-sqlite3': 7.6.12 + '@types/pg': 8.11.10 better-sqlite3: 11.7.2 + kysely: 0.27.5 + pg: 8.13.1 eastasianwidth@0.2.0: {} @@ -3554,6 +3667,8 @@ snapshots: kolorist@1.8.0: {} + kysely@0.27.5: {} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -3668,6 +3783,8 @@ snapshots: object-hash@3.0.0: {} + obuf@1.1.2: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -3714,6 +3831,53 @@ snapshots: pathe@1.1.2: {} + pg-cloudflare@1.1.1: + optional: true + + pg-connection-string@2.7.0: {} + + pg-int8@1.0.1: {} + + pg-numeric@1.0.2: {} + + pg-pool@3.7.0(pg@8.13.1): + dependencies: + pg: 8.13.1 + + pg-protocol@1.7.0: {} + + pg-types@2.2.0: + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + + pg-types@4.0.2: + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.1.0 + postgres-interval: 3.0.0 + postgres-range: 1.1.4 + + pg@8.13.1: + dependencies: + pg-connection-string: 2.7.0 + pg-pool: 3.7.0(pg@8.13.1) + pg-protocol: 1.7.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + + pgpass@1.0.5: + dependencies: + split2: 4.2.0 + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -3782,6 +3946,28 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postgres-array@2.0.0: {} + + postgres-array@3.0.2: {} + + postgres-bytea@1.0.0: {} + + postgres-bytea@3.0.0: + dependencies: + obuf: 1.1.2 + + postgres-date@1.0.7: {} + + postgres-date@2.1.0: {} + + postgres-interval@1.2.0: + dependencies: + xtend: 4.0.2 + + postgres-interval@3.0.0: {} + + postgres-range@1.1.4: {} + prebuild-install@7.1.2: dependencies: detect-libc: 2.0.3 @@ -3930,6 +4116,8 @@ snapshots: source-map@0.6.1: {} + split2@4.2.0: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -4174,6 +4362,8 @@ snapshots: wrappy@1.0.2: {} + xtend@4.0.2: {} + yaml@1.10.2: {} yaml@2.7.0: {} diff --git a/src/lib/server/db/kysely.ts b/src/lib/server/db/kysely.ts new file mode 100644 index 0000000..908e090 --- /dev/null +++ b/src/lib/server/db/kysely.ts @@ -0,0 +1,15 @@ +import { Kysely, PostgresDialect } from "kysely"; +import { Pool } from "pg"; +import type { Database } from "./schema"; + +const dialect = new PostgresDialect({ + pool: new Pool({ + // TODO + }), +}); + +const db = new Kysely({ dialect }); + +// TODO: Migration + +export default db; diff --git a/src/lib/server/db/schema/client.ts b/src/lib/server/db/schema/client.ts index 1e9eb85..08d16ed 100644 --- a/src/lib/server/db/schema/client.ts +++ b/src/lib/server/db/schema/client.ts @@ -6,6 +6,7 @@ import { foreignKey, unique, } from "drizzle-orm/sqlite-core"; +import type { ColumnType, Generated } from "kysely"; import { user } from "./user"; export const client = sqliteTable( @@ -59,3 +60,32 @@ export const userClientChallenge = sqliteTable( }), }), ); + +interface ClientTable { + id: Generated; + encryption_public_key: string; // Base64 + signature_public_key: string; // Base64 +} + +interface UserClientTable { + user_id: number; + client_id: number; + state: "challenging" | "pending" | "active"; +} + +interface UserClientChallengeTable { + id: Generated; + user_id: number; + client_id: number; + answer: string; // Base64 + allowed_ip: string; + expires_at: ColumnType; +} + +declare module "./index" { + interface Database { + client: ClientTable; + user_client: UserClientTable; + user_client_challenge: UserClientChallengeTable; + } +} diff --git a/src/lib/server/db/schema/file.ts b/src/lib/server/db/schema/file.ts index ffe303b..feda927 100644 --- a/src/lib/server/db/schema/file.ts +++ b/src/lib/server/db/schema/file.ts @@ -1,4 +1,5 @@ import { sqliteTable, text, integer, foreignKey } from "drizzle-orm/sqlite-core"; +import type { ColumnType, Generated, JSONColumnType } from "kysely"; import { hsk } from "./hsk"; import { mek } from "./mek"; import { user } from "./user"; @@ -86,3 +87,61 @@ export const fileLog = sqliteTable("file_log", { action: text("action", { enum: ["create", "rename"] }).notNull(), newName: ciphertext("new_name"), }); + +type Ciphertext = JSONColumnType<{ + ciphertext: string; // Base64 + iv: string; // Base64 +}>; + +interface DirectoryTable { + id: Generated; + parent_id: number | null; + user_id: number; + master_encryption_key_version: number; + encrypted_data_encryption_key: string; // Base64 + data_encryption_key_version: Date; + encrypted_name: Ciphertext; +} + +interface DirectoryLogTable { + id: Generated; + directory_id: number; + timestamp: ColumnType; + action: "create" | "rename"; + new_name: Ciphertext | null; +} + +interface FileTable { + id: Generated; + parent_id: number | null; + user_id: number; + path: string; + master_encryption_key_version: number; + encrypted_data_encryption_key: string; // Base64 + data_encryption_key_version: Date; + hmac_secret_key_version: number | null; + content_hmac: string | null; // Base64 + content_type: string; + encrypted_content_iv: string; // Base64 + encrypted_content_hash: string; // Base64 + encrypted_name: Ciphertext; + encrypted_created_at: Ciphertext | null; + encrypted_last_modified_at: Ciphertext; +} + +interface FileLogTable { + id: Generated; + file_id: number; + timestamp: ColumnType; + action: "create" | "rename"; + new_name: Ciphertext | null; +} + +declare module "./index" { + interface Database { + directory: DirectoryTable; + directory_log: DirectoryLogTable; + file: FileTable; + file_log: FileLogTable; + } +} diff --git a/src/lib/server/db/schema/hsk.ts b/src/lib/server/db/schema/hsk.ts index 51f25cc..28b7a89 100644 --- a/src/lib/server/db/schema/hsk.ts +++ b/src/lib/server/db/schema/hsk.ts @@ -1,4 +1,5 @@ import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core"; +import type { ColumnType, Generated } from "kysely"; import { client } from "./client"; import { mek } from "./mek"; import { user } from "./user"; @@ -42,3 +43,27 @@ export const hskLog = sqliteTable( }), }), ); + +interface HskTable { + user_id: number; + version: number; + state: "active"; + master_encryption_key_version: number; + encrypted_key: string; // Base64 +} + +interface HskLogTable { + id: Generated; + user_id: number; + hmac_secret_key_version: number; + timestamp: ColumnType; + action: "create"; + action_by: number | null; +} + +declare module "./index" { + interface Database { + hmac_secret_key: HskTable; + hmac_secret_key_log: HskLogTable; + } +} diff --git a/src/lib/server/db/schema/index.ts b/src/lib/server/db/schema/index.ts index 40cb9be..4292231 100644 --- a/src/lib/server/db/schema/index.ts +++ b/src/lib/server/db/schema/index.ts @@ -4,3 +4,5 @@ export * from "./hsk"; export * from "./mek"; export * from "./session"; export * from "./user"; + +export interface Database {} diff --git a/src/lib/server/db/schema/mek.ts b/src/lib/server/db/schema/mek.ts index e496d9e..e0ac10d 100644 --- a/src/lib/server/db/schema/mek.ts +++ b/src/lib/server/db/schema/mek.ts @@ -1,4 +1,5 @@ import { sqliteTable, text, integer, primaryKey, foreignKey } from "drizzle-orm/sqlite-core"; +import type { ColumnType, Generated } from "kysely"; import { client } from "./client"; import { user } from "./user"; @@ -58,3 +59,34 @@ export const clientMek = sqliteTable( }), }), ); + +interface MekTable { + user_id: number; + version: number; + state: "active" | "retired" | "dead"; +} + +interface MekLogTable { + id: Generated; + user_id: number; + master_encryption_key_version: number; + timestamp: ColumnType; + action: "create"; + action_by: number | null; +} + +interface ClientMekTable { + user_id: number; + client_id: number; + version: number; + encrypted_key: string; // Base64 + encrypted_key_signature: string; // Base64 +} + +declare module "./index" { + interface Database { + master_encryption_key: MekTable; + master_encryption_key_log: MekLogTable; + client_master_encryption_key: ClientMekTable; + } +} diff --git a/src/lib/server/db/schema/session.ts b/src/lib/server/db/schema/session.ts index 5f2129d..d74f099 100644 --- a/src/lib/server/db/schema/session.ts +++ b/src/lib/server/db/schema/session.ts @@ -1,4 +1,5 @@ import { sqliteTable, text, integer, unique } from "drizzle-orm/sqlite-core"; +import type { ColumnType, Generated } from "kysely"; import { client } from "./client"; import { user } from "./user"; @@ -33,3 +34,29 @@ export const sessionUpgradeChallenge = sqliteTable("session_upgrade_challenge", allowedIp: text("allowed_ip").notNull(), expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), }); + +interface SessionTable { + id: string; + user_id: number; + client_id: number | null; + created_at: ColumnType; + last_used_at: Date; + last_used_by_ip: string | null; + last_used_by_agent: string | null; +} + +interface SessionUpgradeChallengeTable { + id: Generated; + session_id: string; + client_id: number; + answer: string; // Base64 + allowed_ip: string; + expires_at: ColumnType; +} + +declare module "./index" { + interface Database { + session: SessionTable; + session_upgrade_challenge: SessionUpgradeChallengeTable; + } +} diff --git a/src/lib/server/db/schema/user.ts b/src/lib/server/db/schema/user.ts index c98fa01..7a34988 100644 --- a/src/lib/server/db/schema/user.ts +++ b/src/lib/server/db/schema/user.ts @@ -1,4 +1,5 @@ import { sqliteTable, text, integer } from "drizzle-orm/sqlite-core"; +import type { Generated } from "kysely"; export const user = sqliteTable("user", { id: integer("id").primaryKey({ autoIncrement: true }), @@ -6,3 +7,16 @@ export const user = sqliteTable("user", { password: text("password").notNull(), nickname: text("nickname").notNull(), }); + +interface UserTable { + id: Generated; + email: string; + nickname: string; + password: string; +} + +declare module "./index" { + interface Database { + user: UserTable; + } +}