From 5cb48b2f3be476c1e2511d2398d38727da68786d Mon Sep 17 00:00:00 2001 From: Jesse Wierzbinski Date: Wed, 17 Apr 2024 22:42:12 -1000 Subject: [PATCH] feat(api): :sparkles: Make Lysand a full OAuth2/OpenID Connect provider as well as still Mastodon compatible --- bun.lockb | Bin 150588 -> 150924 bytes config/config.example.toml | 4 + database/entities/Federation.ts | 2 +- database/entities/User.ts | 23 +- drizzle/0016_keen_mindworm.sql | 3 + drizzle/0017_dusty_black_knight.sql | 2 + drizzle/0018_rapid_hairball.sql | 2 + drizzle/0019_mushy_lorna_dane.sql | 1 + drizzle/meta/0016_snapshot.json | 1781 +++++++++++++++ drizzle/meta/0017_snapshot.json | 1983 ++++++++++++++++ drizzle/meta/0018_snapshot.json | 1787 +++++++++++++++ drizzle/meta/0019_snapshot.json | 1991 +++++++++++++++++ drizzle/meta/_journal.json | 28 + drizzle/schema.ts | 8 +- index.ts | 67 + package.json | 203 +- packages/config-manager/config.type.ts | 3 + packages/request-parser/index.ts | 23 +- routes.ts | 4 +- server.ts | 16 +- server/api/api/auth/login/index.ts | 206 +- server/api/api/v1/apps/index.ts | 4 +- .../api/v1/apps/verify_credentials/index.ts | 2 +- server/api/oauth/authorize/index.ts | 289 +++ server/api/oauth/token/index.ts | 148 +- server/api/users/[uuid]/inbox/index.ts | 2 +- server/api/well-known/jwks/index.ts | 42 + .../well-known/openid-configuration/index.ts | 32 + tests/oauth.test.ts | 89 +- 29 files changed, 8466 insertions(+), 279 deletions(-) create mode 100644 drizzle/0016_keen_mindworm.sql create mode 100644 drizzle/0017_dusty_black_knight.sql create mode 100644 drizzle/0018_rapid_hairball.sql create mode 100644 drizzle/0019_mushy_lorna_dane.sql create mode 100644 drizzle/meta/0016_snapshot.json create mode 100644 drizzle/meta/0017_snapshot.json create mode 100644 drizzle/meta/0018_snapshot.json create mode 100644 drizzle/meta/0019_snapshot.json create mode 100644 server/api/oauth/authorize/index.ts create mode 100644 server/api/well-known/jwks/index.ts create mode 100644 server/api/well-known/openid-configuration/index.ts diff --git a/bun.lockb b/bun.lockb index 1055215e37a646cbf53dd1e2a797e69ea7ecb965..3ceca4d9f3dbe594e25b9d6533a7bdde2440573b 100755 GIT binary patch delta 16006 zcmeHO30PIt+TQEvfwNUm6oDfkf;lJ3ctnsRIUpQ|9FM4|s3<5nq1RcU)I_DURV<+> zwYq9%Won31nM2NsmI{iBmP1adX<`ol`|Uxv{dVu|`Tzeu&;LB_hj+i<_pNWOy}mW= zefDN;T`DhnM_%mTY)38Y^#wEE7`L@w`se*$8Qwb7En-Z0-r)5mTL(@nNpfxZsH;Ju zRq{9}hV(MB<-0xFn2$tpR)ofazKz5{FG5g*>U05y$j!wN9}}uZ8pwa9wZYOz-}eL82RR_yOQz|@4r@i@pmc$UdW&abSS4YJNY9btWwbaC^lN7x%bZYz5 z#ME)g(-RYq$El_415;0K15ptYTpH>z8RG=ZWt9*%mb>qr@t{!ZOgAw zQ|jl$v|*EzQ$|RK2di;x0oQ?jCD;}Gwr0=N^s!p{AkE%Ivj>9R;9pt_N6A5T(C&xTI@ z<2-r&{u(((Q}WI|5g+te9k_mI?X7{v)<`k(GCzxPUXUcc%uAbx%eNrmFT;bEg<9mB z!IIQf^@7$mM3UN3X-e7@Xq4v26FP_MWrbRdcVTbCOC!SN7NL^V86H&?{|TB_g_qT{ zupK-u+A5nZlGGj^2ACV4R|9hH*G8e8P=pi#LJhh}gM;{#$M<(I-FDH29hz|SIQKnsNCpj_+=Xvl+# zQ1+iRPl+e^SmdT{B?;|7fi|?TLA*HD%HHB;o7K1%DM>uOQMlY1b)%{~^0HtHo5PE3 zR{2ZVUR27Y;4VOmP(A8dB~&?pU&_*>-V(6IPITaLQlhZX}3P5OdGwjyBFGa;aj2?{lU(%P(mn|oO0fw(EW ztk_VLaSgQgyfidizD5%4Olgo75UH<~3mT_D>�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 delta 16011 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 diff --git a/config/config.example.toml b/config/config.example.toml index 86c4646e..eb26f53a 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -48,6 +48,10 @@ rules = [ "Don't post illegal content", ] +[oidc] +# Run Lysand with this value missing to generate a new key +jwt_key = "" + # Delete this section if you don't want to use custom OAuth providers # This is an example configuration # The provider MUST support OpenID Connect with .well-known discovery diff --git a/database/entities/Federation.ts b/database/entities/Federation.ts index 8f18f672..5898bfdf 100644 --- a/database/entities/Federation.ts +++ b/database/entities/Federation.ts @@ -19,7 +19,7 @@ export const objectToInboxRequest = async ( const privateKey = await crypto.subtle.importKey( "pkcs8", - Uint8Array.from(atob(author.privateKey ?? ""), (c) => c.charCodeAt(0)), + Buffer.from(author.privateKey ?? "", "base64"), "Ed25519", false, ["sign"], diff --git a/database/entities/User.ts b/database/entities/User.ts index dd7258f0..ba21007b 100644 --- a/database/entities/User.ts +++ b/database/entities/User.ts @@ -729,22 +729,13 @@ export const generateUserKeys = async () => { "verify", ]); - const privateKey = btoa( - String.fromCharCode.apply(null, [ - ...new Uint8Array( - // jesus help me what do these letters mean - await crypto.subtle.exportKey("pkcs8", keys.privateKey), - ), - ]), - ); - const publicKey = btoa( - String.fromCharCode( - ...new Uint8Array( - // why is exporting a key so hard - await crypto.subtle.exportKey("spki", keys.publicKey), - ), - ), - ); + const privateKey = Buffer.from( + await crypto.subtle.exportKey("pkcs8", keys.privateKey), + ).toString("base64"); + + const publicKey = Buffer.from( + await crypto.subtle.exportKey("spki", keys.publicKey), + ).toString("base64"); // Add header, footer and newlines later on // These keys are base64 encrypted diff --git a/drizzle/0016_keen_mindworm.sql b/drizzle/0016_keen_mindworm.sql new file mode 100644 index 00000000..4ee3881e --- /dev/null +++ b/drizzle/0016_keen_mindworm.sql @@ -0,0 +1,3 @@ +ALTER TABLE "Applications" RENAME COLUMN "redirect_uris" TO "redirect_uri";--> statement-breakpoint +ALTER TABLE "Tokens" ADD COLUMN "client_id" text NOT NULL DEFAULT '';--> statement-breakpoint +ALTER TABLE "Tokens" ADD COLUMN "redirect_uri" text NOT NULL DEFAULT ''; \ No newline at end of file diff --git a/drizzle/0017_dusty_black_knight.sql b/drizzle/0017_dusty_black_knight.sql new file mode 100644 index 00000000..ea6b7e69 --- /dev/null +++ b/drizzle/0017_dusty_black_knight.sql @@ -0,0 +1,2 @@ +ALTER TABLE "Tokens" ALTER COLUMN "code" DROP NOT NULL;--> statement-breakpoint +ALTER TABLE "Tokens" ADD COLUMN "expires_at" timestamp(3); \ No newline at end of file diff --git a/drizzle/0018_rapid_hairball.sql b/drizzle/0018_rapid_hairball.sql new file mode 100644 index 00000000..942c7af8 --- /dev/null +++ b/drizzle/0018_rapid_hairball.sql @@ -0,0 +1,2 @@ +ALTER TABLE "Tokens" ALTER COLUMN "client_id" SET DEFAULT ''; +ALTER TABLE "Tokens" ALTER COLUMN "redirect_uri" SET DEFAULT ''; \ No newline at end of file diff --git a/drizzle/0019_mushy_lorna_dane.sql b/drizzle/0019_mushy_lorna_dane.sql new file mode 100644 index 00000000..798ab5e0 --- /dev/null +++ b/drizzle/0019_mushy_lorna_dane.sql @@ -0,0 +1 @@ +ALTER TABLE "Tokens" ADD COLUMN "id_token" text; \ No newline at end of file diff --git a/drizzle/meta/0016_snapshot.json b/drizzle/meta/0016_snapshot.json new file mode 100644 index 00000000..51cd21ce --- /dev/null +++ b/drizzle/meta/0016_snapshot.json @@ -0,0 +1,1781 @@ +{ + "id": "75cb9067-6cf7-4e64-8b2f-e1c140a7d843", + "prevId": "189f44c2-bbf5-4dfd-b02f-ffaedde06921", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": ["client_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": ["emojiId", "noteId"], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": ["emojiId", "userId"], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "FilterKeywords": { + "name": "FilterKeywords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "filterId": { + "name": "filterId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "keyword": { + "name": "keyword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "FilterKeywords_filterId_Filters_id_fk": { + "name": "FilterKeywords_filterId_Filters_id_fk", + "tableFrom": "FilterKeywords", + "tableTo": "Filters", + "columnsFrom": ["filterId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Filters": { + "name": "Filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter_action": { + "name": "filter_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Filters_userId_Users_id_fk": { + "name": "Filters_userId_Users_id_fk", + "tableFrom": "Filters", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": ["likerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": ["likedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": ["remote_id"], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Markers": { + "name": "Markers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notificationId": { + "name": "notificationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Markers_noteId_Notes_id_fk": { + "name": "Markers_noteId_Notes_id_fk", + "tableFrom": "Markers", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_notificationId_Notifications_id_fk": { + "name": "Markers_notificationId_Notifications_id_fk", + "tableFrom": "Markers", + "tableTo": "Notifications", + "columnsFrom": ["notificationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_userId_Users_id_fk": { + "name": "Markers_userId_Users_id_fk", + "tableFrom": "Markers", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": ["noteId", "userId"], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["reblogId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["replyId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["quoteId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["notifiedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["accountId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["ownerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["subjectId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "default": "''", + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": ["userId", "noteId"], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Users_id_fk": { + "name": "UserToPinnedNotes_userId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Notes_id_fk": { + "name": "UserToPinnedNotes_noteId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": ["uri"], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": ["username"], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": ["email"], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/0017_snapshot.json b/drizzle/meta/0017_snapshot.json new file mode 100644 index 00000000..faa1a72a --- /dev/null +++ b/drizzle/meta/0017_snapshot.json @@ -0,0 +1,1983 @@ +{ + "id": "f716d012-9ab2-4fc0-b97b-466a2f159780", + "prevId": "75cb9067-6cf7-4e64-8b2f-e1c140a7d843", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": [ + "client_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": [ + "emojiId", + "noteId" + ], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": [ + "emojiId", + "userId" + ], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "FilterKeywords": { + "name": "FilterKeywords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "filterId": { + "name": "filterId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "keyword": { + "name": "keyword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "FilterKeywords_filterId_Filters_id_fk": { + "name": "FilterKeywords_filterId_Filters_id_fk", + "tableFrom": "FilterKeywords", + "tableTo": "Filters", + "columnsFrom": [ + "filterId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Filters": { + "name": "Filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter_action": { + "name": "filter_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Filters_userId_Users_id_fk": { + "name": "Filters_userId_Users_id_fk", + "tableFrom": "Filters", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": [ + "likerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": [ + "likedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": [ + "remote_id" + ], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Markers": { + "name": "Markers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notificationId": { + "name": "notificationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Markers_noteId_Notes_id_fk": { + "name": "Markers_noteId_Notes_id_fk", + "tableFrom": "Markers", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_notificationId_Notifications_id_fk": { + "name": "Markers_notificationId_Notifications_id_fk", + "tableFrom": "Markers", + "tableTo": "Notifications", + "columnsFrom": [ + "notificationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_userId_Users_id_fk": { + "name": "Markers_userId_Users_id_fk", + "tableFrom": "Markers", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": [ + "noteId", + "userId" + ], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "reblogId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "replyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "quoteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "notifiedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "accountId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "subjectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": [ + "userId", + "noteId" + ], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Users_id_fk": { + "name": "UserToPinnedNotes_userId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Notes_id_fk": { + "name": "UserToPinnedNotes_noteId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": [ + "username" + ], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/0018_snapshot.json b/drizzle/meta/0018_snapshot.json new file mode 100644 index 00000000..4eb5ae6d --- /dev/null +++ b/drizzle/meta/0018_snapshot.json @@ -0,0 +1,1787 @@ +{ + "id": "c4c9b98a-3e9e-4ce4-8267-2af5a275ffdc", + "prevId": "f716d012-9ab2-4fc0-b97b-466a2f159780", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": ["client_id"], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": ["emojiId", "noteId"], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": ["emojiId", "userId"], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": ["emojiId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "FilterKeywords": { + "name": "FilterKeywords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "filterId": { + "name": "filterId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "keyword": { + "name": "keyword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "FilterKeywords_filterId_Filters_id_fk": { + "name": "FilterKeywords_filterId_Filters_id_fk", + "tableFrom": "FilterKeywords", + "tableTo": "Filters", + "columnsFrom": ["filterId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Filters": { + "name": "Filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter_action": { + "name": "filter_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Filters_userId_Users_id_fk": { + "name": "Filters_userId_Users_id_fk", + "tableFrom": "Filters", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": ["likerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": ["likedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": ["remote_id"], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Markers": { + "name": "Markers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notificationId": { + "name": "notificationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Markers_noteId_Notes_id_fk": { + "name": "Markers_noteId_Notes_id_fk", + "tableFrom": "Markers", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_notificationId_Notifications_id_fk": { + "name": "Markers_notificationId_Notifications_id_fk", + "tableFrom": "Markers", + "tableTo": "Notifications", + "columnsFrom": ["notificationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_userId_Users_id_fk": { + "name": "Markers_userId_Users_id_fk", + "tableFrom": "Markers", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": ["modId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": ["noteId", "userId"], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": ["userId"], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": ["uri"], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": ["authorId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["reblogId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["replyId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": ["quoteId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["notifiedId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": ["accountId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["ownerId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": ["subjectId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": ["applicationId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": ["userId", "noteId"], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": ["noteId"], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Users_id_fk": { + "name": "UserToPinnedNotes_userId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": ["userId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Notes_id_fk": { + "name": "UserToPinnedNotes_noteId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": ["noteId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": ["uri"], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": ["username"], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": ["email"], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": ["instanceId"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/drizzle/meta/0019_snapshot.json b/drizzle/meta/0019_snapshot.json new file mode 100644 index 00000000..94e5f22f --- /dev/null +++ b/drizzle/meta/0019_snapshot.json @@ -0,0 +1,1991 @@ +{ + "id": "c45f1b6f-a872-4e39-9c0b-05b1d6b27384", + "prevId": "c4c9b98a-3e9e-4ce4-8267-2af5a275ffdc", + "version": "5", + "dialect": "pg", + "tables": { + "Applications": { + "name": "Applications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "website": { + "name": "website", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "vapid_key": { + "name": "vapid_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "Applications_client_id_index": { + "name": "Applications_client_id_index", + "columns": [ + "client_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Attachments": { + "name": "Attachments", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "remote_url": { + "name": "remote_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "thumbnail_url": { + "name": "thumbnail_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blurhash": { + "name": "blurhash", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sha256": { + "name": "sha256", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "fps": { + "name": "fps", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "duration": { + "name": "duration", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Attachments_noteId_Notes_id_fk": { + "name": "Attachments_noteId_Notes_id_fk", + "tableFrom": "Attachments", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToNote": { + "name": "EmojiToNote", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToNote_emojiId_noteId_index": { + "name": "EmojiToNote_emojiId_noteId_index", + "columns": [ + "emojiId", + "noteId" + ], + "isUnique": true + }, + "EmojiToNote_noteId_index": { + "name": "EmojiToNote_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToNote_emojiId_Emojis_id_fk": { + "name": "EmojiToNote_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToNote_noteId_Notes_id_fk": { + "name": "EmojiToNote_noteId_Notes_id_fk", + "tableFrom": "EmojiToNote", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "EmojiToUser": { + "name": "EmojiToUser", + "schema": "", + "columns": { + "emojiId": { + "name": "emojiId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "EmojiToUser_emojiId_userId_index": { + "name": "EmojiToUser_emojiId_userId_index", + "columns": [ + "emojiId", + "userId" + ], + "isUnique": true + }, + "EmojiToUser_userId_index": { + "name": "EmojiToUser_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "EmojiToUser_emojiId_Emojis_id_fk": { + "name": "EmojiToUser_emojiId_Emojis_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Emojis", + "columnsFrom": [ + "emojiId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "EmojiToUser_userId_Users_id_fk": { + "name": "EmojiToUser_userId_Users_id_fk", + "tableFrom": "EmojiToUser", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Emojis": { + "name": "Emojis", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "shortcode": { + "name": "shortcode", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "visible_in_picker": { + "name": "visible_in_picker", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Emojis_instanceId_Instances_id_fk": { + "name": "Emojis_instanceId_Instances_id_fk", + "tableFrom": "Emojis", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "FilterKeywords": { + "name": "FilterKeywords", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "filterId": { + "name": "filterId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "keyword": { + "name": "keyword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "whole_word": { + "name": "whole_word", + "type": "boolean", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "FilterKeywords_filterId_Filters_id_fk": { + "name": "FilterKeywords_filterId_Filters_id_fk", + "tableFrom": "FilterKeywords", + "tableTo": "Filters", + "columnsFrom": [ + "filterId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Filters": { + "name": "Filters", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "context": { + "name": "context", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "filter_action": { + "name": "filter_action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Filters_userId_Users_id_fk": { + "name": "Filters_userId_Users_id_fk", + "tableFrom": "Filters", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Flags": { + "name": "Flags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "flag_type": { + "name": "flag_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'other'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Flags_noteId_Notes_id_fk": { + "name": "Flags_noteId_Notes_id_fk", + "tableFrom": "Flags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Flags_userId_Users_id_fk": { + "name": "Flags_userId_Users_id_fk", + "tableFrom": "Flags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Instances": { + "name": "Instances", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "base_url": { + "name": "base_url", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Likes": { + "name": "Likes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "likerId": { + "name": "likerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "likedId": { + "name": "likedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Likes_likerId_Users_id_fk": { + "name": "Likes_likerId_Users_id_fk", + "tableFrom": "Likes", + "tableTo": "Users", + "columnsFrom": [ + "likerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Likes_likedId_Notes_id_fk": { + "name": "Likes_likedId_Notes_id_fk", + "tableFrom": "Likes", + "tableTo": "Notes", + "columnsFrom": [ + "likedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "LysandObject": { + "name": "LysandObject", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "remote_id": { + "name": "remote_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "extra_data": { + "name": "extra_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "extensions": { + "name": "extensions", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "LysandObject_remote_id_index": { + "name": "LysandObject_remote_id_index", + "columns": [ + "remote_id" + ], + "isUnique": true + }, + "LysandObject_uri_index": { + "name": "LysandObject_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "LysandObject_authorId_LysandObject_id_fk": { + "name": "LysandObject_authorId_LysandObject_id_fk", + "tableFrom": "LysandObject", + "tableTo": "LysandObject", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Markers": { + "name": "Markers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "notificationId": { + "name": "notificationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "timeline": { + "name": "timeline", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Markers_noteId_Notes_id_fk": { + "name": "Markers_noteId_Notes_id_fk", + "tableFrom": "Markers", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_notificationId_Notifications_id_fk": { + "name": "Markers_notificationId_Notifications_id_fk", + "tableFrom": "Markers", + "tableTo": "Notifications", + "columnsFrom": [ + "notificationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Markers_userId_Users_id_fk": { + "name": "Markers_userId_Users_id_fk", + "tableFrom": "Markers", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModNotes": { + "name": "ModNotes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModNotes_noteId_Notes_id_fk": { + "name": "ModNotes_noteId_Notes_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_userId_Users_id_fk": { + "name": "ModNotes_userId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModNotes_modId_Users_id_fk": { + "name": "ModNotes_modId_Users_id_fk", + "tableFrom": "ModNotes", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "ModTags": { + "name": "ModTags", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "modId": { + "name": "modId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "ModTags_noteId_Notes_id_fk": { + "name": "ModTags_noteId_Notes_id_fk", + "tableFrom": "ModTags", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_userId_Users_id_fk": { + "name": "ModTags_userId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "ModTags_modId_Users_id_fk": { + "name": "ModTags_modId_Users_id_fk", + "tableFrom": "ModTags", + "tableTo": "Users", + "columnsFrom": [ + "modId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "NoteToMentions": { + "name": "NoteToMentions", + "schema": "", + "columns": { + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "NoteToMentions_noteId_userId_index": { + "name": "NoteToMentions_noteId_userId_index", + "columns": [ + "noteId", + "userId" + ], + "isUnique": true + }, + "NoteToMentions_userId_index": { + "name": "NoteToMentions_userId_index", + "columns": [ + "userId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "NoteToMentions_noteId_Notes_id_fk": { + "name": "NoteToMentions_noteId_Notes_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "NoteToMentions_userId_Users_id_fk": { + "name": "NoteToMentions_userId_Users_id_fk", + "tableFrom": "NoteToMentions", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notes": { + "name": "Notes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "authorId": { + "name": "authorId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "reblogId": { + "name": "reblogId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "content_type": { + "name": "content_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'text/plain'" + }, + "visibility": { + "name": "visibility", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replyId": { + "name": "replyId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "quoteId": { + "name": "quoteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "spoiler_text": { + "name": "spoiler_text", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "content_source": { + "name": "content_source", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": { + "Notes_uri_index": { + "name": "Notes_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Notes_authorId_Users_id_fk": { + "name": "Notes_authorId_Users_id_fk", + "tableFrom": "Notes", + "tableTo": "Users", + "columnsFrom": [ + "authorId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_applicationId_Applications_id_fk": { + "name": "Notes_applicationId_Applications_id_fk", + "tableFrom": "Notes", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_reblogId_Notes_id_fk": { + "name": "Notes_reblogId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "reblogId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notes_replyId_Notes_id_fk": { + "name": "Notes_replyId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "replyId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + }, + "Notes_quoteId_Notes_id_fk": { + "name": "Notes_quoteId_Notes_id_fk", + "tableFrom": "Notes", + "tableTo": "Notes", + "columnsFrom": [ + "quoteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Notifications": { + "name": "Notifications", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "notifiedId": { + "name": "notifiedId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "dismissed": { + "name": "dismissed", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "Notifications_notifiedId_Users_id_fk": { + "name": "Notifications_notifiedId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "notifiedId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_accountId_Users_id_fk": { + "name": "Notifications_accountId_Users_id_fk", + "tableFrom": "Notifications", + "tableTo": "Users", + "columnsFrom": [ + "accountId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Notifications_noteId_Notes_id_fk": { + "name": "Notifications_noteId_Notes_id_fk", + "tableFrom": "Notifications", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdAccounts": { + "name": "OpenIdAccounts", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "server_id": { + "name": "server_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdAccounts_userId_Users_id_fk": { + "name": "OpenIdAccounts_userId_Users_id_fk", + "tableFrom": "OpenIdAccounts", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "OpenIdLoginFlows": { + "name": "OpenIdLoginFlows", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "code_verifier": { + "name": "code_verifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "issuer_id": { + "name": "issuer_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "OpenIdLoginFlows_applicationId_Applications_id_fk": { + "name": "OpenIdLoginFlows_applicationId_Applications_id_fk", + "tableFrom": "OpenIdLoginFlows", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Relationships": { + "name": "Relationships", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "ownerId": { + "name": "ownerId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "subjectId": { + "name": "subjectId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "following": { + "name": "following", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "showing_reblogs": { + "name": "showing_reblogs", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "notifying": { + "name": "notifying", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "followed_by": { + "name": "followed_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocking": { + "name": "blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "blocked_by": { + "name": "blocked_by", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting": { + "name": "muting", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "muting_notifications": { + "name": "muting_notifications", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "requested": { + "name": "requested", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "domain_blocking": { + "name": "domain_blocking", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "endorsed": { + "name": "endorsed", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "languages": { + "name": "languages", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "Relationships_ownerId_Users_id_fk": { + "name": "Relationships_ownerId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "ownerId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Relationships_subjectId_Users_id_fk": { + "name": "Relationships_subjectId_Users_id_fk", + "tableFrom": "Relationships", + "tableTo": "Users", + "columnsFrom": [ + "subjectId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Tokens": { + "name": "Tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "Tokens_userId_Users_id_fk": { + "name": "Tokens_userId_Users_id_fk", + "tableFrom": "Tokens", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "Tokens_applicationId_Applications_id_fk": { + "name": "Tokens_applicationId_Applications_id_fk", + "tableFrom": "Tokens", + "tableTo": "Applications", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "UserToPinnedNotes": { + "name": "UserToPinnedNotes", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "noteId": { + "name": "noteId", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "UserToPinnedNotes_userId_noteId_index": { + "name": "UserToPinnedNotes_userId_noteId_index", + "columns": [ + "userId", + "noteId" + ], + "isUnique": true + }, + "UserToPinnedNotes_noteId_index": { + "name": "UserToPinnedNotes_noteId_index", + "columns": [ + "noteId" + ], + "isUnique": false + } + }, + "foreignKeys": { + "UserToPinnedNotes_userId_Users_id_fk": { + "name": "UserToPinnedNotes_userId_Users_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Users", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "UserToPinnedNotes_noteId_Notes_id_fk": { + "name": "UserToPinnedNotes_noteId_Notes_id_fk", + "tableFrom": "UserToPinnedNotes", + "tableTo": "Notes", + "columnsFrom": [ + "noteId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "Users": { + "name": "Users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "uuid_generate_v7()" + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "note": { + "name": "note", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "is_admin": { + "name": "is_admin", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "endpoints": { + "name": "endpoints", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "source": { + "name": "source", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "avatar": { + "name": "avatar", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "header": { + "name": "header", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "is_bot": { + "name": "is_bot", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_locked": { + "name": "is_locked", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "is_discoverable": { + "name": "is_discoverable", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "sanctions": { + "name": "sanctions", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "instanceId": { + "name": "instanceId", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "disable_automoderation": { + "name": "disable_automoderation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "Users_uri_index": { + "name": "Users_uri_index", + "columns": [ + "uri" + ], + "isUnique": true + }, + "Users_username_index": { + "name": "Users_username_index", + "columns": [ + "username" + ], + "isUnique": true + }, + "Users_email_index": { + "name": "Users_email_index", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": { + "Users_instanceId_Instances_id_fk": { + "name": "Users_instanceId_Instances_id_fk", + "tableFrom": "Users", + "tableTo": "Instances", + "columnsFrom": [ + "instanceId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": {}, + "schemas": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index bc9297be..034e84a4 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -113,6 +113,34 @@ "when": 1713399438164, "tag": "0015_easy_mojo", "breakpoints": true + }, + { + "idx": 16, + "version": "5", + "when": 1713413369623, + "tag": "0016_keen_mindworm", + "breakpoints": true + }, + { + "idx": 17, + "version": "5", + "when": 1713417089150, + "tag": "0017_dusty_black_knight", + "breakpoints": true + }, + { + "idx": 18, + "version": "5", + "when": 1713418575392, + "tag": "0018_rapid_hairball", + "breakpoints": true + }, + { + "idx": 19, + "version": "5", + "when": 1713421706451, + "tag": "0019_mushy_lorna_dane", + "breakpoints": true } ] } \ No newline at end of file diff --git a/drizzle/schema.ts b/drizzle/schema.ts index a3ed3e89..7a707a4c 100644 --- a/drizzle/schema.ts +++ b/drizzle/schema.ts @@ -187,7 +187,7 @@ export const Applications = pgTable( clientId: text("client_id").notNull(), secret: text("secret").notNull(), scopes: text("scopes").notNull(), - redirectUris: text("redirect_uris").notNull(), + redirectUri: text("redirect_uri").notNull(), }, (table) => { return { @@ -206,10 +206,14 @@ export const Tokens = pgTable("Tokens", { tokenType: text("token_type").notNull(), scope: text("scope").notNull(), accessToken: text("access_token").notNull(), - code: text("code").notNull(), + code: text("code"), + expiresAt: timestamp("expires_at", { precision: 3, mode: "string" }), createdAt: timestamp("created_at", { precision: 3, mode: "string" }) .defaultNow() .notNull(), + clientId: text("client_id").notNull().default(""), + redirectUri: text("redirect_uri").notNull().default(""), + idToken: text("id_token"), userId: uuid("userId") .references(() => Users.id, { onDelete: "cascade", diff --git a/index.ts b/index.ts index 5467de08..37cd6396 100644 --- a/index.ts +++ b/index.ts @@ -43,6 +43,73 @@ try { process.exit(1); } +if (isEntry) { + // Check if JWT private key is set in config + if (!config.oidc.jwt_key) { + await dualServerLogger.log( + LogLevel.CRITICAL, + "Server", + "The JWT private key is not set in the config", + ); + await dualServerLogger.log( + LogLevel.CRITICAL, + "Server", + "Below is a generated key for you to copy in the config at oidc.jwt_private_key", + ); + // Generate a key for them + const keys = await crypto.subtle.generateKey("Ed25519", true, [ + "sign", + "verify", + ]); + + const privateKey = Buffer.from( + await crypto.subtle.exportKey("pkcs8", keys.privateKey), + ).toString("base64"); + + const publicKey = Buffer.from( + await crypto.subtle.exportKey("spki", keys.publicKey), + ).toString("base64"); + + await dualServerLogger.log( + LogLevel.CRITICAL, + "Server", + `${privateKey};${publicKey}`, + ); + process.exit(1); + } + + // Try and import the key + const privateKey = await crypto.subtle + .importKey( + "pkcs8", + Buffer.from(config.oidc.jwt_key.split(";")[0], "base64"), + "Ed25519", + false, + ["sign"], + ) + .catch((e) => e as Error); + + // Try and import the key + const publicKey = await crypto.subtle + .importKey( + "spki", + Buffer.from(config.oidc.jwt_key.split(";")[1], "base64"), + "Ed25519", + false, + ["verify"], + ) + .catch((e) => e as Error); + + if (privateKey instanceof Error || publicKey instanceof Error) { + await dualServerLogger.log( + LogLevel.CRITICAL, + "Server", + "The JWT key could not be imported! You may generate a new one by removing the old one from the config and restarting the server (this will invalidate all current JWTs).", + ); + process.exit(1); + } +} + const server = createServer(config, dualServerLogger, true); await dualServerLogger.log( diff --git a/package.json b/package.json index 9f81bc42..b9a2a039 100644 --- a/package.json +++ b/package.json @@ -1,104 +1,105 @@ { - "name": "lysand", - "module": "index.ts", - "type": "module", - "version": "0.4.0", - "description": "A project to build a federated social network", - "author": { - "email": "contact@cpluspatch.com", - "name": "CPlusPatch", - "url": "https://cpluspatch.com" - }, - "bugs": { - "url": "https://github.com/lysand-org/lysand/issues" - }, - "icon": "https://github.com/lysand-org/lysand", - "license": "AGPL-3.0", - "keywords": ["federated", "activitypub", "bun"], - "workspaces": ["packages/*"], - "maintainers": [ - { - "email": "contact@cpluspatch.com", - "name": "CPlusPatch", - "url": "https://cpluspatch.com" - } - ], - "repository": { - "type": "git", - "url": "git+https://github.com/lysand-org/lysand.git" - }, - "private": true, - "scripts": { - "dev": "bun run --watch index.ts", - "vite:dev": "bunx --bun vite pages", - "vite:build": "bunx --bun vite build pages", - "fe:dev": "bun --bun nuxt dev packages/frontend", - "fe:build": "bun --bun nuxt build packages/frontend", - "fe:analyze": "bun --bun nuxt analyze packages/frontend", - "start": "NITRO_PORT=5173 bun run dist/frontend/server/index.mjs & NODE_ENV=production bun run dist/index.js --prod", - "lint": "bunx --bun eslint --config .eslintrc.cjs --ext .ts .", - "prod-build": "bun run build.ts", - "benchmark:timeline": "bun run benchmarks/timelines.ts", - "cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs --exclude-ext sql,log", - "cli": "bun run cli.ts" - }, - "trustedDependencies": [ - "@biomejs/biome", - "@fortawesome/fontawesome-common-types", - "@fortawesome/free-regular-svg-icons", - "@fortawesome/free-solid-svg-icons", - "es5-ext", - "esbuild", - "json-editor-vue", - "msgpackr-extract", - "nuxt-app", - "sharp", - "vue-demi" - ], - "devDependencies": { - "@biomejs/biome": "^1.7.0", - "@types/cli-table": "^0.3.4", - "@types/html-to-text": "^9.0.4", - "@types/ioredis": "^5.0.0", - "@types/jsonld": "^1.5.13", - "@types/mime-types": "^2.1.4", - "@types/pg": "^8.11.5", - "bun-types": "latest", - "drizzle-kit": "^0.20.14", - "typescript": "latest" - }, - "peerDependencies": { - "typescript": "^5.3.2" - }, - "dependencies": { - "@json2csv/plainjs": "^7.0.6", - "blurhash": "^2.0.5", - "bullmq": "^5.7.1", - "chalk": "^5.3.0", - "cli-parser": "workspace:*", - "cli-table": "^0.3.11", - "config-manager": "workspace:*", - "drizzle-orm": "^0.30.7", - "extract-zip": "^2.0.1", - "html-to-text": "^9.0.5", - "ioredis": "^5.3.2", - "ip-matching": "^2.1.2", - "iso-639-1": "^3.1.0", - "isomorphic-dompurify": "latest", - "linkify-html": "^4.1.3", - "linkify-string": "^4.1.3", - "linkifyjs": "^4.1.3", - "log-manager": "workspace:*", - "magic-regexp": "^0.8.0", - "marked": "^12.0.1", - "media-manager": "workspace:*", - "megalodon": "^10.0.0", - "meilisearch": "^0.38.0", - "mime-types": "^2.1.35", - "oauth4webapi": "^2.4.0", - "pg": "^8.11.5", - "request-parser": "workspace:*", - "sharp": "^0.33.3", - "zod": "^3.22.4" + "name": "lysand", + "module": "index.ts", + "type": "module", + "version": "0.4.0", + "description": "A project to build a federated social network", + "author": { + "email": "contact@cpluspatch.com", + "name": "CPlusPatch", + "url": "https://cpluspatch.com" + }, + "bugs": { + "url": "https://github.com/lysand-org/lysand/issues" + }, + "icon": "https://github.com/lysand-org/lysand", + "license": "AGPL-3.0", + "keywords": ["federated", "activitypub", "bun"], + "workspaces": ["packages/*"], + "maintainers": [ + { + "email": "contact@cpluspatch.com", + "name": "CPlusPatch", + "url": "https://cpluspatch.com" } + ], + "repository": { + "type": "git", + "url": "git+https://github.com/lysand-org/lysand.git" + }, + "private": true, + "scripts": { + "dev": "bun run --watch index.ts", + "vite:dev": "bunx --bun vite pages", + "vite:build": "bunx --bun vite build pages", + "fe:dev": "bun --bun nuxt dev packages/frontend", + "fe:build": "bun --bun nuxt build packages/frontend", + "fe:analyze": "bun --bun nuxt analyze packages/frontend", + "start": "NITRO_PORT=5173 bun run dist/frontend/server/index.mjs & NODE_ENV=production bun run dist/index.js --prod", + "lint": "bunx --bun eslint --config .eslintrc.cjs --ext .ts .", + "prod-build": "bun run build.ts", + "benchmark:timeline": "bun run benchmarks/timelines.ts", + "cloc": "cloc . --exclude-dir node_modules,dist,.output,.nuxt,meta,logs --exclude-ext sql,log", + "cli": "bun run cli.ts" + }, + "trustedDependencies": [ + "@biomejs/biome", + "@fortawesome/fontawesome-common-types", + "@fortawesome/free-regular-svg-icons", + "@fortawesome/free-solid-svg-icons", + "es5-ext", + "esbuild", + "json-editor-vue", + "msgpackr-extract", + "nuxt-app", + "sharp", + "vue-demi" + ], + "devDependencies": { + "@biomejs/biome": "^1.7.0", + "@types/cli-table": "^0.3.4", + "@types/html-to-text": "^9.0.4", + "@types/ioredis": "^5.0.0", + "@types/jsonld": "^1.5.13", + "@types/mime-types": "^2.1.4", + "@types/pg": "^8.11.5", + "bun-types": "latest", + "drizzle-kit": "^0.20.14", + "typescript": "latest" + }, + "peerDependencies": { + "typescript": "^5.3.2" + }, + "dependencies": { + "@json2csv/plainjs": "^7.0.6", + "blurhash": "^2.0.5", + "bullmq": "^5.7.1", + "chalk": "^5.3.0", + "cli-parser": "workspace:*", + "cli-table": "^0.3.11", + "config-manager": "workspace:*", + "drizzle-orm": "^0.30.7", + "extract-zip": "^2.0.1", + "html-to-text": "^9.0.5", + "ioredis": "^5.3.2", + "ip-matching": "^2.1.2", + "iso-639-1": "^3.1.0", + "isomorphic-dompurify": "latest", + "jose": "^5.2.4", + "linkify-html": "^4.1.3", + "linkify-string": "^4.1.3", + "linkifyjs": "^4.1.3", + "log-manager": "workspace:*", + "magic-regexp": "^0.8.0", + "marked": "^12.0.1", + "media-manager": "workspace:*", + "megalodon": "^10.0.0", + "meilisearch": "^0.38.0", + "mime-types": "^2.1.35", + "oauth4webapi": "^2.4.0", + "pg": "^8.11.5", + "request-parser": "workspace:*", + "sharp": "^0.33.3", + "zod": "^3.22.4" + } } diff --git a/packages/config-manager/config.type.ts b/packages/config-manager/config.type.ts index 91a5c799..ef18e041 100644 --- a/packages/config-manager/config.type.ts +++ b/packages/config-manager/config.type.ts @@ -89,6 +89,8 @@ export interface Config { client_secret: string; icon: string; }[]; + + jwt_key: string; }; http: { @@ -447,6 +449,7 @@ export const defaultConfig: Config = { }, oidc: { providers: [], + jwt_key: "", }, http: { base_url: "https://lysand.social", diff --git a/packages/request-parser/index.ts b/packages/request-parser/index.ts index d45eee98..cbae659c 100644 --- a/packages/request-parser/index.ts +++ b/packages/request-parser/index.ts @@ -137,23 +137,14 @@ export class RequestParser { * @throws Error if body is invalid */ private async parseFormUrlencoded(): Promise> { - const formData = await this.request.formData(); - const result: Partial = {}; + const parsed = parse(await this.request.text(), { + parseArrays: true, + interpretNumericEntities: true, + }); - for (const [key, value] of formData.entries()) { - if (key.endsWith("[]")) { - const arrayKey = key.slice(0, -2) as keyof T; - if (!result[arrayKey]) { - result[arrayKey] = [] as T[keyof T]; - } - - (result[arrayKey] as FormDataEntryValue[]).push(value); - } else { - result[key as keyof T] = value as T[keyof T]; - } - } - - return result; + return castBooleanObject( + parsed as PossiblyRecursiveObject, + ) as Partial; } /** diff --git a/routes.ts b/routes.ts index 8387ad91..b3582b96 100644 --- a/routes.ts +++ b/routes.ts @@ -19,8 +19,8 @@ for (const [route, path] of Object.entries(routes)) { export { routes }; -export const matchRoute = (url: string) => { - const route = routeMatcher.match(url); +export const matchRoute = (request: Request) => { + const route = routeMatcher.match(request); return route ?? null; }; diff --git a/server.ts b/server.ts index b55b64ec..4b59c96f 100644 --- a/server.ts +++ b/server.ts @@ -1,5 +1,6 @@ import { dualLogger } from "@loggers"; import { errorResponse, response } from "@response"; +import type { MatchedRoute } from "bun"; import type { Config } from "config-manager"; import { matches } from "ip-matching"; import type { LogManager, MultiLogManager } from "log-manager"; @@ -129,10 +130,19 @@ export const createServer = ( // If route is .well-known, remove dot because the filesystem router can't handle dots for some reason const matchedRoute = matchRoute( - req.url.replace(".well-known", "well-known"), + new Request(req.url.replace(".well-known", "well-known"), { + method: req.method, + }), ); - if (matchedRoute?.filePath && matchedRoute.name !== "/[...404]") { + if ( + matchedRoute?.filePath && + matchedRoute.name !== "/[...404]" && + !( + new URL(req.url).pathname.startsWith("/oauth/authorize") && + req.method === "GET" + ) + ) { return await processRoute(matchedRoute, req, logger); } @@ -164,8 +174,6 @@ export const createServer = ( return null; }); - console.log(proxy); - if (!proxy || proxy.status === 404) { if (config.frontend.glitch.enabled) { return ( diff --git a/server/api/api/auth/login/index.ts b/server/api/api/auth/login/index.ts index 5461d229..42fe761f 100644 --- a/server/api/api/auth/login/index.ts +++ b/server/api/api/auth/login/index.ts @@ -1,10 +1,13 @@ -import { randomBytes } from "node:crypto"; import { apiRoute, applyConfig } from "@api"; import { z } from "zod"; -import { TokenType } from "~database/entities/Token"; import { findFirstUser } from "~database/entities/User"; +import { SignJWT } from "jose"; +import { config } from "~packages/config-manager"; +import { errorResponse, response } from "@response"; +import { stringify } from "qs"; +import { fromZodError } from "zod-validation-error"; +import { RequestParser } from "~packages/request-parser"; import { db } from "~drizzle/db"; -import { Tokens } from "~drizzle/schema"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -20,76 +23,139 @@ export const meta = applyConfig({ export const schema = z.object({ email: z.string().email(), - password: z.string().max(100).min(3), + password: z.string().min(2).max(100), }); +export const querySchema = z.object({ + scope: z.string().optional(), + redirect_uri: z.string().url().optional(), + response_type: z.enum([ + "code", + "token", + "none", + "id_token", + "code id_token", + "code token", + "token id_token", + "code token id_token", + ]), + client_id: z.string(), + state: z.string().optional(), + code_challenge: z.string().optional(), + code_challenge_method: z.enum(["plain", "S256"]).optional(), + prompt: z + .enum(["none", "login", "consent", "select_account"]) + .optional() + .default("none"), + max_age: z + .number() + .int() + .optional() + .default(60 * 60 * 24 * 7), +}); + +const returnError = (query: object, error: string, description: string) => + response(null, 302, { + Location: `/oauth/authorize?${stringify({ + ...query, + error, + error_description: description, + })}`, + }); + /** - * OAuth Code flow + * Login flow */ -export default apiRoute( - async (req, matchedRoute, extraData) => { - const scopes = (matchedRoute.query.scope || "") - .replaceAll("+", " ") - .split(" "); - const redirect_uri = matchedRoute.query.redirect_uri; - const response_type = matchedRoute.query.response_type; - const client_id = matchedRoute.query.client_id; +export default apiRoute(async (req, matchedRoute, extraData) => { + const { email, password } = extraData.parsedRequest; - const { email, password } = extraData.parsedRequest; - - const redirectToLogin = (error: string) => - Response.redirect( - `/oauth/authorize?${new URLSearchParams({ - ...matchedRoute.query, - error: encodeURIComponent(error), - }).toString()}`, - 302, - ); - - if (response_type !== "code") - return redirectToLogin("Invalid response_type"); - - if (!email || !password) - return redirectToLogin("Invalid username or password"); - - const user = await findFirstUser({ - where: (user, { eq }) => eq(user.email, email), - }); - - if ( - !user || - !(await Bun.password.verify(password, user.password || "")) - ) - return redirectToLogin("Invalid username or password"); - - const application = await db.query.Applications.findFirst({ - where: (app, { eq }) => eq(app.clientId, client_id), - }); - - if (!application) return redirectToLogin("Invalid client_id"); - - const code = randomBytes(32).toString("hex"); - - await db.insert(Tokens).values({ - accessToken: randomBytes(64).toString("base64url"), - code: code, - scope: scopes.join(" "), - tokenType: TokenType.BEARER, - applicationId: application.id, - userId: user.id, - }); - - // Redirect to OAuth confirmation screen - return Response.redirect( - `/oauth/redirect?${new URLSearchParams({ - redirect_uri, - code, - client_id, - application: application.name, - website: application.website ?? "", - scope: scopes.join(" "), - }).toString()}`, - 302, + if (!email || !password) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Missing email or password", ); - }, -); + + // Find user + const user = await findFirstUser({ + where: (user, { eq }) => eq(user.email, email), + }); + + if (!user || !(await Bun.password.verify(password, user.password || ""))) + return returnError( + extraData.parsedRequest, + "invalid_request", + "Invalid email or password", + ); + + const parsedQuery = await new RequestParser( + new Request(req.url), + ).toObject(); + + if (!parsedQuery) { + return errorResponse("Invalid query", 400); + } + + const parsingResult = querySchema.safeParse(parsedQuery); + + if (parsingResult && !parsingResult.success) { + // Return a 422 error with the first error message + return errorResponse(fromZodError(parsingResult.error).toString(), 422); + } + + const { client_id } = parsingResult.data; + + // Try and import the key + const privateKey = await crypto.subtle.importKey( + "pkcs8", + Buffer.from(config.oidc.jwt_key.split(";")[0], "base64"), + "Ed25519", + false, + ["sign"], + ); + + // Generate JWT + const jwt = await new SignJWT({ + sub: user.id, + iss: new URL(config.http.base_url).origin, + aud: client_id, + exp: Math.floor(Date.now() / 1000) + 60 * 60, + iat: Math.floor(Date.now() / 1000), + nbf: Math.floor(Date.now() / 1000), + }) + .setProtectedHeader({ alg: "EdDSA" }) + .sign(privateKey); + + const application = await db.query.Applications.findFirst({ + where: (app, { eq }) => eq(app.clientId, client_id), + }); + + if (!application) { + return errorResponse("Invalid application", 400); + } + + const searchParams = new URLSearchParams({ + application: application.name, + client_secret: application.secret, + }); + + if (application.website) + searchParams.append("website", application.website); + + // Add all data that is not undefined + for (const [key, value] of Object.entries(parsingResult.data)) { + if (value !== undefined) searchParams.append(key, String(value)); + } + + // Redirect to OAuth authorize with JWT + return response(null, 302, { + Location: new URL( + `/oauth/redirect?${searchParams.toString()}`, + config.http.base_url, + ).toString(), + // Set cookie with JWT + "Set-Cookie": `jwt=${jwt}; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=${ + 60 * 60 + }`, + }); +}); diff --git a/server/api/api/v1/apps/index.ts b/server/api/api/v1/apps/index.ts index a6d24bf8..dee56ced 100644 --- a/server/api/api/v1/apps/index.ts +++ b/server/api/api/v1/apps/index.ts @@ -37,7 +37,7 @@ export default apiRoute( .insert(Applications) .values({ name: client_name || "", - redirectUris: redirect_uris || "", + redirectUri: redirect_uris || "", scopes: scopes || "read", website: website || null, clientId: randomBytes(32).toString("base64url"), @@ -52,7 +52,7 @@ export default apiRoute( website: app.website, client_id: app.clientId, client_secret: app.secret, - redirect_uri: app.redirectUris, + redirect_uri: app.redirectUri, vapid_link: app.vapidKey, }); }, diff --git a/server/api/api/v1/apps/verify_credentials/index.ts b/server/api/api/v1/apps/verify_credentials/index.ts index 84f1d7bd..35ce56ac 100644 --- a/server/api/api/v1/apps/verify_credentials/index.ts +++ b/server/api/api/v1/apps/verify_credentials/index.ts @@ -31,7 +31,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { name: application.name, website: application.website, vapid_key: application.vapidKey, - redirect_uris: application.redirectUris, + redirect_uris: application.redirectUri, scopes: application.scopes, }); }); diff --git a/server/api/oauth/authorize/index.ts b/server/api/oauth/authorize/index.ts new file mode 100644 index 00000000..d9707d89 --- /dev/null +++ b/server/api/oauth/authorize/index.ts @@ -0,0 +1,289 @@ +import { randomBytes } from "node:crypto"; +import { apiRoute, applyConfig, idValidator } from "@api"; +import { z } from "zod"; +import { TokenType } from "~database/entities/Token"; +import { findFirstUser } from "~database/entities/User"; +import { db } from "~drizzle/db"; +import { Tokens } from "~drizzle/schema"; +import { response } from "@response"; +import { jwtVerify, SignJWT } from "jose"; +import { config } from "~packages/config-manager"; + +export const meta = applyConfig({ + allowedMethods: ["POST"], + ratelimits: { + max: 4, + duration: 60, + }, + route: "/oauth/authorize", + auth: { + required: false, + }, +}); + +export const schema = z.object({ + scope: z.string().optional(), + redirect_uri: z.string().url().optional(), + response_type: z.enum([ + "code", + "token", + "none", + "id_token", + "code id_token", + "code token", + "token id_token", + "code token id_token", + ]), + client_id: z.string(), + state: z.string().optional(), + code_challenge: z.string().optional(), + code_challenge_method: z.enum(["plain", "S256"]).optional(), +}); + +export const querySchema = z.object({ + prompt: z + .enum(["none", "login", "consent", "select_account"]) + .optional() + .default("none"), + max_age: z + .number() + .int() + .optional() + .default(60 * 60 * 24 * 7), +}); + +const returnError = (error: string, description: string) => + response(null, 302, { + Location: new URL( + `/oauth/authorize?${new URLSearchParams({ + error: error, + error_description: description, + }).toString()}`, + config.http.base_url, + ).toString(), + }); + +/** + * OIDC Authorization + */ +export default apiRoute( + async (req, matchedRoute, extraData) => { + const { + scope, + redirect_uri, + response_type, + client_id, + state, + code_challenge, + code_challenge_method, + } = extraData.parsedRequest; + + const cookie = req.headers.get("Cookie"); + + if (!cookie) + return returnError( + "invalid_request", + "No cookies were sent with the request", + ); + + const jwt = cookie + .split(";") + .find((c) => c.trim().startsWith("jwt=")) + ?.split("=")[1]; + + if (!jwt) + return returnError( + "invalid_request", + "No jwt cookie was sent in the request", + ); + + // Try and import the key + const privateKey = await crypto.subtle.importKey( + "pkcs8", + Buffer.from(config.oidc.jwt_key.split(";")[0], "base64"), + "Ed25519", + true, + ["sign"], + ); + + const publicKey = await crypto.subtle.importKey( + "spki", + Buffer.from(config.oidc.jwt_key.split(";")[1], "base64"), + "Ed25519", + true, + ["verify"], + ); + + const result = await jwtVerify(jwt, publicKey, { + algorithms: ["EdDSA"], + issuer: new URL(config.http.base_url).origin, + audience: client_id, + }).catch((e) => { + console.error(e); + return null; + }); + + if (!result) + return returnError( + "invalid_request", + "Invalid JWT, could not verify", + ); + + const payload = result.payload; + + if (!payload.sub) return returnError("invalid_request", "Invalid sub"); + if (!payload.aud) return returnError("invalid_request", "Invalid aud"); + if (!payload.exp) return returnError("invalid_request", "Invalid exp"); + + // Check if the user is authenticated + const user = await findFirstUser({ + where: (user, { eq }) => eq(user.id, payload.sub ?? ""), + }); + + if (!user) return returnError("invalid_request", "Invalid sub"); + + const responseTypes = response_type.split(" "); + + const asksCode = responseTypes.includes("code"); + const asksToken = responseTypes.includes("token"); + const asksIdToken = responseTypes.includes("id_token"); + + if (!asksCode && !asksToken && !asksIdToken) + return returnError( + "invalid_request", + "Invalid response_type, must ask for code, token, or id_token", + ); + + if (asksCode && !redirect_uri) + return returnError( + "invalid_request", + "Redirect URI is required for code flow", + ); + + /* if (asksCode && !code_challenge) + return returnError( + "invalid_request", + "Code challenge is required for code flow", + ); + + if (asksCode && !code_challenge_method) + return returnError( + "invalid_request", + "Code challenge method is required for code flow", + ); */ + + // Authenticate the user + const application = await db.query.Applications.findFirst({ + where: (app, { eq }) => eq(app.clientId, client_id), + }); + + if (!application) + return returnError( + "invalid_client", + "Invalid client_id or client_secret", + ); + + if (application.redirectUri !== redirect_uri) + return returnError( + "invalid_request", + "Redirect URI does not match client_id", + ); + + /* if (application.slate !== slate) + return returnError("invalid_request", "Invalid slate"); */ + + // Validate scopes, they can either be equal or a subset of the application's scopes + const applicationScopes = application.scopes.split(" "); + + if ( + scope && + !scope.split(" ").every((s) => applicationScopes.includes(s)) + ) + return returnError("invalid_scope", "Invalid scope"); + + // Generate tokens + const code = randomBytes(256).toString("base64url"); + + // Handle the requested scopes + let idTokenPayload = {}; + const scopeIncludesOpenID = scope?.split(" ").includes("openid"); + const scopeIncludesProfile = scope?.split(" ").includes("profile"); + const scopeIncludesEmail = scope?.split(" ").includes("email"); + if (scope) { + const scopes = scope.split(" "); + if (scopeIncludesOpenID) { + // Include the standard OpenID claims + idTokenPayload = { + ...idTokenPayload, + sub: user.id, + aud: client_id, + iss: new URL(config.http.base_url).origin, + iat: Math.floor(Date.now() / 1000), + exp: Math.floor(Date.now() / 1000) + 60 * 60, + }; + } + if (scopeIncludesProfile) { + // Include the user's profile information + idTokenPayload = { + ...idTokenPayload, + name: user.displayName, + preferred_username: user.username, + picture: user.avatar, + updated_at: new Date(user.updatedAt).toISOString(), + }; + } + if (scopeIncludesEmail) { + // Include the user's email address + idTokenPayload = { + ...idTokenPayload, + email: user.email, + email_verified: true, + }; + } + } + + const idToken = await new SignJWT(idTokenPayload) + .setProtectedHeader({ + alg: "EdDSA", + }) + .sign(privateKey); + + await db.insert(Tokens).values({ + accessToken: randomBytes(64).toString("base64url"), + code: code, + scope: scope ?? application.scopes, + tokenType: TokenType.BEARER, + applicationId: application.id, + redirectUri: redirect_uri ?? application.redirectUri, + expiresAt: new Date(Date.now() + 60 * 60 * 24 * 14).toISOString(), + idToken: + scopeIncludesOpenID || + scopeIncludesEmail || + scopeIncludesProfile + ? idToken + : null, + clientId: client_id, + userId: user.id, + }); + + // Redirect to the client + const redirectUri = new URL(redirect_uri ?? application.redirectUri); + + const searchParams = new URLSearchParams({ + code: code, + scope: scope ?? application.scopes, + token_type: "Bearer", + client_id: client_id, + }); + + if (state) searchParams.set("state", state); + + return response(null, 302, { + Location: `${redirectUri.origin}${ + redirectUri.pathname + }?${searchParams.toString()}`, + "Cache-Control": "no-store", + Pragma: "no-cache", + }); + }, +); diff --git a/server/api/oauth/token/index.ts b/server/api/oauth/token/index.ts index 60a79b65..ed6fbce1 100644 --- a/server/api/oauth/token/index.ts +++ b/server/api/oauth/token/index.ts @@ -1,7 +1,10 @@ -import { apiRoute, applyConfig } from "@api"; -import { errorResponse, jsonResponse } from "@response"; +import { apiRoute, applyConfig, idValidator } from "@api"; +import { jsonResponse } from "@response"; +import { eq } from "drizzle-orm"; import { z } from "zod"; import { db } from "~drizzle/db"; +import { Tokens } from "~drizzle/schema"; +import { config } from "~packages/config-manager"; export const meta = applyConfig({ allowedMethods: ["POST"], @@ -16,14 +19,43 @@ export const meta = applyConfig({ }); export const schema = z.object({ - grant_type: z.string(), - code: z.string(), - redirect_uri: z.string().url(), - client_id: z.string(), - client_secret: z.string(), - scope: z.string(), + code: z.string().optional(), + code_verifier: z.string().optional(), + grant_type: z.enum([ + "authorization_code", + "refresh_token", + "client_credentials", + "password", + "urn:ietf:params:oauth:grant-type:device_code", + "urn:ietf:params:oauth:grant-type:token-exchange", + "urn:ietf:params:oauth:grant-type:saml2-bearer", + "urn:openid:params:grant-type:ciba", + ]), + client_id: z.string().optional(), + client_secret: z.string().optional(), + username: z.string().optional(), + password: z.string().optional(), + redirect_uri: z.string().url().optional(), + refresh_token: z.string().optional(), + scope: z.string().optional(), + assertion: z.string().optional(), + audience: z.string().optional(), + subject_token_type: z.string().optional(), + subject_token: z.string().optional(), + actor_token_type: z.string().optional(), + actor_token: z.string().optional(), + auth_req_id: z.string().optional(), }); +const returnError = (error: string, description: string) => + jsonResponse( + { + error, + error_description: description, + }, + 401, + ); + /** * Allows getting token from OAuth code */ @@ -33,50 +65,78 @@ export default apiRoute( grant_type, code, redirect_uri, + scope, client_id, client_secret, - scope, } = extraData.parsedRequest; - if (grant_type !== "authorization_code") - return errorResponse( - "Invalid grant type (try 'authorization_code')", - 422, - ); + switch (grant_type) { + case "authorization_code": { + if (!code) { + return returnError("invalid_request", "Code is required"); + } - // Get associated token - const application = await db.query.Applications.findFirst({ - where: (application, { eq, and }) => - and( - eq(application.clientId, client_id), - eq(application.secret, client_secret), - eq(application.redirectUris, redirect_uri), - eq(application.scopes, scope?.replaceAll("+", " ")), - ), - }); + if (!redirect_uri) { + return returnError( + "invalid_request", + "Redirect URI is required", + ); + } - if (!application) - return errorResponse( - "Invalid client credentials (missing application)", - 401, - ); + if (!client_id) { + return returnError( + "invalid_client", + "Client ID is required", + ); + } - const token = await db.query.Tokens.findFirst({ - where: (token, { eq }) => - eq(token.code, code) && eq(token.applicationId, application.id), - }); + // Verify the client_secret + const client = await db.query.Applications.findFirst({ + where: (application, { eq }) => + eq(application.clientId, client_id), + }); - if (!token) - return errorResponse( - "Invalid access token or client credentials", - 401, - ); + if (!client || client.secret !== client_secret) { + return returnError( + "invalid_client", + "Invalid client credentials", + ); + } - return jsonResponse({ - access_token: token.accessToken, - token_type: token.tokenType, - scope: token.scope, - created_at: new Date(token.createdAt).getTime(), - }); + const token = await db.query.Tokens.findFirst({ + where: (token, { eq, and }) => + and( + eq(token.code, code), + eq(token.redirectUri, redirect_uri), + eq(token.clientId, client_id), + ), + }); + + if (!token) { + return returnError("invalid_grant", "Code not found"); + } + + // Invalidate the code + await db + .update(Tokens) + .set({ code: null }) + .where(eq(Tokens.id, token.id)); + + return jsonResponse({ + access_token: token.accessToken, + token_type: "Bearer", + expires_in: token.expiresAt + ? (new Date(token.expiresAt).getTime() - Date.now()) / + 1000 + : null, + id_token: token.idToken, + refresh_token: null, + scope: token.scope, + created_at: new Date(token.createdAt).toISOString(), + }); + } + } + + return returnError("unsupported_grant_type", "Unsupported grant type"); }, ); diff --git a/server/api/users/[uuid]/inbox/index.ts b/server/api/users/[uuid]/inbox/index.ts index f00b971b..36a15ba8 100644 --- a/server/api/users/[uuid]/inbox/index.ts +++ b/server/api/users/[uuid]/inbox/index.ts @@ -86,7 +86,7 @@ export default apiRoute(async (req, matchedRoute, extraData) => { const public_key = await crypto.subtle.importKey( "spki", - Uint8Array.from(atob(sender.publicKey), (c) => c.charCodeAt(0)), + Buffer.from(sender.publicKey, "base64"), "Ed25519", false, ["verify"], diff --git a/server/api/well-known/jwks/index.ts b/server/api/well-known/jwks/index.ts new file mode 100644 index 00000000..a5299e3f --- /dev/null +++ b/server/api/well-known/jwks/index.ts @@ -0,0 +1,42 @@ +import { apiRoute, applyConfig } from "@api"; +import { jsonResponse } from "@response"; +import { config } from "~packages/config-manager"; +import { exportJWK, createRemoteJWKSet } from "jose"; + +export const meta = applyConfig({ + allowedMethods: ["GET"], + auth: { + required: false, + }, + ratelimits: { + duration: 30, + max: 60, + }, + route: "/.well-known/jwks", +}); + +export default apiRoute(async (req, matchedRoute, extraData) => { + const publicKey = await crypto.subtle.importKey( + "spki", + Buffer.from(config.oidc.jwt_key.split(";")[1], "base64"), + "Ed25519", + true, + ["verify"], + ); + + const jwk = await exportJWK(publicKey); + + // Remove the private key + jwk.d = undefined; + + return jsonResponse({ + keys: [ + { + ...jwk, + use: "sig", + alg: "EdDSA", + kid: "1", + }, + ], + }); +}); diff --git a/server/api/well-known/openid-configuration/index.ts b/server/api/well-known/openid-configuration/index.ts new file mode 100644 index 00000000..bab12344 --- /dev/null +++ b/server/api/well-known/openid-configuration/index.ts @@ -0,0 +1,32 @@ +import { apiRoute, applyConfig } from "@api"; +import { jsonResponse } from "@response"; +import { config } from "~packages/config-manager"; + +export const meta = applyConfig({ + allowedMethods: ["GET"], + auth: { + required: false, + }, + ratelimits: { + duration: 30, + max: 60, + }, + route: "/.well-known/openid-configuration", +}); + +export default apiRoute(async (req, matchedRoute, extraData) => { + const base_url = new URL(config.http.base_url); + return jsonResponse({ + issuer: base_url.origin.toString(), + authorization_endpoint: `${base_url.origin}/oauth/authorize`, + token_endpoint: `${base_url.origin}/oauth/token`, + userinfo_endpoint: `${base_url.origin}/api/v1/accounts/verify_credentials`, + jwks_uri: `${base_url.origin}/.well-known/jwks`, + response_types_supported: ["code"], + subject_types_supported: ["public"], + id_token_signing_alg_values_supported: ["EdDSA"], + scopes_supported: ["openid", "profile", "email"], + token_endpoint_auth_methods_supported: ["client_secret_basic"], + claims_supported: ["sub"], + }); +}); diff --git a/tests/oauth.test.ts b/tests/oauth.test.ts index e70cf4be..7b97b838 100644 --- a/tests/oauth.test.ts +++ b/tests/oauth.test.ts @@ -13,6 +13,7 @@ const base_url = "http://lysand.localhost:8080"; //config.http.base_url; let client_id: string; let client_secret: string; let code: string; +let jwt: string; let token: APIToken; const { users, passwords, deleteUsers } = await getTestUsers(1); @@ -57,7 +58,7 @@ describe("POST /api/v1/apps/", () => { }); describe("POST /api/auth/login/", () => { - test("should get a code", async () => { + test("should get a JWT", async () => { const formData = new FormData(); formData.append("email", users[0]?.email ?? ""); @@ -77,33 +78,80 @@ describe("POST /api/auth/login/", () => { ); expect(response.status).toBe(302); - expect(response.headers.get("Location")).toMatch( - /^\/oauth\/redirect\?redirect_uri=https%3A%2F%2Fexample.com&code=[a-f0-9]+&client_id=[a-zA-Z0-9_-]+&application=Test\+Application&website=https%3A%2F%2Fexample.com&scope=read\+write$/, + expect(response.headers.get("location")).toBeDefined(); + const locationHeader = new URL( + response.headers.get("Location") ?? "", + "", ); - code = - new URL( - response.headers.get("Location") ?? "", - "http://lysand.localhost:8080", - ).searchParams.get("code") ?? ""; + expect(locationHeader.pathname).toBe("/oauth/redirect"); + expect(locationHeader.searchParams.get("client_id")).toBe(client_id); + expect(locationHeader.searchParams.get("redirect_uri")).toBe( + "https://example.com", + ); + expect(locationHeader.searchParams.get("response_type")).toBe("code"); + expect(locationHeader.searchParams.get("scope")).toBe("read write"); + + expect(response.headers.get("Set-Cookie")).toMatch(/jwt=[^;]+;/); + + jwt = + response.headers.get("Set-Cookie")?.match(/jwt=([^;]+);/)?.[1] ?? + ""; + }); +}); + +describe("POST /oauth/authorize/", () => { + test("should get a code", async () => { + const response = await sendTestRequest( + new Request(wrapRelativeUrl("/oauth/authorize", base_url), { + method: "POST", + headers: { + Cookie: `jwt=${jwt}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + client_id, + client_secret, + redirect_uri: "https://example.com", + response_type: "code", + scope: "read write", + max_age: "604800", + }), + }), + ); + + expect(response.status).toBe(302); + expect(response.headers.get("location")).toBeDefined(); + const locationHeader = new URL( + response.headers.get("Location") ?? "", + "", + ); + + expect(locationHeader.origin).toBe("https://example.com"); + expect(locationHeader.searchParams.get("client_id")).toBe(client_id); + expect(locationHeader.searchParams.get("scope")).toBe("read write"); + + code = locationHeader.searchParams.get("code") ?? ""; }); }); describe("POST /oauth/token/", () => { test("should get an access token", async () => { - const formData = new FormData(); - - formData.append("grant_type", "authorization_code"); - formData.append("code", code); - formData.append("redirect_uri", "https://example.com"); - formData.append("client_id", client_id); - formData.append("client_secret", client_secret); - formData.append("scope", "read+write"); - const response = await sendTestRequest( new Request(wrapRelativeUrl("/oauth/token/", base_url), { method: "POST", - body: formData, + headers: { + Authorization: `Bearer ${jwt}`, + "Content-Type": "application/x-www-form-urlencoded", + }, + body: new URLSearchParams({ + grant_type: "authorization_code", + code, + redirect_uri: "https://example.com", + client_id, + client_secret, + scope: "read write", + }), }), ); @@ -115,7 +163,10 @@ describe("POST /oauth/token/", () => { access_token: expect.any(String), token_type: "Bearer", scope: "read write", - created_at: expect.any(Number), + created_at: expect.any(String), + expires_in: expect.any(Number), + id_token: null, + refresh_token: null, }); token = json;