From 890f4f0958778f7254f2bfa4ca9b992343422bb0 Mon Sep 17 00:00:00 2001 From: AG Date: Sat, 6 Dec 2025 16:14:28 +0200 Subject: [PATCH] Bug fixes and refactoring --- playwright-report/index.html | 2 +- playwright.config.ts | 5 + server/package-lock.json | 125 +++++++++++----------- server/package.json | 2 +- server/prisma/prisma/dev.db | Bin 0 -> 106496 bytes server/prisma/prisma/dev.db-journal | Bin 0 -> 25136 bytes server/prisma/schema.migrate.prisma | 118 ++++++++++++++++++++ server/src/index.ts | 1 + server/src/lib/prisma.ts | 3 +- server/src/routes/auth.ts | 1 + server/src/scripts/backupSporadicSets.ts | 26 ----- server/src/scripts/migratePlans.ts | 42 -------- server/src/scripts/restoreSporadicSets.ts | 77 ------------- 13 files changed, 188 insertions(+), 214 deletions(-) create mode 100644 server/prisma/prisma/dev.db create mode 100644 server/prisma/prisma/dev.db-journal create mode 100644 server/prisma/schema.migrate.prisma delete mode 100644 server/src/scripts/backupSporadicSets.ts delete mode 100644 server/src/scripts/migratePlans.ts delete mode 100644 server/src/scripts/restoreSporadicSets.ts diff --git a/playwright-report/index.html b/playwright-report/index.html index eaab490..3733c2b 100644 --- a/playwright-report/index.html +++ b/playwright-report/index.html @@ -82,4 +82,4 @@ Error generating stack: `+n.message+`
- \ No newline at end of file + \ No newline at end of file diff --git a/playwright.config.ts b/playwright.config.ts index 2249545..3aa4bac 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -11,6 +11,11 @@ export default defineConfig({ baseURL: 'http://localhost:3000', trace: 'on-first-retry', }, + webServer: { + command: 'npm run dev:full', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + }, projects: [ { name: 'chromium', diff --git a/server/package-lock.json b/server/package-lock.json index e4afd7f..a5d0347 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -13,7 +13,7 @@ "@prisma/client": "^7.1.0", "@types/better-sqlite3": "^7.6.13", "bcryptjs": "3.0.3", - "better-sqlite3": "^12.5.0", + "better-sqlite3": "^11.0.0", "cors": "2.8.5", "dotenv": "17.2.3", "express": "5.1.0", @@ -182,6 +182,20 @@ "better-sqlite3": "^12.4.5" } }, + "node_modules/@prisma/adapter-better-sqlite3/node_modules/better-sqlite3": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz", + "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x" + } + }, "node_modules/@prisma/client": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.1.0.tgz", @@ -434,15 +448,15 @@ } }, "node_modules/@types/express": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.5.tgz", - "integrity": "sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "dev": true, "license": "MIT", "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^1" + "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { @@ -476,13 +490,6 @@ "@types/node": "*" } }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -535,25 +542,13 @@ } }, "node_modules/@types/serve-static": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz", - "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "dev": true, "license": "MIT", "dependencies": { "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "<1" - } - }, - "node_modules/@types/serve-static/node_modules/@types/send": { - "version": "0.17.6", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz", - "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", "@types/node": "*" } }, @@ -671,17 +666,14 @@ } }, "node_modules/better-sqlite3": { - "version": "12.5.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.5.0.tgz", - "integrity": "sha512-WwCZ/5Diz7rsF29o27o0Gcc1Du+l7Zsv7SYtVPG0X3G/uUI1LqdxrQI7c9Hs2FWpqXXERjW9hp6g3/tH7DlVKg==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", + "integrity": "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" - }, - "engines": { - "node": "20.x || 22.x || 23.x || 24.x || 25.x" } }, "node_modules/binary-extensions": { @@ -1404,9 +1396,9 @@ } }, "node_modules/finalhandler": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", - "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "dependencies": { "debug": "^4.4.0", @@ -1417,7 +1409,11 @@ "statuses": "^2.0.1" }, "engines": { - "node": ">= 0.8" + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/foreground-child": { @@ -1672,28 +1668,23 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-status-codes": { @@ -2046,15 +2037,19 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-response": { @@ -2536,15 +2531,15 @@ } }, "node_modules/raw-body": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", - "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.7.0", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" diff --git a/server/package.json b/server/package.json index cb94432..5176134 100644 --- a/server/package.json +++ b/server/package.json @@ -14,7 +14,7 @@ "@prisma/client": "^7.1.0", "@types/better-sqlite3": "^7.6.13", "bcryptjs": "3.0.3", - "better-sqlite3": "^12.5.0", + "better-sqlite3": "^11.0.0", "cors": "2.8.5", "dotenv": "17.2.3", "express": "5.1.0", diff --git a/server/prisma/prisma/dev.db b/server/prisma/prisma/dev.db new file mode 100644 index 0000000000000000000000000000000000000000..2e64a8146ca82740185b9b02d52eba15b213b08c GIT binary patch literal 106496 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 literal 0 HcmV?d00001 diff --git a/server/prisma/prisma/dev.db-journal b/server/prisma/prisma/dev.db-journal new file mode 100644 index 0000000000000000000000000000000000000000..c260195cc857e3222833db9685452993a69b3952 GIT binary patch literal 25136 zcmeI3PmJ4C9>p< z-sk&!|9;bEGI0ID39PJB0IM6f00e*l5IB5h zwa`6W&)msm*3M{xtXP7kl|>2Ta>dbgwr~|mvWR6z(urtUj-ynN>{N83sRYTErbw!)R!LPr8WJRth$68nakJkr6Dy)F z3KiWP4P58G8IUlDv;TScY&N#)c4QS%u_9C~v5d6}DpyqGlr0sZvSiB|ab(>_ShBFK zT12i?szeYaMaIOk)cs*D7?s;RmzTb3%=ilf_-)-m`_?RvxDi$@tn{%V1zSJA~qzGJ-6;hQ`3 z*KIWT-PZQ4TI&w~s&VJ}ahZSwf$I&nXN$7zWZqbm@A;VcMSi>4F>V^Iv>{geUE=QT zhee(m+F(gO83t*2mWJIRL<5`LB6PIT+==3NqdLx194?4Ld3ts?=-0;4TB0*ueIy7Eh9?l?rufWv6Q9894TjC82TxTEAGx zarKLfv`!5Xlc%%q2l+gA`EvGQIo3Dwu3g`A+#Z>DJUy-QM9!}kdRDz-7K;7(@wZ3E zY~=n&Bdch+ez?CycWFlhovBR6;oc~Af_Mo`R)Z^KaAkf}PW0&nWKvEq+Y?OMB2K)V zd9b>c=gQ^mLp>JjXp(Bt#nH#J@40E&X0!h<+Rf^Ur<6%^(;->!w&?QNF{25(-KpK$ zW#UEk+98uVtv0XQY^P2@yq9x1u3cU<#^wwXXH)X;2djDR!iDU^?R3mVDxaE`D@uAK ziAW#0y_x+Uqd7!D@GkX`<8ytlH#?`M`dsO@jTRd(MK`$N`a#(6_T0geX>ek{4$=l9wkiM8x7)~CP$ENvrYHv##YKObsV8k zs;}g@+u|ZGm>13z=hIfl$H^zpHon;WFMVDX2jFOa6bDGI=>1XS035^t5`4eoJH!DX z4ghfg8tY<5ONax+kv~S`1oo{E2S|>95C>p?3xYU6w5^?t)kNn2763VcAqjDSWJoSL z(?J{n;s7(@`}8=s$Y&r9Kqs{#!~sq$4)E<=_e***Js9Bf00e*l5C8%|;PEH$)t|rn%TLiBvaSFC literal 0 HcmV?d00001 diff --git a/server/prisma/schema.migrate.prisma b/server/prisma/schema.migrate.prisma new file mode 100644 index 0000000..26c5cd4 --- /dev/null +++ b/server/prisma/schema.migrate.prisma @@ -0,0 +1,118 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + + + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(uuid()) + email String @unique + password String + role String @default("USER") // USER, ADMIN + isFirstLogin Boolean @default(true) + isBlocked Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + profile UserProfile? + sessions WorkoutSession[] + exercises Exercise[] + plans WorkoutPlan[] + weightRecords BodyWeightRecord[] +} + +model BodyWeightRecord { + id String @id @default(uuid()) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + weight Float + date DateTime @default(now()) + dateStr String // YYYY-MM-DD for unique constraint + + @@unique([userId, dateStr]) +} + +model UserProfile { + id String @id @default(uuid()) + userId String @unique + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + weight Float? + height Float? + gender String? + birthDate DateTime? + language String? @default("en") +} + +model Exercise { + id String @id @default(uuid()) + userId String? // Null means system default + user User? @relation(fields: [userId], references: [id], onDelete: Cascade) + name String + type String // STRENGTH, CARDIO, BODYWEIGHT, STATIC + bodyWeightPercentage Float? @default(0) + isArchived Boolean @default(false) + isUnilateral Boolean @default(false) + + sets WorkoutSet[] + planExercises PlanExercise[] +} + +model WorkoutSession { + id String @id @default(uuid()) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + startTime DateTime + endTime DateTime? + userBodyWeight Float? + note String? + planId String? + planName String? + type String @default("STANDARD") // STANDARD, QUICK_LOG + + sets WorkoutSet[] +} + +model WorkoutSet { + id String @id @default(uuid()) + sessionId String + session WorkoutSession @relation(fields: [sessionId], references: [id], onDelete: Cascade) + exerciseId String + exercise Exercise @relation(fields: [exerciseId], references: [id]) + + order Int + weight Float? + reps Int? + distanceMeters Float? + durationSeconds Int? + height Float? + bodyWeightPercentage Float? + completed Boolean @default(true) + side String? // LEFT, RIGHT, or null for bilateral + timestamp DateTime @default(now()) +} + +model WorkoutPlan { + id String @id @default(uuid()) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + name String + description String? + exercises String? // JSON string of exercise IDs (Deprecated, to be removed) + planExercises PlanExercise[] + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model PlanExercise { + id String @id @default(uuid()) + planId String + plan WorkoutPlan @relation(fields: [planId], references: [id], onDelete: Cascade) + exerciseId String + exercise Exercise @relation(fields: [exerciseId], references: [id]) + order Int + isWeighted Boolean @default(false) +} diff --git a/server/src/index.ts b/server/src/index.ts index e02c2a7..212991d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -1,3 +1,4 @@ +import 'dotenv/config'; import express from 'express'; import cors from 'cors'; import authRoutes from './routes/auth'; diff --git a/server/src/lib/prisma.ts b/server/src/lib/prisma.ts index f9e70d2..849982e 100644 --- a/server/src/lib/prisma.ts +++ b/server/src/lib/prisma.ts @@ -1,7 +1,5 @@ import { PrismaClient } from '@prisma/client'; import { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3'; -import BetterSqlite3 from 'better-sqlite3'; -import path from 'path'; // Ensure env vars are loaded import 'dotenv/config'; @@ -18,6 +16,7 @@ if (!dbUrl) { throw new Error("DATABASE_URL environment variable is not set. Please check your .env file."); } +console.log('Initializing Prisma Adapter with URL:', dbUrl); const adapter = new PrismaBetterSqlite3({ url: dbUrl }); const prisma = diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 75efb7d..7d7e770 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -57,6 +57,7 @@ router.post('/login', async (req, res) => { res.json({ success: true, user: userSafe, token }); } catch (error) { + console.error('Login error:', error); res.status(500).json({ error: 'Server error' }); } }); diff --git a/server/src/scripts/backupSporadicSets.ts b/server/src/scripts/backupSporadicSets.ts deleted file mode 100644 index 5096763..0000000 --- a/server/src/scripts/backupSporadicSets.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import fs from 'fs'; -import path from 'path'; - -const prisma = new PrismaClient(); - -async function backup() { - try { - console.log('Starting backup...'); - // Backup Quick Log sessions and their sets (formerly SporadicSets) - const quickLogSessions = await prisma.workoutSession.findMany({ - where: { type: 'QUICK_LOG' }, - include: { sets: true } - }); - - const backupPath = path.join(__dirname, '../../sporadic_backup.json'); - fs.writeFileSync(backupPath, JSON.stringify(quickLogSessions, null, 2)); - console.log(`Backed up ${quickLogSessions.length} quick log sessions to ${backupPath}`); - } catch (error) { - console.error('Backup failed:', error); - } finally { - await prisma.$disconnect(); - } -} - -backup(); diff --git a/server/src/scripts/migratePlans.ts b/server/src/scripts/migratePlans.ts deleted file mode 100644 index 40d59e6..0000000 --- a/server/src/scripts/migratePlans.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { PrismaClient } from '@prisma/client'; - -const prisma = new PrismaClient(); - -async function migrate() { - console.log('Starting migration...'); - const plans = await prisma.workoutPlan.findMany(); - console.log(`Found ${plans.length} plans.`); - - for (const plan of plans) { - if (plan.exercises) { - try { - const steps = JSON.parse(plan.exercises); - console.log(`Migrating plan ${plan.name} (${plan.id}) with ${steps.length} steps.`); - - let order = 0; - for (const step of steps) { - await prisma.planExercise.create({ - data: { - planId: plan.id, - exerciseId: step.exerciseId, - order: order++, - isWeighted: step.isWeighted || false - } - }); - } - } catch (e) { - console.error(`Error parsing JSON for plan ${plan.id}:`, e); - } - } - } - console.log('Migration complete.'); -} - -migrate() - .catch((e) => { - console.error(e); - process.exit(1); - }) - .finally(async () => { - await prisma.$disconnect(); - }); diff --git a/server/src/scripts/restoreSporadicSets.ts b/server/src/scripts/restoreSporadicSets.ts deleted file mode 100644 index c85726d..0000000 --- a/server/src/scripts/restoreSporadicSets.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import fs from 'fs'; -import path from 'path'; - -const prisma = new PrismaClient(); - -async function restore() { - try { - const backupPath = path.join(__dirname, '../../sporadic_backup.json'); - if (!fs.existsSync(backupPath)) { - console.error('Backup file not found!'); - return; - } - - const sporadicSets = JSON.parse(fs.readFileSync(backupPath, 'utf-8')); - console.log(`Found ${sporadicSets.length} sporadic sets to restore.`); - - for (const set of sporadicSets) { - const date = new Date(set.timestamp); - const startOfDay = new Date(date); - startOfDay.setHours(0, 0, 0, 0); - const endOfDay = new Date(date); - endOfDay.setHours(23, 59, 59, 999); - - // Find or create a QUICK_LOG session for this day - let session = await prisma.workoutSession.findFirst({ - where: { - userId: set.userId, - type: 'QUICK_LOG', - startTime: { - gte: startOfDay, - lte: endOfDay - } - } - }); - - if (!session) { - session = await prisma.workoutSession.create({ - data: { - userId: set.userId, - startTime: startOfDay, // Use start of day as session start - type: 'QUICK_LOG', - note: 'Daily Quick Log' - } - }); - console.log(`Created new QUICK_LOG session for ${startOfDay.toISOString()}`); - } - - // Create the WorkoutSet - await prisma.workoutSet.create({ - data: { - sessionId: session.id, - exerciseId: set.exerciseId, - order: 0, // Order doesn't matter much for sporadic sets, or we could increment - weight: set.weight, - reps: set.reps, - distanceMeters: set.distanceMeters, - durationSeconds: set.durationSeconds, - height: set.height, - bodyWeightPercentage: set.bodyWeightPercentage, - side: set.side, - timestamp: new Date(set.timestamp), - completed: true - } - }); - } - - console.log('Restoration complete!'); - - } catch (error) { - console.error('Restoration failed:', error); - } finally { - await prisma.$disconnect(); - } -} - -restore();