feat: migrate to postgres, add migrations

This commit is contained in:
Matej Stieranka 2025-06-27 20:10:08 +02:00
parent 1d06466f16
commit 8841442191
16 changed files with 360 additions and 451 deletions

1
.npmrc
View file

@ -1 +0,0 @@
public-hoist-pattern[]=*@libsql*

View file

@ -24,8 +24,8 @@ COPY . .
# Uncomment the following line in case you want to disable telemetry during the build. # Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
ARG DB_FILE_NAME ENV DB_DRIVER="pglite"
ENV DB_FILE_NAME=${DB_FILE_NAME} ENV DB_LOCATION="./data"
RUN corepack enable pnpm && pnpm run build RUN corepack enable pnpm && pnpm run build
@ -47,6 +47,10 @@ COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
RUN mkdir -p /app/data
COPY --from=builder --chown=nextjs:nodejs /app/data ./data
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./
USER nextjs USER nextjs
EXPOSE 3000 EXPOSE 3000

View file

@ -4,16 +4,24 @@ services:
build: build:
context: . context: .
args: args:
DB_FILE_NAME: ${DB_FILE_NAME} DB_LOCATION: ${DB_LOCATION}
environment: environment:
AUTH_SECRET: ${AUTH_SECRET} AUTH_SECRET: ${AUTH_SECRET}
AUTH_GITHUB_ID: ${AUTH_GITHUB_ID} AUTH_GITHUB_ID: ${AUTH_GITHUB_ID}
AUTH_GITHUB_SECRET: ${AUTH_GITHUB_SECRET} AUTH_GITHUB_SECRET: ${AUTH_GITHUB_SECRET}
DB_FILE_NAME: ${DB_FILE_NAME} DB_DRIVER: ${DB_DRIVER}
DB_LOCATION: ${DB_LOCATION}
ports: ports:
- 3000:3000 - 3000:3000
restart: unless-stopped restart: unless-stopped
volumes: postgres:
- type: bind container_name: inffo2-postgres
source: ./data/data.db hostname: inffo2-postgres
target: /app/data.db image: postgres:15
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports:
- 5432:5432
restart: unless-stopped

View file

@ -1,11 +1,16 @@
import 'dotenv/config'; import "dotenv/config";
import { defineConfig } from 'drizzle-kit'; import { defineConfig } from "drizzle-kit";
if (!process.env.DB_LOCATION) {
throw new Error("DB_LOCATION environment variable is not set");
}
export default defineConfig({ export default defineConfig({
out: './drizzle', out: "./drizzle",
schema: './src/db/schema.ts', schema: "./src/db/schema.ts",
dialect: 'sqlite', dialect: "postgresql",
dbCredentials: { ...(process.env.DB_DRIVER === "pglite" ? { driver: "pglite" } : {}),
url: process.env.DB_FILE_NAME!, dbCredentials: {
}, url: process.env.DB_LOCATION,
},
}); });

View file

