diff --git a/better-auth_migrations/2026-02-27T12-45-33.464Z.sql b/better-auth_migrations/2026-02-27T12-45-33.464Z.sql deleted file mode 100644 index eb97fb6..0000000 --- a/better-auth_migrations/2026-02-27T12-45-33.464Z.sql +++ /dev/null @@ -1,13 +0,0 @@ -create table "user" ("id" text not null primary key, "name" text not null, "email" text not null unique, "emailVerified" integer not null, "image" text, "createdAt" date not null, "updatedAt" date not null); - -create table "session" ("id" text not null primary key, "expiresAt" date not null, "token" text not null unique, "createdAt" date not null, "updatedAt" date not null, "ipAddress" text, "userAgent" text, "userId" text not null references "user" ("id") on delete cascade); - -create table "account" ("id" text not null primary key, "accountId" text not null, "providerId" text not null, "userId" text not null references "user" ("id") on delete cascade, "accessToken" text, "refreshToken" text, "idToken" text, "accessTokenExpiresAt" date, "refreshTokenExpiresAt" date, "scope" text, "password" text, "createdAt" date not null, "updatedAt" date not null); - -create table "verification" ("id" text not null primary key, "identifier" text not null, "value" text not null, "expiresAt" date not null, "createdAt" date not null, "updatedAt" date not null); - -create index "session_userId_idx" on "session" ("userId"); - -create index "account_userId_idx" on "account" ("userId"); - -create index "verification_identifier_idx" on "verification" ("identifier"); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f796946..c4ed8c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "ba-tts-dialect-annotation-plattform", "version": "0.1.0", "dependencies": { + "@better-auth/drizzle-adapter": "^1.5.4", "@libsql/client": "^0.17.0", "@tailwindcss/postcss": "^4.2.1", "auth": "^1.5.3", @@ -558,38 +559,45 @@ } }, "node_modules/@better-auth/core": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.19.tgz", - "integrity": "sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.5.4.tgz", + "integrity": "sha512-k5AdwPRQETZn0vdB60EB9CDxxfllpJXKqVxTjyXIUSRz7delNGlU0cR/iRP3VfVJwvYR1NbekphBDNo+KGoEzQ==", + "license": "MIT", + "peer": true, "dependencies": { - "@standard-schema/spec": "^1.0.0", - "zod": "^4.3.5" + "@standard-schema/spec": "^1.1.0", + "zod": "^4.3.6" }, "peerDependencies": { - "@better-auth/utils": "0.3.0", + "@better-auth/utils": "0.3.1", "@better-fetch/fetch": "1.1.21", - "better-call": "1.1.8", + "@cloudflare/workers-types": ">=4", + "better-call": "1.3.2", "jose": "^6.1.0", "kysely": "^0.28.5", "nanostores": "^1.0.1" + }, + "peerDependenciesMeta": { + "@cloudflare/workers-types": { + "optional": true + } } }, - "node_modules/@better-auth/telemetry": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.4.19.tgz", - "integrity": "sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw==", - "dependencies": { - "@better-auth/utils": "0.3.0", - "@better-fetch/fetch": "1.1.21" - }, + "node_modules/@better-auth/drizzle-adapter": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.5.4.tgz", + "integrity": "sha512-4M4nMAWrDd3TmpV6dONkJjybBVKRZghe5Oj0NNyDEoXubxastQdO7Sb5B54I1rTx5yoMgsqaB+kbJnu/9UgjQg==", + "license": "MIT", "peerDependencies": { - "@better-auth/core": "1.4.19" + "@better-auth/core": "1.5.4", + "@better-auth/utils": "^0.3.0", + "drizzle-orm": ">=0.41.0" } }, "node_modules/@better-auth/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.1.tgz", + "integrity": "sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg==", "license": "MIT" }, "node_modules/@better-fetch/fetch": { @@ -2814,6 +2822,19 @@ } } }, + "node_modules/auth/node_modules/@better-auth/drizzle-adapter": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@better-auth/drizzle-adapter/-/drizzle-adapter-1.5.3.tgz", + "integrity": "sha512-dib9V1vpwDu+TKLC+L+8Q5bLNS0uE3JCT4pGotw52pnpiQF8msoMK4eEfri19f8DtNltpb2F2yzyIsTugBBYNQ==", + "license": "MIT", + "optional": true, + "peer": true, + "peerDependencies": { + "@better-auth/core": "1.5.3", + "@better-auth/utils": "^0.3.0", + "drizzle-orm": ">=0.41.0" + } + }, "node_modules/auth/node_modules/@better-auth/kysely-adapter": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@better-auth/kysely-adapter/-/kysely-adapter-1.5.3.tgz", @@ -2848,12 +2869,6 @@ "@better-auth/core": "1.5.3" } }, - "node_modules/auth/node_modules/@better-auth/utils": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.1.tgz", - "integrity": "sha512-+CGp4UmZSUrHHnpHhLPYu6cV+wSUSvVbZbNykxhUDocpVNTo9uFFxw/NqJlh1iC4wQ9HKKWGCKuZ5wUgS0v6Kg==", - "license": "MIT" - }, "node_modules/auth/node_modules/better-auth": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/better-auth/-/better-auth-1.5.3.tgz", @@ -2968,26 +2983,6 @@ } } }, - "node_modules/auth/node_modules/better-call": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.3.2.tgz", - "integrity": "sha512-4cZIfrerDsNTn3cm+MhLbUePN0gdwkhSXEuG7r/zuQ8c/H7iU0/jSK5TD3FW7U0MgKHce/8jGpPYNO4Ve+4NBw==", - "license": "MIT", - "dependencies": { - "@better-auth/utils": "^0.3.1", - "@better-fetch/fetch": "^1.1.21", - "rou3": "^0.7.12", - "set-cookie-parser": "^3.0.1" - }, - "peerDependencies": { - "zod": "^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, "node_modules/auth/node_modules/drizzle-orm": { "version": "0.41.0", "resolved": "https://registry.npmjs.org/drizzle-orm/-/drizzle-orm-0.41.0.tgz", @@ -3109,12 +3104,6 @@ } } }, - "node_modules/auth/node_modules/set-cookie-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz", - "integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==", - "license": "MIT" - }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -3256,7 +3245,42 @@ "better-auth": "^1.4.19" } }, - "node_modules/better-call": { + "node_modules/better-auth/node_modules/@better-auth/core": { + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/@better-auth/core/-/core-1.4.19.tgz", + "integrity": "sha512-uADLHG1jc5BnEJi7f6ijUN5DmPPRSj++7m/G19z3UqA3MVCo4Y4t1MMa4IIxLCqGDFv22drdfxescgW+HnIowA==", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "zod": "^4.3.5" + }, + "peerDependencies": { + "@better-auth/utils": "0.3.0", + "@better-fetch/fetch": "1.1.21", + "better-call": "1.1.8", + "jose": "^6.1.0", + "kysely": "^0.28.5", + "nanostores": "^1.0.1" + } + }, + "node_modules/better-auth/node_modules/@better-auth/telemetry": { + "version": "1.4.19", + "resolved": "https://registry.npmjs.org/@better-auth/telemetry/-/telemetry-1.4.19.tgz", + "integrity": "sha512-ApGNS7olCTtDpKF8Ow3Z+jvFAirOj7c4RyFUpu8axklh3mH57ndpfUAUjhgA8UVoaaH/mnm/Tl884BlqiewLyw==", + "dependencies": { + "@better-auth/utils": "0.3.0", + "@better-fetch/fetch": "1.1.21" + }, + "peerDependencies": { + "@better-auth/core": "1.4.19" + } + }, + "node_modules/better-auth/node_modules/@better-auth/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@better-auth/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-W+Adw6ZA6mgvnSnhOki270rwJ42t4XzSK6YWGF//BbVXL6SwCLWfyzBc1lN2m/4RM28KubdBKQ4X5VMoLRNPQw==", + "license": "MIT" + }, + "node_modules/better-auth/node_modules/better-call": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.1.8.tgz", "integrity": "sha512-XMQ2rs6FNXasGNfMjzbyroSwKwYbZ/T3IxruSS6U2MJRsSYh3wYtG3o6H00ZlKZ/C/UPOAD97tqgQJNsxyeTXw==", @@ -3276,6 +3300,32 @@ } } }, + "node_modules/better-auth/node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/better-call": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/better-call/-/better-call-1.3.2.tgz", + "integrity": "sha512-4cZIfrerDsNTn3cm+MhLbUePN0gdwkhSXEuG7r/zuQ8c/H7iU0/jSK5TD3FW7U0MgKHce/8jGpPYNO4Ve+4NBw==", + "license": "MIT", + "dependencies": { + "@better-auth/utils": "^0.3.1", + "@better-fetch/fetch": "^1.1.21", + "rou3": "^0.7.12", + "set-cookie-parser": "^3.0.1" + }, + "peerDependencies": { + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/better-sqlite3": { "version": "12.6.2", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.6.2.tgz", @@ -5244,9 +5294,9 @@ } }, "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.0.1.tgz", + "integrity": "sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==", "license": "MIT" }, "node_modules/sharp": { diff --git a/package.json b/package.json index f14b054..8f910df 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,10 @@ "build": "next build", "start": "next start", "db:push": "drizzle-kit push", - "auth:push": "auth migrate" + "auth:push": "auth generate" }, "dependencies": { + "@better-auth/drizzle-adapter": "^1.5.4", "@libsql/client": "^0.17.0", "@tailwindcss/postcss": "^4.2.1", "auth": "^1.5.3", diff --git a/src/app/actions/get-dataset-entries.ts b/src/app/actions/get-dataset-entries.ts index 4991843..3179424 100644 --- a/src/app/actions/get-dataset-entries.ts +++ b/src/app/actions/get-dataset-entries.ts @@ -11,7 +11,6 @@ interface FilterParams { modelName?: string; dialect?: string; iteration?: number; - utteranceId?: string; } export async function getDatasetEntries( @@ -40,10 +39,6 @@ export async function getDatasetEntries( conditions.push(eq(dataset_entry.iteration, filters.iteration)); } - if (filters?.utteranceId) { - conditions.push(eq(dataset_entry.utteranceId, filters.utteranceId)); - } - const whereClause = and(...conditions); const entries = await db diff --git a/src/app/actions/get-filter-options.ts b/src/app/actions/get-filter-options.ts index fcb0ac0..7a4a64e 100644 --- a/src/app/actions/get-filter-options.ts +++ b/src/app/actions/get-filter-options.ts @@ -15,13 +15,11 @@ export async function getFilterOptions(datasetId: number) { const modelNames = [...new Set(entries.map(e => e.modelName))].sort(); const dialects = [...new Set(entries.map(e => e.dialect))].sort(); const iterations = [...new Set(entries.map(e => e.iteration))].sort((a, b) => a - b); - const utteranceIds = [...new Set(entries.map(e => e.utteranceId).filter(Boolean))].sort(); return { speakerIds, modelNames, dialects, iterations, - utteranceIds, }; } diff --git a/src/app/actions/upload-utterances.ts b/src/app/actions/upload-utterances.ts deleted file mode 100644 index 6f1693b..0000000 --- a/src/app/actions/upload-utterances.ts +++ /dev/null @@ -1,119 +0,0 @@ -'use server'; - -import db from '@/lib/db'; -import { dataset_utterance } from '@/lib/model/utterance'; -import { eq, and } from 'drizzle-orm'; -import { revalidatePath } from 'next/cache'; - -export async function uploadDatasetUtterances( - datasetId: number, - formData: FormData -) { - const file = formData.get('file') as File; - - if (!file) { - throw new Error('No file provided'); - } - - if (!file.name.endsWith('.csv')) { - throw new Error('File must be a CSV file'); - } - - try { - const csvContent = await file.text(); - const lines = csvContent.split('\n').filter((line) => line.trim()); - - if (lines.length < 2) { - throw new Error('CSV is empty or invalid'); - } - - // Parse CSV header - const headers = lines[0].split(',').map((h) => h.trim()); - - if (!headers.includes('id') || !headers.includes('text')) { - throw new Error('CSV must have "id" and "text" columns'); - } - - // Get indices of id and text columns - const idIndex = headers.indexOf('id'); - const textIndex = headers.indexOf('text'); - - // Parse utterances - const utterances: typeof dataset_utterance.$inferInsert[] = []; - - for (let i = 1; i < lines.length; i++) { - // Handle CSV parsing with quotes - const line = lines[i]; - const parts: string[] = []; - let current = ''; - let inQuotes = false; - - for (let j = 0; j < line.length; j++) { - const char = line[j]; - const nextChar = line[j + 1]; - - if (char === '"') { - if (inQuotes && nextChar === '"') { - current += '"'; - j++; // Skip next quote - } else { - inQuotes = !inQuotes; - } - } else if (char === ',' && !inQuotes) { - parts.push(current.trim()); - current = ''; - } else { - current += char; - } - } - parts.push(current.trim()); - - if (parts.length > Math.max(idIndex, textIndex)) { - const id = parts[idIndex]?.trim(); - const text = parts[textIndex]?.trim(); - - if (id && text) { - utterances.push({ - datasetId, - id, - text, - }); - } - } - } - - if (utterances.length === 0) { - throw new Error('No valid utterances found in CSV'); - } - - // Check for existing utterances to avoid duplicates - const existingUtterances = await db - .select({ id: dataset_utterance.id }) - .from(dataset_utterance) - .where(eq(dataset_utterance.datasetId, datasetId)); - - const existingIds = new Set(existingUtterances.map((u) => u.id)); - - // Filter out duplicates - const newUtterances = utterances.filter((u) => !existingIds.has(u.id)); - - if (newUtterances.length === 0) { - throw new Error('All utterances already exist in the dataset'); - } - - // Insert new utterances into database - await db.insert(dataset_utterance).values(newUtterances); - - revalidatePath(`/admin/datasets/${datasetId}`); - - return { - success: true, - utterancesCreated: newUtterances.length, - }; - } catch (error) { - console.error('Error uploading utterances:', error); - throw new Error( - error instanceof Error ? error.message : 'Failed to upload utterances' - ); - } -} diff --git a/src/app/actions/upload.ts b/src/app/actions/upload.ts index 586964f..e7caa2f 100644 --- a/src/app/actions/upload.ts +++ b/src/app/actions/upload.ts @@ -2,7 +2,6 @@ import db from '@/lib/db'; import { dataset_entry } from '@/lib/model/dataset_entry'; -import { dataset_utterance } from '@/lib/model/utterance'; import { revalidatePath } from 'next/cache'; import { eq } from 'drizzle-orm'; import { writeFile, mkdir, readFile, rm, readdir } from 'fs/promises'; @@ -14,7 +13,6 @@ interface DatasetEntryInput { id: string; audio_file: string; duration_ms: string; - utt_id: string; speaker: string; model: string; dialect: string; @@ -104,7 +102,6 @@ export async function uploadDatasetEntries( speakerId: row.speaker, modelName: row.model, fileName: relativePath, - utteranceId: row.utt_id || undefined, dialect: row.dialect, iteration: parseInt(row.iteration, 10), }); diff --git a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx index 0cf30a2..01e7131 100644 --- a/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx +++ b/src/app/admin/datasets/[id]/entries/[entryId]/page.tsx @@ -1,7 +1,6 @@ import Link from 'next/link'; import db from '@/lib/db'; import { dataset_entry } from '@/lib/model/dataset_entry'; -import { dataset_utterance } from '@/lib/model/utterance'; import { eq } from 'drizzle-orm'; import AudioPlayer from '@/components/AudioPlayer'; @@ -42,17 +41,6 @@ export default async function DatasetEntryPage({ params }: DatasetEntryPageProps const entry = entries[0]; - // Fetch utterance if utteranceId is present - let utterance = null; - if (entry.utteranceId) { - const utterances = await db - .select() - .from(dataset_utterance) - .where(eq(dataset_utterance.id, entry.utteranceId)); - - utterance = utterances.length > 0 ? utterances[0] : null; - } - return (
Utterance {entry.utteranceId}
-{utterance.text}
-Created
diff --git a/src/app/admin/datasets/[id]/page.tsx b/src/app/admin/datasets/[id]/page.tsx index a1efd2c..7ea8918 100644 --- a/src/app/admin/datasets/[id]/page.tsx +++ b/src/app/admin/datasets/[id]/page.tsx @@ -5,7 +5,6 @@ import { eq } from 'drizzle-orm'; import EditableDatasetHeader from '@/components/EditableDatasetHeader'; import DatasetEntriesList from '@/components/DatasetEntriesList'; import UploadDatasetEntriesModal from '@/components/UploadDatasetEntriesModal'; -import UploadUtterancesModal from '@/components/UploadUtterancesModal'; import RemoveAllEntriesButton from '@/components/RemoveAllEntriesButton'; interface DatasetPageProps { @@ -57,7 +56,6 @@ export default async function DatasetPage({ params }: DatasetPageProps) {