From d9fd09c13d89d693b3fa915c4ae9dcb99e7894c3 Mon Sep 17 00:00:00 2001 From: goddonebianu Date: Tue, 10 Mar 2026 09:45:15 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=BC=BA=E5=88=B6=E8=A6=81?= =?UTF-8?q?=E6=B1=82=20username=20=E5=B9=B6-=20=E6=B7=BB=E5=8A=A0=20hooks?= =?UTF-8?q?=20=E9=AA=8C=E8=AF=81=20username=20=E5=BF=85=E5=A1=AB=20-=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=20schema:=20username=20=E6=94=B9=E4=B8=BA=20?= =?UTF-8?q?NOT=20NULL=20-=20=E9=87=8D=E7=BD=AE=E6=9C=AC=E5=9C=B0=E5=92=8C?= =?UTF-8?q?=E7=94=9F=E4=BA=A7=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../20251210105812_init/migration.sql | 120 -------- .../migration.sql | 138 --------- .../migration.sql | 8 - .../migration.sql | 30 -- .../migration.sql | 11 - .../migration.sql | 7 - .../migration.sql | 94 ------- .../migration.sql | 12 - .../migration.sql | 33 --- .../20260310014042_init/migration.sql | 262 ++++++++++++++++++ prisma/schema.prisma | 2 +- src/auth.ts | 15 +- 12 files changed, 277 insertions(+), 455 deletions(-) delete mode 100644 prisma/migrations/20251210105812_init/migration.sql delete mode 100644 prisma/migrations/20260105081337_dictionary_add/migration.sql delete mode 100644 prisma/migrations/20260106061255_remove_unique_constraint/migration.sql delete mode 100644 prisma/migrations/20260106105236_create_translation_history/migration.sql delete mode 100644 prisma/migrations/20260106112014_relax_unique_constraint_on_pairs/migration.sql delete mode 100644 prisma/migrations/20260108021511_change_varchar_to_text/migration.sql delete mode 100644 prisma/migrations/20260113125222_optimize_dicttionary/migration.sql delete mode 100644 prisma/migrations/20260203113111_set_username_unique/migration.sql delete mode 100644 prisma/migrations/20260308142357_add_folder_visibility_and_favorites/migration.sql create mode 100644 prisma/migrations/20260310014042_init/migration.sql diff --git a/prisma/migrations/20251210105812_init/migration.sql b/prisma/migrations/20251210105812_init/migration.sql deleted file mode 100644 index 7e0dd7e..0000000 --- a/prisma/migrations/20251210105812_init/migration.sql +++ /dev/null @@ -1,120 +0,0 @@ --- CreateTable -CREATE TABLE "pairs" ( - "id" SERIAL NOT NULL, - "locale1" VARCHAR(10) NOT NULL, - "locale2" VARCHAR(10) NOT NULL, - "text1" TEXT NOT NULL, - "text2" TEXT NOT NULL, - "ipa1" TEXT, - "ipa2" TEXT, - "folder_id" INTEGER NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "pairs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "folders" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "user_id" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "folders_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "user" ( - "id" TEXT NOT NULL, - "name" TEXT NOT NULL, - "email" TEXT NOT NULL, - "emailVerified" BOOLEAN NOT NULL DEFAULT false, - "image" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "user_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "session" ( - "id" TEXT NOT NULL, - "expiresAt" TIMESTAMP(3) NOT NULL, - "token" TEXT NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - "ipAddress" TEXT, - "userAgent" TEXT, - "userId" TEXT NOT NULL, - - CONSTRAINT "session_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "account" ( - "id" TEXT NOT NULL, - "accountId" TEXT NOT NULL, - "providerId" TEXT NOT NULL, - "userId" TEXT NOT NULL, - "accessToken" TEXT, - "refreshToken" TEXT, - "idToken" TEXT, - "accessTokenExpiresAt" TIMESTAMP(3), - "refreshTokenExpiresAt" TIMESTAMP(3), - "scope" TEXT, - "password" TEXT, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "account_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "verification" ( - "id" TEXT NOT NULL, - "identifier" TEXT NOT NULL, - "value" TEXT NOT NULL, - "expiresAt" TIMESTAMP(3) NOT NULL, - "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updatedAt" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "verification_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "pairs_folder_id_idx" ON "pairs"("folder_id"); - --- CreateIndex -CREATE UNIQUE INDEX "pairs_folder_id_locale1_locale2_text1_key" ON "pairs"("folder_id", "locale1", "locale2", "text1"); - --- CreateIndex -CREATE INDEX "folders_user_id_idx" ON "folders"("user_id"); - --- CreateIndex -CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); - --- CreateIndex -CREATE INDEX "session_userId_idx" ON "session"("userId"); - --- CreateIndex -CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); - --- CreateIndex -CREATE INDEX "account_userId_idx" ON "account"("userId"); - --- CreateIndex -CREATE INDEX "verification_identifier_idx" ON "verification"("identifier"); - --- AddForeignKey -ALTER TABLE "pairs" ADD CONSTRAINT "pairs_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "folders"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "folders" ADD CONSTRAINT "folders_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260105081337_dictionary_add/migration.sql b/prisma/migrations/20260105081337_dictionary_add/migration.sql deleted file mode 100644 index bb57941..0000000 --- a/prisma/migrations/20260105081337_dictionary_add/migration.sql +++ /dev/null @@ -1,138 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `ipa1` on the `pairs` table. All the data in the column will be lost. - - You are about to drop the column `ipa2` on the `pairs` table. All the data in the column will be lost. - -*/ --- AlterTable --- 重命名并修改类型为 TEXT -ALTER TABLE "pairs" -RENAME COLUMN "locale1" TO "language1"; - -ALTER TABLE "pairs" -ALTER COLUMN "language1" SET DATA TYPE VARCHAR(20); - -ALTER TABLE "pairs" -RENAME COLUMN "locale2" TO "language2"; - -ALTER TABLE "pairs" -ALTER COLUMN "language2" SET DATA TYPE VARCHAR(20); - --- CreateTable -CREATE TABLE "dictionary_lookups" ( - "id" SERIAL NOT NULL, - "user_id" TEXT, - "text" TEXT NOT NULL, - "query_lang" TEXT NOT NULL, - "definition_lang" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "dictionary_word_id" INTEGER, - "dictionary_phrase_id" INTEGER, - - CONSTRAINT "dictionary_lookups_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dictionary_words" ( - "id" SERIAL NOT NULL, - "standard_form" TEXT NOT NULL, - "query_lang" TEXT NOT NULL, - "definition_lang" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_words_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dictionary_phrases" ( - "id" SERIAL NOT NULL, - "standard_form" TEXT NOT NULL, - "query_lang" TEXT NOT NULL, - "definition_lang" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_phrases_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dictionary_word_entries" ( - "id" SERIAL NOT NULL, - "word_id" INTEGER NOT NULL, - "ipa" TEXT NOT NULL, - "definition" TEXT NOT NULL, - "part_of_speech" TEXT NOT NULL, - "example" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_word_entries_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dictionary_phrase_entries" ( - "id" SERIAL NOT NULL, - "phrase_id" INTEGER NOT NULL, - "definition" TEXT NOT NULL, - "example" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_phrase_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "dictionary_lookups_user_id_idx" ON "dictionary_lookups"("user_id"); - --- CreateIndex -CREATE INDEX "dictionary_lookups_created_at_idx" ON "dictionary_lookups"("created_at"); - --- CreateIndex -CREATE INDEX "dictionary_lookups_text_query_lang_definition_lang_idx" ON "dictionary_lookups"("text", "query_lang", "definition_lang"); - --- CreateIndex -CREATE INDEX "dictionary_words_standard_form_idx" ON "dictionary_words"("standard_form"); - --- CreateIndex -CREATE INDEX "dictionary_words_query_lang_definition_lang_idx" ON "dictionary_words"("query_lang", "definition_lang"); - --- CreateIndex -CREATE UNIQUE INDEX "dictionary_words_standard_form_query_lang_definition_lang_key" ON "dictionary_words"("standard_form", "query_lang", "definition_lang"); - --- CreateIndex -CREATE INDEX "dictionary_phrases_standard_form_idx" ON "dictionary_phrases"("standard_form"); - --- CreateIndex -CREATE INDEX "dictionary_phrases_query_lang_definition_lang_idx" ON "dictionary_phrases"("query_lang", "definition_lang"); - --- CreateIndex -CREATE UNIQUE INDEX "dictionary_phrases_standard_form_query_lang_definition_lang_key" ON "dictionary_phrases"("standard_form", "query_lang", "definition_lang"); - --- CreateIndex -CREATE INDEX "dictionary_word_entries_word_id_idx" ON "dictionary_word_entries"("word_id"); - --- CreateIndex -CREATE INDEX "dictionary_word_entries_created_at_idx" ON "dictionary_word_entries"("created_at"); - --- CreateIndex -CREATE INDEX "dictionary_phrase_entries_phrase_id_idx" ON "dictionary_phrase_entries"("phrase_id"); - --- CreateIndex -CREATE INDEX "dictionary_phrase_entries_created_at_idx" ON "dictionary_phrase_entries"("created_at"); - --- AddForeignKey -ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_dictionary_word_id_fkey" FOREIGN KEY ("dictionary_word_id") REFERENCES "dictionary_words"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_dictionary_phrase_id_fkey" FOREIGN KEY ("dictionary_phrase_id") REFERENCES "dictionary_phrases"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "dictionary_word_entries" ADD CONSTRAINT "dictionary_word_entries_word_id_fkey" FOREIGN KEY ("word_id") REFERENCES "dictionary_words"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "dictionary_phrase_entries" ADD CONSTRAINT "dictionary_phrase_entries_phrase_id_fkey" FOREIGN KEY ("phrase_id") REFERENCES "dictionary_phrases"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260106061255_remove_unique_constraint/migration.sql b/prisma/migrations/20260106061255_remove_unique_constraint/migration.sql deleted file mode 100644 index ffbe1ce..0000000 --- a/prisma/migrations/20260106061255_remove_unique_constraint/migration.sql +++ /dev/null @@ -1,8 +0,0 @@ --- DropIndex -DROP INDEX "dictionary_phrases_standard_form_query_lang_definition_lang_key"; - --- DropIndex -DROP INDEX "dictionary_words_standard_form_query_lang_definition_lang_key"; - --- RenameIndex -ALTER INDEX "pairs_folder_id_locale1_locale2_text1_key" RENAME TO "pairs_folder_id_language1_language2_text1_key"; diff --git a/prisma/migrations/20260106105236_create_translation_history/migration.sql b/prisma/migrations/20260106105236_create_translation_history/migration.sql deleted file mode 100644 index 7371156..0000000 --- a/prisma/migrations/20260106105236_create_translation_history/migration.sql +++ /dev/null @@ -1,30 +0,0 @@ --- CreateTable -CREATE TABLE "translation_history" ( - "id" SERIAL NOT NULL, - "user_id" TEXT, - "source_text" TEXT NOT NULL, - "source_language" VARCHAR(20) NOT NULL, - "target_language" VARCHAR(20) NOT NULL, - "translated_text" TEXT NOT NULL, - "source_ipa" TEXT, - "target_ipa" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "translation_history_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "translation_history_user_id_idx" ON "translation_history"("user_id"); - --- CreateIndex -CREATE INDEX "translation_history_created_at_idx" ON "translation_history"("created_at"); - --- CreateIndex -CREATE INDEX "translation_history_source_text_target_language_idx" ON "translation_history"("source_text", "target_language"); - --- CreateIndex -CREATE INDEX "translation_history_translated_text_source_language_target__idx" ON "translation_history"("translated_text", "source_language", "target_language"); - --- AddForeignKey -ALTER TABLE "translation_history" ADD CONSTRAINT "translation_history_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20260106112014_relax_unique_constraint_on_pairs/migration.sql b/prisma/migrations/20260106112014_relax_unique_constraint_on_pairs/migration.sql deleted file mode 100644 index c16b32f..0000000 --- a/prisma/migrations/20260106112014_relax_unique_constraint_on_pairs/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[folder_id,language1,language2,text1,text2]` on the table `pairs` will be added. If there are existing duplicate values, this will fail. - -*/ --- DropIndex -DROP INDEX "pairs_folder_id_language1_language2_text1_key"; - --- CreateIndex -CREATE UNIQUE INDEX "pairs_folder_id_language1_language2_text1_text2_key" ON "pairs"("folder_id", "language1", "language2", "text1", "text2"); diff --git a/prisma/migrations/20260108021511_change_varchar_to_text/migration.sql b/prisma/migrations/20260108021511_change_varchar_to_text/migration.sql deleted file mode 100644 index 712b68a..0000000 --- a/prisma/migrations/20260108021511_change_varchar_to_text/migration.sql +++ /dev/null @@ -1,7 +0,0 @@ --- AlterTable -ALTER TABLE "pairs" ALTER COLUMN "language1" SET DATA TYPE TEXT, -ALTER COLUMN "language2" SET DATA TYPE TEXT; - --- AlterTable -ALTER TABLE "translation_history" ALTER COLUMN "source_language" SET DATA TYPE TEXT, -ALTER COLUMN "target_language" SET DATA TYPE TEXT; diff --git a/prisma/migrations/20260113125222_optimize_dicttionary/migration.sql b/prisma/migrations/20260113125222_optimize_dicttionary/migration.sql deleted file mode 100644 index a24a98c..0000000 --- a/prisma/migrations/20260113125222_optimize_dicttionary/migration.sql +++ /dev/null @@ -1,94 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `dictionary_phrase_id` on the `dictionary_lookups` table. All the data in the column will be lost. - - You are about to drop the column `dictionary_word_id` on the `dictionary_lookups` table. All the data in the column will be lost. - - You are about to drop the `dictionary_phrase_entries` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `dictionary_phrases` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `dictionary_word_entries` table. If the table is not empty, all the data it contains will be lost. - - You are about to drop the `dictionary_words` table. If the table is not empty, all the data it contains will be lost. - -*/ --- DropForeignKey -ALTER TABLE "dictionary_lookups" DROP CONSTRAINT "dictionary_lookups_dictionary_phrase_id_fkey"; - --- DropForeignKey -ALTER TABLE "dictionary_lookups" DROP CONSTRAINT "dictionary_lookups_dictionary_word_id_fkey"; - --- DropForeignKey -ALTER TABLE "dictionary_phrase_entries" DROP CONSTRAINT "dictionary_phrase_entries_phrase_id_fkey"; - --- DropForeignKey -ALTER TABLE "dictionary_word_entries" DROP CONSTRAINT "dictionary_word_entries_word_id_fkey"; - --- DropIndex -DROP INDEX "dictionary_lookups_text_query_lang_definition_lang_idx"; - --- AlterTable -ALTER TABLE "dictionary_lookups" DROP COLUMN "dictionary_phrase_id", -DROP COLUMN "dictionary_word_id", -ADD COLUMN "dictionary_item_id" INTEGER, -ADD COLUMN "normalized_text" TEXT NOT NULL DEFAULT ''; - --- DropTable -DROP TABLE "dictionary_phrase_entries"; - --- DropTable -DROP TABLE "dictionary_phrases"; - --- DropTable -DROP TABLE "dictionary_word_entries"; - --- DropTable -DROP TABLE "dictionary_words"; - --- CreateTable -CREATE TABLE "dictionary_items" ( - "id" SERIAL NOT NULL, - "frequency" INTEGER NOT NULL DEFAULT 1, - "standard_form" TEXT NOT NULL, - "query_lang" TEXT NOT NULL, - "definition_lang" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_items_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "dictionary_entries" ( - "id" SERIAL NOT NULL, - "item_id" INTEGER NOT NULL, - "ipa" TEXT, - "definition" TEXT NOT NULL, - "part_of_speech" TEXT, - "example" TEXT NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "dictionary_entries_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "dictionary_items_standard_form_idx" ON "dictionary_items"("standard_form"); - --- CreateIndex -CREATE INDEX "dictionary_items_query_lang_definition_lang_idx" ON "dictionary_items"("query_lang", "definition_lang"); - --- CreateIndex -CREATE UNIQUE INDEX "dictionary_items_standard_form_query_lang_definition_lang_key" ON "dictionary_items"("standard_form", "query_lang", "definition_lang"); - --- CreateIndex -CREATE INDEX "dictionary_entries_item_id_idx" ON "dictionary_entries"("item_id"); - --- CreateIndex -CREATE INDEX "dictionary_entries_created_at_idx" ON "dictionary_entries"("created_at"); - --- CreateIndex -CREATE INDEX "dictionary_lookups_normalized_text_idx" ON "dictionary_lookups"("normalized_text"); - --- AddForeignKey -ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_dictionary_item_id_fkey" FOREIGN KEY ("dictionary_item_id") REFERENCES "dictionary_items"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "dictionary_entries" ADD CONSTRAINT "dictionary_entries_item_id_fkey" FOREIGN KEY ("item_id") REFERENCES "dictionary_items"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260203113111_set_username_unique/migration.sql b/prisma/migrations/20260203113111_set_username_unique/migration.sql deleted file mode 100644 index 1e854fa..0000000 --- a/prisma/migrations/20260203113111_set_username_unique/migration.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[username]` on the table `user` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "user" ADD COLUMN "displayUsername" TEXT, -ADD COLUMN "username" TEXT; - --- CreateIndex -CREATE UNIQUE INDEX "user_username_key" ON "user"("username"); diff --git a/prisma/migrations/20260308142357_add_folder_visibility_and_favorites/migration.sql b/prisma/migrations/20260308142357_add_folder_visibility_and_favorites/migration.sql deleted file mode 100644 index 7cc4bca..0000000 --- a/prisma/migrations/20260308142357_add_folder_visibility_and_favorites/migration.sql +++ /dev/null @@ -1,33 +0,0 @@ --- CreateEnum -CREATE TYPE "Visibility" AS ENUM ('PRIVATE', 'PUBLIC'); - --- AlterTable -ALTER TABLE "folders" ADD COLUMN "visibility" "Visibility" NOT NULL DEFAULT 'PRIVATE'; - --- CreateTable -CREATE TABLE "folder_favorites" ( - "id" SERIAL NOT NULL, - "user_id" TEXT NOT NULL, - "folder_id" INTEGER NOT NULL, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - - CONSTRAINT "folder_favorites_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE INDEX "folder_favorites_folder_id_idx" ON "folder_favorites"("folder_id"); - --- CreateIndex -CREATE UNIQUE INDEX "folder_favorites_user_id_folder_id_key" ON "folder_favorites"("user_id", "folder_id"); - --- CreateIndex -CREATE INDEX "folder_favorites_user_id_idx" ON "folder_favorites"("user_id"); - --- CreateIndex -CREATE INDEX "folders_visibility_idx" ON "folders"("visibility"); - --- AddForeignKey -ALTER TABLE "folder_favorites" ADD CONSTRAINT "folder_favorites_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "folder_favorites" ADD CONSTRAINT "folder_favorites_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "folders"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20260310014042_init/migration.sql b/prisma/migrations/20260310014042_init/migration.sql new file mode 100644 index 0000000..329f744 --- /dev/null +++ b/prisma/migrations/20260310014042_init/migration.sql @@ -0,0 +1,262 @@ +-- CreateEnum +CREATE TYPE "Visibility" AS ENUM ('PRIVATE', 'PUBLIC'); + +-- CreateTable +CREATE TABLE "user" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "emailVerified" BOOLEAN NOT NULL DEFAULT false, + "image" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "displayUsername" TEXT, + "username" TEXT NOT NULL, + + CONSTRAINT "user_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "session" ( + "id" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "token" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + "ipAddress" TEXT, + "userAgent" TEXT, + "userId" TEXT NOT NULL, + + CONSTRAINT "session_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "account" ( + "id" TEXT NOT NULL, + "accountId" TEXT NOT NULL, + "providerId" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "accessToken" TEXT, + "refreshToken" TEXT, + "idToken" TEXT, + "accessTokenExpiresAt" TIMESTAMP(3), + "refreshTokenExpiresAt" TIMESTAMP(3), + "scope" TEXT, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "account_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "verification" ( + "id" TEXT NOT NULL, + "identifier" TEXT NOT NULL, + "value" TEXT NOT NULL, + "expiresAt" TIMESTAMP(3) NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "verification_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "pairs" ( + "id" SERIAL NOT NULL, + "language1" TEXT NOT NULL, + "language2" TEXT NOT NULL, + "text1" TEXT NOT NULL, + "text2" TEXT NOT NULL, + "ipa1" TEXT, + "ipa2" TEXT, + "folder_id" INTEGER NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "pairs_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "folders" ( + "id" SERIAL NOT NULL, + "name" TEXT NOT NULL, + "user_id" TEXT NOT NULL, + "visibility" "Visibility" NOT NULL DEFAULT 'PRIVATE', + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "folders_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "folder_favorites" ( + "id" SERIAL NOT NULL, + "user_id" TEXT NOT NULL, + "folder_id" INTEGER NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "folder_favorites_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dictionary_lookups" ( + "id" SERIAL NOT NULL, + "user_id" TEXT, + "text" TEXT NOT NULL, + "query_lang" TEXT NOT NULL, + "definition_lang" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "dictionary_item_id" INTEGER, + "normalized_text" TEXT NOT NULL DEFAULT '', + + CONSTRAINT "dictionary_lookups_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dictionary_items" ( + "id" SERIAL NOT NULL, + "frequency" INTEGER NOT NULL DEFAULT 1, + "standard_form" TEXT NOT NULL, + "query_lang" TEXT NOT NULL, + "definition_lang" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "dictionary_items_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "dictionary_entries" ( + "id" SERIAL NOT NULL, + "item_id" INTEGER NOT NULL, + "ipa" TEXT, + "definition" TEXT NOT NULL, + "part_of_speech" TEXT, + "example" TEXT NOT NULL, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "dictionary_entries_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "translation_history" ( + "id" SERIAL NOT NULL, + "user_id" TEXT, + "source_text" TEXT NOT NULL, + "source_language" TEXT NOT NULL, + "target_language" TEXT NOT NULL, + "translated_text" TEXT NOT NULL, + "source_ipa" TEXT, + "target_ipa" TEXT, + "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updated_at" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "translation_history_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "user_email_key" ON "user"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "user_username_key" ON "user"("username"); + +-- CreateIndex +CREATE UNIQUE INDEX "session_token_key" ON "session"("token"); + +-- CreateIndex +CREATE INDEX "session_userId_idx" ON "session"("userId"); + +-- CreateIndex +CREATE INDEX "account_userId_idx" ON "account"("userId"); + +-- CreateIndex +CREATE INDEX "verification_identifier_idx" ON "verification"("identifier"); + +-- CreateIndex +CREATE INDEX "pairs_folder_id_idx" ON "pairs"("folder_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "pairs_folder_id_language1_language2_text1_text2_key" ON "pairs"("folder_id", "language1", "language2", "text1", "text2"); + +-- CreateIndex +CREATE INDEX "folders_user_id_idx" ON "folders"("user_id"); + +-- CreateIndex +CREATE INDEX "folders_visibility_idx" ON "folders"("visibility"); + +-- CreateIndex +CREATE INDEX "folder_favorites_user_id_idx" ON "folder_favorites"("user_id"); + +-- CreateIndex +CREATE INDEX "folder_favorites_folder_id_idx" ON "folder_favorites"("folder_id"); + +-- CreateIndex +CREATE UNIQUE INDEX "folder_favorites_user_id_folder_id_key" ON "folder_favorites"("user_id", "folder_id"); + +-- CreateIndex +CREATE INDEX "dictionary_lookups_user_id_idx" ON "dictionary_lookups"("user_id"); + +-- CreateIndex +CREATE INDEX "dictionary_lookups_created_at_idx" ON "dictionary_lookups"("created_at"); + +-- CreateIndex +CREATE INDEX "dictionary_lookups_normalized_text_idx" ON "dictionary_lookups"("normalized_text"); + +-- CreateIndex +CREATE INDEX "dictionary_items_standard_form_idx" ON "dictionary_items"("standard_form"); + +-- CreateIndex +CREATE INDEX "dictionary_items_query_lang_definition_lang_idx" ON "dictionary_items"("query_lang", "definition_lang"); + +-- CreateIndex +CREATE UNIQUE INDEX "dictionary_items_standard_form_query_lang_definition_lang_key" ON "dictionary_items"("standard_form", "query_lang", "definition_lang"); + +-- CreateIndex +CREATE INDEX "dictionary_entries_item_id_idx" ON "dictionary_entries"("item_id"); + +-- CreateIndex +CREATE INDEX "dictionary_entries_created_at_idx" ON "dictionary_entries"("created_at"); + +-- CreateIndex +CREATE INDEX "translation_history_user_id_idx" ON "translation_history"("user_id"); + +-- CreateIndex +CREATE INDEX "translation_history_created_at_idx" ON "translation_history"("created_at"); + +-- CreateIndex +CREATE INDEX "translation_history_source_text_target_language_idx" ON "translation_history"("source_text", "target_language"); + +-- CreateIndex +CREATE INDEX "translation_history_translated_text_source_language_target__idx" ON "translation_history"("translated_text", "source_language", "target_language"); + +-- AddForeignKey +ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "pairs" ADD CONSTRAINT "pairs_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "folders"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "folders" ADD CONSTRAINT "folders_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "folder_favorites" ADD CONSTRAINT "folder_favorites_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "folder_favorites" ADD CONSTRAINT "folder_favorites_folder_id_fkey" FOREIGN KEY ("folder_id") REFERENCES "folders"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_dictionary_item_id_fkey" FOREIGN KEY ("dictionary_item_id") REFERENCES "dictionary_items"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dictionary_lookups" ADD CONSTRAINT "dictionary_lookups_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "dictionary_entries" ADD CONSTRAINT "dictionary_entries_item_id_fkey" FOREIGN KEY ("item_id") REFERENCES "dictionary_items"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "translation_history" ADD CONSTRAINT "translation_history_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "user"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5eb367b..c865f85 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -16,7 +16,7 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt displayUsername String? - username String? @unique + username String @unique accounts Account[] dictionaryLookUps DictionaryLookUp[] folders Folder[] diff --git a/src/auth.ts b/src/auth.ts index 82f8abf..691fc4f 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -1,8 +1,9 @@ import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { nextCookies } from "better-auth/next-js"; -import { prisma } from "./lib/db"; import { username } from "better-auth/plugins"; +import { createAuthMiddleware, APIError } from "better-auth/api"; +import { prisma } from "./lib/db"; import { sendEmail, generateVerificationEmailHtml, @@ -41,4 +42,16 @@ export const auth = betterAuth({ }, }, plugins: [nextCookies(), username()], + hooks: { + before: createAuthMiddleware(async (ctx) => { + if (ctx.path !== "/sign-up/email" && ctx.path !== "/update-user") return; + + const body = ctx.body as { username?: string }; + if (!body.username || body.username.trim() === "") { + throw new APIError("BAD_REQUEST", { + message: "Username is required", + }); + } + }), + }, });