@ -1,17 +0,0 @@
CREATE TABLE `eventsTable` (
`id` integer PRIMARY KEY NOT NULL,
`startTime` text NOT NULL,
`endTime` text NOT NULL,
`name` text NOT NULL,
`title` text NOT NULL,
`description` text NOT NULL,
`type` text NOT NULL,
`lineId` integer NOT NULL,
FOREIGN KEY (`lineId`) REFERENCES `linesTable`(`id`) ON UPDATE cascade ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `linesTable` (
`id` integer PRIMARY KEY NOT NULL,
`name` text NOT NULL,
`description` text NOT NULL
);

View file

@ -0,0 +1,18 @@
CREATE TABLE "eventsTable" (
"id" integer PRIMARY KEY NOT NULL,
"startTime" timestamp NOT NULL,
"endTime" timestamp NOT NULL,
"name" text NOT NULL,
"title" text NOT NULL,
"description" text NOT NULL,
"type" text NOT NULL,
"lineId" integer NOT NULL
);
--> statement-breakpoint
CREATE TABLE "linesTable" (
"id" integer PRIMARY KEY NOT NULL,
"name" text NOT NULL,
"description" text NOT NULL
);
--> statement-breakpoint
ALTER TABLE "eventsTable" ADD CONSTRAINT "eventsTable_lineId_linesTable_id_fk" FOREIGN KEY ("lineId") REFERENCES "public"."linesTable"("id") ON DELETE cascade ON UPDATE cascade;

View file

@ -1,67 +1,60 @@
{ {
"version": "6", "id": "c0102a7e-5d09-41ca-88d1-c66f2f299e65",
"dialect": "sqlite",
"id": "67a86f93-2bc8-4fe2-8786-995874bf9863",
"prevId": "00000000-0000-0000-0000-000000000000", "prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": { "tables": {
"eventsTable": { "public.eventsTable": {
"name": "eventsTable", "name": "eventsTable",
"schema": "",
"columns": { "columns": {
"id": { "id": {
"name": "id", "name": "id",
"type": "integer", "type": "integer",
"primaryKey": true, "primaryKey": true,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"startTime": { "startTime": {
"name": "startTime", "name": "startTime",
"type": "text", "type": "timestamp",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"endTime": { "endTime": {
"name": "endTime", "name": "endTime",
"type": "text", "type": "timestamp",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"name": { "name": {
"name": "name", "name": "name",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"title": { "title": {
"name": "title", "name": "title",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"description": { "description": {
"name": "description", "name": "description",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"type": { "type": {
"name": "type", "name": "type",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"lineId": { "lineId": {
"name": "lineId", "name": "lineId",
"type": "integer", "type": "integer",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
} }
}, },
"indexes": {}, "indexes": {},
@ -82,48 +75,51 @@
}, },
"compositePrimaryKeys": {}, "compositePrimaryKeys": {},
"uniqueConstraints": {}, "uniqueConstraints": {},
"checkConstraints": {} "policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}, },
"linesTable": { "public.linesTable": {
"name": "linesTable", "name": "linesTable",
"schema": "",
"columns": { "columns": {
"id": { "id": {
"name": "id", "name": "id",
"type": "integer", "type": "integer",
"primaryKey": true, "primaryKey": true,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"name": { "name": {
"name": "name", "name": "name",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
}, },
"description": { "description": {
"name": "description", "name": "description",
"type": "text", "type": "text",
"primaryKey": false, "primaryKey": false,
"notNull": true, "notNull": true
"autoincrement": false
} }
}, },
"indexes": {}, "indexes": {},
"foreignKeys": {}, "foreignKeys": {},
"compositePrimaryKeys": {}, "compositePrimaryKeys": {},
"uniqueConstraints": {}, "uniqueConstraints": {},
"checkConstraints": {} "policies": {},
"checkConstraints": {},
"isRLSEnabled": false
} }
}, },
"views": {},
"enums": {}, "enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": { "_meta": {
"columns": {},
"schemas": {}, "schemas": {},
"tables": {}, "tables": {}
"columns": {}
},
"internal": {
"indexes": {}
} }
} }

View file

@ -1,12 +1,12 @@
{ {
"version": "7", "version": "7",
"dialect": "sqlite", "dialect": "postgresql",
"entries": [ "entries": [
{ {
"idx": 0, "idx": 0,
"version": "6", "version": "7",
"when": 1750781935205, "when": 1750786732013,
"tag": "0000_low_firebird", "tag": "0000_noisy_omega_red",
"breakpoints": true "breakpoints": true
} }
] ]

View file

@ -2,11 +2,9 @@ import type { NextConfig } from "next";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
output: "standalone", output: "standalone",
serverExternalPackages: ["@electric-sql/pglite"],
outputFileTracingIncludes: { outputFileTracingIncludes: {
"./**/*": [ "**/*": ["./drizzle/**/*"],
"./node_modules/@libsql/darwin*/**/*",
"./node_modules/@libsql/linux*/**/*",
],
}, },
}; };

View file

@ -1,50 +1,55 @@
{ {
"name": "inffo2", "name": "inffo2",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"packageManager": "pnpm@8.12.0", "packageManager": "pnpm@8.12.0",
"scripts": { "scripts": {
"dev": "next dev --turbopack", "dev": "next dev --turbopack",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",
"push-db": "drizzle-kit push" "push-db": "drizzle-kit push"
}, },
"dependencies": { "dependencies": {
"@libsql/client": "^0.15.9", "@electric-sql/pglite": "^0.3.3",
"@radix-ui/react-collapsible": "^1.1.11", "@radix-ui/react-collapsible": "^1.1.11",
"@radix-ui/react-dialog": "^1.1.14", "@radix-ui/react-dialog": "^1.1.14",
"@radix-ui/react-dropdown-menu": "^2.1.15", "@radix-ui/react-dropdown-menu": "^2.1.15",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-select": "^2.2.5", "@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-tooltip": "^1.2.7", "@radix-ui/react-tooltip": "^1.2.7",
"cheerio": "^1.1.0", "cheerio": "^1.1.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"dotenv": "^16.5.0", "dotenv": "^16.5.0",
"drizzle-orm": "^0.44.2", "drizzle-orm": "^0.44.2",
"drizzle-zod": "^0.8.2", "drizzle-zod": "^0.8.2",
"lucide-react": "^0.522.0", "lucide-react": "^0.522.0",
"next": "15.3.4", "next": "15.3.4",
"next-auth": "5.0.0-beta.29", "next-auth": "5.0.0-beta.29",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"react": "^19.0.0", "pg": "^8.16.2",
"react-dom": "^19.0.0", "react": "^19.0.0",
"tailwind-merge": "^3.3.1", "react-dom": "^19.0.0",
"zod": "^3.25.67" "tailwind-merge": "^3.3.1",
}, "zod": "^3.25.67"
"devDependencies": { },
"@biomejs/biome": "2.0.5", "devDependencies": {
"@tailwindcss/postcss": "^4", "@biomejs/biome": "2.0.5",
"@types/node": "^20", "@tailwindcss/postcss": "^4",
"@types/react": "^19", "@types/node": "^20",
"@types/react-dom": "^19", "@types/pg": "^8.15.4",
"drizzle-kit": "^0.31.2", "@types/react": "^19",
"tailwindcss": "^4", "@types/react-dom": "^19",
"tsx": "^4.20.3", "drizzle-kit": "^0.31.2",
"tw-animate-css": "^1.3.4", "tailwindcss": "^4",
"typescript": "^5" "tsx": "^4.20.3",
} "tw-animate-css": "^1.3.4",
"typescript": "^5"
},
"browser": {
"crypto": false
}
} }

330
pnpm-lock.yaml generated
View file

@ -5,9 +5,9 @@ settings:
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
dependencies: dependencies:
'@libsql/client': '@electric-sql/pglite':
specifier: ^0.15.9 specifier: ^0.3.3
version: 0.15.9 version: 0.3.3
'@radix-ui/react-collapsible': '@radix-ui/react-collapsible':
specifier: ^1.1.11 specifier: ^1.1.11
version: 1.1.11(@types/react-dom@19.1.6)(@types/react@19.1.8)(react-dom@19.1.0)(react@19.1.0) version: 1.1.11(@types/react-dom@19.1.6)(@types/react@19.1.8)(react-dom@19.1.0)(react@19.1.0)
@ -46,7 +46,7 @@ dependencies:
version: 16.5.0 version: 16.5.0
drizzle-orm: drizzle-orm:
specifier: ^0.44.2 specifier: ^0.44.2
version: 0.44.2(@libsql/client@0.15.9) version: 0.44.2(@electric-sql/pglite@0.3.3)(@types/pg@8.15.4)(pg@8.16.2)
drizzle-zod: drizzle-zod:
specifier: ^0.8.2 specifier: ^0.8.2
version: 0.8.2(drizzle-orm@0.44.2)(zod@3.25.67) version: 0.8.2(drizzle-orm@0.44.2)(zod@3.25.67)
@ -62,6 +62,9 @@ dependencies:
next-themes: next-themes:
specifier: ^0.4.6 specifier: ^0.4.6
version: 0.4.6(react-dom@19.1.0)(react@19.1.0) version: 0.4.6(react-dom@19.1.0)(react@19.1.0)
pg:
specifier: ^8.16.2
version: 8.16.2
react: react:
specifier: ^19.0.0 specifier: ^19.0.0
version: 19.1.0 version: 19.1.0
@ -85,6 +88,9 @@ devDependencies:
'@types/node': '@types/node':
specifier: ^20 specifier: ^20
version: 20.19.1 version: 20.19.1
'@types/pg':
specifier: ^8.15.4
version: 8.15.4
'@types/react': '@types/react':
specifier: ^19 specifier: ^19
version: 19.1.8 version: 19.1.8
@ -234,6 +240,10 @@ packages:
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==} resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
dev: true dev: true
/@electric-sql/pglite@0.3.3:
resolution: {integrity: sha512-JrvHOx9q0yvKEby0bK8qzGTVw6K+yEg8enxDWb2IwNKr5XZxRrBb+GNIqoAIP7yXyhRg5jcENWmdHmtnAT87vA==}
dev: false
/@emnapi/runtime@1.4.3: /@emnapi/runtime@1.4.3:
resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==}
requiresBuild: true requiresBuild: true
@ -943,128 +953,6 @@ packages:
'@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/sourcemap-codec': 1.5.0
dev: true dev: true
/@libsql/client@0.15.9:
resolution: {integrity: sha512-VT3do0a0vwYVaNcp/y05ikkKS3OrFR5UeEf5SUuYZVgKVl1Nc1k9ajoYSsOid8AD/vlhLDB5yFQaV4HmT/OB9w==}
dependencies:
'@libsql/core': 0.15.9
'@libsql/hrana-client': 0.7.0
js-base64: 3.7.7
libsql: 0.5.13
promise-limit: 2.7.0
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false
/@libsql/core@0.15.9:
resolution: {integrity: sha512-4OVdeAmuaCUq5hYT8NNn0nxlO9AcA/eTjXfUZ+QK8MT3Dz7Z76m73x7KxjU6I64WyXX98dauVH2b9XM+d84npw==}
dependencies:
js-base64: 3.7.7
dev: false
/@libsql/darwin-arm64@0.5.13:
resolution: {integrity: sha512-ASz/EAMLDLx3oq9PVvZ4zBXXHbz2TxtxUwX2xpTRFR4V4uSHAN07+jpLu3aK5HUBLuv58z7+GjaL5w/cyjR28Q==}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@libsql/darwin-x64@0.5.13:
resolution: {integrity: sha512-kzglniv1difkq8opusSXM7u9H0WoEPeKxw0ixIfcGfvlCVMJ+t9UNtXmyNHW68ljdllje6a4C6c94iPmIYafYA==}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: false
optional: true
/@libsql/hrana-client@0.7.0:
resolution: {integrity: sha512-OF8fFQSkbL7vJY9rfuegK1R7sPgQ6kFMkDamiEccNUvieQ+3urzfDFI616oPl8V7T9zRmnTkSjMOImYCAVRVuw==}
dependencies:
'@libsql/isomorphic-fetch': 0.3.1
'@libsql/isomorphic-ws': 0.1.5
js-base64: 3.7.7
node-fetch: 3.3.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false
/@libsql/isomorphic-fetch@0.3.1:
resolution: {integrity: sha512-6kK3SUK5Uu56zPq/Las620n5aS9xJq+jMBcNSOmjhNf/MUvdyji4vrMTqD7ptY7/4/CAVEAYDeotUz60LNQHtw==}
engines: {node: '>=18.0.0'}
dev: false
/@libsql/isomorphic-ws@0.1.5:
resolution: {integrity: sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg==}
dependencies:
'@types/ws': 8.18.1
ws: 8.18.2
transitivePeerDependencies:
- bufferutil
- utf-8-validate
dev: false
/@libsql/linux-arm-gnueabihf@0.5.13:
resolution: {integrity: sha512-UEW+VZN2r0mFkfztKOS7cqfS8IemuekbjUXbXCwULHtusww2QNCXvM5KU9eJCNE419SZCb0qaEWYytcfka8qeA==}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/linux-arm-musleabihf@0.5.13:
resolution: {integrity: sha512-NMDgLqryYBv4Sr3WoO/m++XDjR5KLlw9r/JK4Ym6A1XBv2bxQQNhH0Lxx3bjLW8qqhBD4+0xfms4d2cOlexPyA==}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/linux-arm64-gnu@0.5.13:
resolution: {integrity: sha512-/wCxVdrwl1ee6D6LEjwl+w4SxuLm5UL9Kb1LD5n0bBGs0q+49ChdPPh7tp175iRgkcrTgl23emymvt1yj3KxVQ==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/linux-arm64-musl@0.5.13:
resolution: {integrity: sha512-xnVAbZIanUgX57XqeI5sNaDnVilp0Di5syCLSEo+bRyBobe/1IAeehNZpyVbCy91U2N6rH1C/mZU7jicVI9x+A==}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/linux-x64-gnu@0.5.13:
resolution: {integrity: sha512-/mfMRxcQAI9f8t7tU3QZyh25lXgXKzgin9B9TOSnchD73PWtsVhlyfA6qOCfjQl5kr4sHscdXD5Yb3KIoUgrpQ==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/linux-x64-musl@0.5.13:
resolution: {integrity: sha512-rdefPTpQCVwUjIQYbDLMv3qpd5MdrT0IeD0UZPGqhT9AWU8nJSQoj2lfyIDAWEz7PPOVCY4jHuEn7FS2sw9kRA==}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: false
optional: true
/@libsql/win32-x64-msvc@0.5.13:
resolution: {integrity: sha512-aNcmDrD1Ws+dNZIv9ECbxBQumqB9MlSVEykwfXJpqv/593nABb8Ttg5nAGUPtnADyaGDTrGvPPP81d/KsKho4Q==}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: false
optional: true
/@neon-rs/load@0.0.4:
resolution: {integrity: sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw==}
dev: false
/@next/env@15.3.4: /@next/env@15.3.4:
resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==} resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==}
dev: false dev: false
@ -1982,6 +1870,13 @@ packages:
dependencies: dependencies:
undici-types: 6.21.0 undici-types: 6.21.0
/@types/pg@8.15.4:
resolution: {integrity: sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==}
dependencies:
'@types/node': 20.19.1
pg-protocol: 1.10.2
pg-types: 2.2.0
/@types/react-dom@19.1.6(@types/react@19.1.8): /@types/react-dom@19.1.6(@types/react@19.1.8):
resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==}
peerDependencies: peerDependencies:
@ -1994,12 +1889,6 @@ packages:
dependencies: dependencies:
csstype: 3.1.3 csstype: 3.1.3
/@types/ws@8.18.1:
resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==}
dependencies:
'@types/node': 20.19.1
dev: false
/aria-hidden@1.2.6: /aria-hidden@1.2.6:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2126,11 +2015,6 @@ packages:
/csstype@3.1.3: /csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
/data-uri-to-buffer@4.0.1:
resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==}
engines: {node: '>= 12'}
dev: false
/debug@4.4.1: /debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@ -2143,11 +2027,6 @@ packages:
ms: 2.1.3 ms: 2.1.3
dev: true dev: true
/detect-libc@2.0.2:
resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
engines: {node: '>=8'}
dev: false
/detect-libc@2.0.4: /detect-libc@2.0.4:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@ -2200,7 +2079,7 @@ packages:
- supports-color - supports-color
dev: true dev: true
/drizzle-orm@0.44.2(@libsql/client@0.15.9): /drizzle-orm@0.44.2(@electric-sql/pglite@0.3.3)(@types/pg@8.15.4)(pg@8.16.2):
resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==} resolution: {integrity: sha512-zGAqBzWWkVSFjZpwPOrmCrgO++1kZ5H/rZ4qTGeGOe18iXGVJWf3WPfHOVwFIbmi8kHjfJstC6rJomzGx8g/dQ==}
peerDependencies: peerDependencies:
'@aws-sdk/client-rds-data': '>=3' '@aws-sdk/client-rds-data': '>=3'
@ -2292,7 +2171,9 @@ packages:
sqlite3: sqlite3:
optional: true optional: true
dependencies: dependencies:
'@libsql/client': 0.15.9 '@electric-sql/pglite': 0.3.3
'@types/pg': 8.15.4
pg: 8.16.2
dev: false dev: false
/drizzle-zod@0.8.2(drizzle-orm@0.44.2)(zod@3.25.67): /drizzle-zod@0.8.2(drizzle-orm@0.44.2)(zod@3.25.67):
@ -2301,7 +2182,7 @@ packages:
drizzle-orm: '>=0.36.0' drizzle-orm: '>=0.36.0'
zod: ^3.25.1 zod: ^3.25.1
dependencies: dependencies:
drizzle-orm: 0.44.2(@libsql/client@0.15.9) drizzle-orm: 0.44.2(@electric-sql/pglite@0.3.3)(@types/pg@8.15.4)(pg@8.16.2)
zod: 3.25.67 zod: 3.25.67
dev: false dev: false
@ -2404,21 +2285,6 @@ packages:
'@esbuild/win32-x64': 0.25.5 '@esbuild/win32-x64': 0.25.5
dev: true dev: true
/fetch-blob@3.2.0:
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
engines: {node: ^12.20 || >= 14.13}
dependencies:
node-domexception: 1.0.0
web-streams-polyfill: 3.3.3
dev: false
/formdata-polyfill@4.0.10:
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
engines: {node: '>=12.20.0'}
dependencies:
fetch-blob: 3.2.0
dev: false
/fsevents@2.3.3: /fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -2473,29 +2339,6 @@ packages:
resolution: {integrity: sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==} resolution: {integrity: sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==}
dev: false dev: false
/js-base64@3.7.7:
resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==}
dev: false
/libsql@0.5.13:
resolution: {integrity: sha512-5Bwoa/CqzgkTwySgqHA5TsaUDRrdLIbdM4egdPcaAnqO3aC+qAgS6BwdzuZwARA5digXwiskogZ8H7Yy4XfdOg==}
cpu: [x64, arm64, wasm32, arm]
os: [darwin, linux, win32]
dependencies:
'@neon-rs/load': 0.0.4
detect-libc: 2.0.2
optionalDependencies:
'@libsql/darwin-arm64': 0.5.13
'@libsql/darwin-x64': 0.5.13
'@libsql/linux-arm-gnueabihf': 0.5.13
'@libsql/linux-arm-musleabihf': 0.5.13
'@libsql/linux-arm64-gnu': 0.5.13
'@libsql/linux-arm64-musl': 0.5.13
'@libsql/linux-x64-gnu': 0.5.13
'@libsql/linux-x64-musl': 0.5.13
'@libsql/win32-x64-msvc': 0.5.13
dev: false
/lightningcss-darwin-arm64@1.30.1: /lightningcss-darwin-arm64@1.30.1:
resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==} resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
engines: {node: '>= 12.0.0'} engines: {node: '>= 12.0.0'}
@ -2721,21 +2564,6 @@ packages:
- babel-plugin-macros - babel-plugin-macros
dev: false dev: false
/node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
deprecated: Use your platform's native DOMException instead
dev: false
/node-fetch@3.3.2:
resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
dependencies:
data-uri-to-buffer: 4.0.1
fetch-blob: 3.2.0
formdata-polyfill: 4.0.10
dev: false
/nth-check@2.1.1: /nth-check@2.1.1:
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
dependencies: dependencies:
@ -2765,6 +2593,65 @@ packages:
entities: 6.0.1 entities: 6.0.1
dev: false dev: false
/pg-cloudflare@1.2.6:
resolution: {integrity: sha512-uxmJAnmIgmYgnSFzgOf2cqGQBzwnRYcrEgXuFjJNEkpedEIPBSEzxY7ph4uA9k1mI+l/GR0HjPNS6FKNZe8SBQ==}
requiresBuild: true
dev: false
optional: true
/pg-connection-string@2.9.1:
resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==}
dev: false
/pg-int8@1.0.1:
resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==}
engines: {node: '>=4.0.0'}
/pg-pool@3.10.1(pg@8.16.2):
resolution: {integrity: sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==}
peerDependencies:
pg: '>=8.0'
dependencies:
pg: 8.16.2
dev: false
/pg-protocol@1.10.2:
resolution: {integrity: sha512-Ci7jy8PbaWxfsck2dwZdERcDG2A0MG8JoQILs+uZNjABFuBuItAZCWUNz8sXRDMoui24rJw7WlXqgpMdBSN/vQ==}
/pg-types@2.2.0:
resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==}
engines: {node: '>=4'}
dependencies:
pg-int8: 1.0.1
postgres-array: 2.0.0
postgres-bytea: 1.0.0
postgres-date: 1.0.7
postgres-interval: 1.2.0
/pg@8.16.2:
resolution: {integrity: sha512-OtLWF0mKLmpxelOt9BqVq83QV6bTfsS0XLegIeAKqKjurRnRKie1Dc1iL89MugmSLhftxw6NNCyZhm1yQFLMEQ==}
engines: {node: '>= 16.0.0'}
peerDependencies:
pg-native: '>=3.0.1'
peerDependenciesMeta:
pg-native:
optional: true
dependencies:
pg-connection-string: 2.9.1
pg-pool: 3.10.1(pg@8.16.2)
pg-protocol: 1.10.2
pg-types: 2.2.0
pgpass: 1.0.5
optionalDependencies:
pg-cloudflare: 1.2.6
dev: false
/pgpass@1.0.5:
resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==}
dependencies:
split2: 4.2.0
dev: false
/picocolors@1.1.1: /picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
@ -2786,6 +2673,24 @@ packages:
source-map-js: 1.2.1 source-map-js: 1.2.1
dev: true dev: true
/postgres-array@2.0.0:
resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==}
engines: {node: '>=4'}
/postgres-bytea@1.0.0:
resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==}
engines: {node: '>=0.10.0'}
/postgres-date@1.0.7:
resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==}
engines: {node: '>=0.10.0'}
/postgres-interval@1.2.0:
resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==}
engines: {node: '>=0.10.0'}
dependencies:
xtend: 4.0.2
/preact-render-to-string@6.5.11(preact@10.24.3): /preact-render-to-string@6.5.11(preact@10.24.3):
resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==}
peerDependencies: peerDependencies:
@ -2798,10 +2703,6 @@ packages:
resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==}
dev: false dev: false
/promise-limit@2.7.0:
resolution: {integrity: sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw==}
dev: false
/react-dom@19.1.0(react@19.1.0): /react-dom@19.1.0(react@19.1.0):
resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
peerDependencies: peerDependencies:
@ -2944,6 +2845,11 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
dev: true dev: true
/split2@4.2.0:
resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==}
engines: {node: '>= 10.x'}
dev: false
/streamsearch@1.1.0: /streamsearch@1.1.0:
resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
@ -3055,11 +2961,6 @@ packages:
tslib: 2.8.1 tslib: 2.8.1
dev: false dev: false
/web-streams-polyfill@3.3.3:
resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==}
engines: {node: '>= 8'}
dev: false
/whatwg-encoding@3.1.1: /whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'} engines: {node: '>=18'}
@ -3072,18 +2973,9 @@ packages:
engines: {node: '>=18'} engines: {node: '>=18'}
dev: false dev: false
/ws@8.18.2: /xtend@4.0.2:
resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
engines: {node: '>=10.0.0'} engines: {node: '>=0.4'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
dev: false
/yallist@5.0.0: /yallist@5.0.0:
resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}

19
src/db/getInstance.ts Normal file
View file

@ -0,0 +1,19 @@
import { drizzle as drizzlePostgres } from "drizzle-orm/node-postgres";
import { drizzle as drizzlePglite } from "drizzle-orm/pglite";
export function getDbInstance() {
if (!process.env.DB_LOCATION) {
throw new Error("DB_LOCATION environment variable is not set");
}
if (process.env.DB_DRIVER === "pglite") {
return {
type: "pglite" as const,
db: drizzlePglite(process.env.DB_LOCATION),
};
}
return {
type: "postgres" as const,
db: drizzlePostgres(process.env.DB_LOCATION),
};
}

View file

@ -1,22 +1,10 @@
import type { Event, Line } from "@/common/parser"; import type { Event, Line } from "@/common/parser";
import "dotenv/config"; import "dotenv/config";
import { and, eq, gte, inArray, like, lte, or } from "drizzle-orm"; import { and, eq, gte, inArray, like, lte, or } from "drizzle-orm";
import { drizzle } from "drizzle-orm/libsql"; import { getDbInstance } from "./getInstance";
import { migrate } from "drizzle-orm/libsql/migrator"; import { eventsTable, linesTable } from "./schema";
import {
eventsTable,
type eventsTableSelectSchema,
linesTable,
} from "./schema";
if (!process.env.DB_FILE_NAME) { const { db } = getDbInstance();
throw new Error("DB_FILE_NAME environment variable is not set");
}
const db = drizzle(process.env.DB_FILE_NAME);
await migrate(db, {
migrationsFolder: "./drizzle",
});
export async function saveLines(lines: Line[]) { export async function saveLines(lines: Line[]) {
await db await db
@ -33,21 +21,8 @@ export async function saveLines(lines: Line[]) {
export async function updateLineEvents(lineId: number, events: Event[]) { export async function updateLineEvents(lineId: number, events: Event[]) {
await db.transaction(async (tx) => { await db.transaction(async (tx) => {
// First, delete existing events for the line
await tx.delete(eventsTable).where(eq(eventsTable.lineId, lineId)); await tx.delete(eventsTable).where(eq(eventsTable.lineId, lineId));
// Then, insert the new events await tx.insert(eventsTable).values(events);
await tx.insert(eventsTable).values(
events.map((event) => ({
id: event.id,
startTime: event.startTime.toISOString(),
endTime: event.endTime.toISOString(),
name: event.name,
title: event.title,
description: event.description,
type: event.type,
lineId: lineId,
})),
);
}); });
} }
@ -68,36 +43,22 @@ export async function getLineById(lineId: number) {
} }
export async function getEventsForLine(lineId: number) { export async function getEventsForLine(lineId: number) {
return formatEvents( return db
await db .select()
.select() .from(eventsTable)
.from(eventsTable) .where(eq(eventsTable.lineId, lineId))
.where(eq(eventsTable.lineId, lineId)) .orderBy(eventsTable.startTime, eventsTable.endTime);
.orderBy(eventsTable.startTime, eventsTable.endTime),
);
} }
export async function getEventsByIds(eventIds: number[]) { export async function getEventsByIds(eventIds: number[]) {
if (eventIds.length === 0) { if (eventIds.length === 0) {
return []; return [];
} }
return formatEvents( return db
await db .select()
.select() .from(eventsTable)
.from(eventsTable) .where(inArray(eventsTable.id, eventIds))
.where(inArray(eventsTable.id, eventIds)) .orderBy(eventsTable.startTime, eventsTable.endTime);
.orderBy(eventsTable.startTime, eventsTable.endTime),
);
}
function formatEvents(
events: ReturnType<(typeof eventsTableSelectSchema)["parse"]>[],
) {
return events.map((event) => ({
...event,
startTime: new Date(event.startTime),
endTime: new Date(event.endTime),
})) satisfies Event[];
} }
export async function getAllEvents() { export async function getAllEvents() {
@ -122,42 +83,40 @@ export async function getEventsForDate(date: string) {
const endOfDay = new Date(date); const endOfDay = new Date(date);
endOfDay.setDate(endOfDay.getDate() + 1); endOfDay.setDate(endOfDay.getDate() + 1);
return formatEvents( return db
await db .select()
.select() .from(eventsTable)
.from(eventsTable) .where(
.where( and(
and( gte(eventsTable.startTime, startOfDay),
gte(eventsTable.startTime, startOfDay.toISOString()), lte(eventsTable.endTime, endOfDay),
lte(eventsTable.endTime, endOfDay.toISOString()), ),
), )
) .orderBy(eventsTable.startTime, eventsTable.endTime);
.orderBy(eventsTable.startTime, eventsTable.endTime),
);
} }
export async function getEventById(eventId: number) { export async function getEventById(eventId: number) {
return formatEvents( const event = await db
await db .select()
.select() .from(eventsTable)
.from(eventsTable) .where(eq(eventsTable.id, eventId))
.where(eq(eventsTable.id, eventId)) .limit(1);
.limit(1), if (event.length === 0) {
)[0]; return null;
}
return event[0];
} }
export async function searchEvents(query: string) { export async function searchEvents(query: string) {
return formatEvents( return await db
await db .select()
.select() .from(eventsTable)
.from(eventsTable) .where(
.where( or(
or( like(eventsTable.name, `%${query}%`),
like(eventsTable.name, `%${query}%`), like(eventsTable.title, `%${query}%`),
like(eventsTable.title, `%${query}%`), like(eventsTable.description, `%${query}%`),
like(eventsTable.description, `%${query}%`), ),
), )
) .orderBy(eventsTable.startTime, eventsTable.endTime);
.orderBy(eventsTable.startTime, eventsTable.endTime),
);
} }

View file

@ -1,24 +1,23 @@
import { int, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { createSelectSchema } from "drizzle-zod";
export const linesTable = sqliteTable("linesTable", { export const linesTable = pgTable("linesTable", {
id: int().primaryKey(), id: integer().primaryKey(),
name: text().notNull(), name: text().notNull(),
description: text().notNull(), description: text().notNull(),
}); });
export const eventsTable = sqliteTable("eventsTable", { export const eventsTable = pgTable("eventsTable", {
id: int().primaryKey(), id: integer().primaryKey(),
startTime: text().notNull(), startTime: timestamp().notNull(),
endTime: text().notNull(), endTime: timestamp().notNull(),
name: text().notNull(), name: text().notNull(),
title: text().notNull(), title: text().notNull(),
description: text().notNull(), description: text().notNull(),
type: text().notNull(), type: text().notNull(),
lineId: int().notNull().references(() => linesTable.id, { lineId: integer()
onDelete: "cascade", .notNull()
onUpdate: "cascade", .references(() => linesTable.id, {
}), onDelete: "cascade",
onUpdate: "cascade",
}),
}); });
export const eventsTableSelectSchema = createSelectSchema(eventsTable);

6
src/instrumentation.ts Normal file
View file

@ -0,0 +1,6 @@
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
const { runMigrations } = await import("./migrate");
await runMigrations();
}
}

18
src/migrate.ts Normal file
View file

@ -0,0 +1,18 @@
import { migrate as migratePostgres } from "drizzle-orm/node-postgres/migrator";
import { migrate as migratePglite } from "drizzle-orm/pglite/migrator";
import { getDbInstance } from "./db/getInstance";
export async function runMigrations() {
if (typeof window !== "undefined") {
return;
}
const { db, type } = getDbInstance();
if (type === "pglite") {
await migratePglite(db, { migrationsFolder: "./drizzle" });
} else if (type === "postgres") {
await migratePostgres(db, { migrationsFolder: "./drizzle" });
} else {
throw new Error(`Unsupported database type: ${type}`);
}
console.log("Migrations completed successfully.");
}