Switch database from SQLite to PostgreSQL
Replace in-memory JSON file storage with Prisma ORM using PostgreSQL. Update environment variables and Prisma schema accordingly.
This commit is contained in:
+1
-1
@@ -1,3 +1,3 @@
|
||||
DATABASE_URL="file:./dev.db"
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/bikeapp?schema=public"
|
||||
OLLAMA_URL="http://localhost:11434"
|
||||
OLLAMA_MODEL="llama3.1:8b"
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/bikeapp?schema=public"
|
||||
OLLAMA_URL="http://localhost:11434"
|
||||
OLLAMA_MODEL="llama3.1:8b"
|
||||
+31
-1
@@ -1 +1,31 @@
|
||||
{"scripts":{"dev":"next dev","build":"next build","start":"next start","lint":"eslint src","db:seed":"tsx prisma/seed.ts"},"dependencies":{"@prisma/client":"^5.22.0","next":"^16.2.6","postcss":"^8.5.10","prisma":"^5.22.0","react":"^19.2.1","react-dom":"^19.2.1","sharp":"^0.33.5","zod":"^3.23.8"},"devDependencies":{"@types/node":"^20.17.6","@types/react":"^19.2.7","@types/react-dom":"^19.2.3","eslint":"^9.39.4","eslint-config-next":"^16.2.6","tsx":"^4.19.2","typescript":"^5.6.3"}}
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "eslint src",
|
||||
"db:seed": "tsx prisma/seed.ts",
|
||||
"db:migrate": "prisma migrate dev",
|
||||
"db:studio": "prisma studio",
|
||||
"postinstall": "prisma generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^5.22.0",
|
||||
"next": "^16.2.6",
|
||||
"postcss": "^8.5.10",
|
||||
"prisma": "^5.22.0",
|
||||
"react": "^19.2.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"sharp": "^0.33.5",
|
||||
"zod": "^3.23.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.17.6",
|
||||
"@types/react": "^19.2.7",
|
||||
"@types/react-dom": "^19.2.3",
|
||||
"eslint": "^9.39.4",
|
||||
"eslint-config-next": "^16.2.6",
|
||||
"tsx": "^4.19.2",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ generator client {
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlite"
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ export async function POST(request: Request) {
|
||||
.filter((product): product is Product => Boolean(product))
|
||||
.map((product) => setupVerification[product.id])
|
||||
.filter(Boolean);
|
||||
const prefs = store.preferences();
|
||||
const prefs = await store.preferences();
|
||||
const prompt = [
|
||||
"You are a professional mountain bike suspension and tire setup tuner.",
|
||||
"Combine manufacturer setup ranges, community setup patterns, and the rider's past preferences.",
|
||||
|
||||
@@ -21,7 +21,7 @@ const schema = z.object({
|
||||
});
|
||||
|
||||
export async function GET() {
|
||||
return NextResponse.json(store.listBuilds());
|
||||
return NextResponse.json(await store.listBuilds());
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
@@ -35,7 +35,7 @@ export async function POST(request: Request) {
|
||||
rearTireId: body.rearTireId,
|
||||
};
|
||||
const setup = { ...buildSetupDefaults(selection, body.rider), ...body.setup };
|
||||
const build = store.createBuild({
|
||||
const build = await store.createBuild({
|
||||
name: body.name,
|
||||
riderWeightKg: body.rider.weight,
|
||||
discipline: body.rider.discipline,
|
||||
|
||||
@@ -19,6 +19,6 @@ const schema = z.object({
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = schema.parse(await request.json());
|
||||
const review = store.createReview(body);
|
||||
const review = await store.createReview(body);
|
||||
return NextResponse.json(review, { status: 201 });
|
||||
}
|
||||
|
||||
+31
-53
@@ -1,58 +1,36 @@
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { prisma } from "./prisma";
|
||||
|
||||
type Database = {
|
||||
builds: any[];
|
||||
reviews: any[];
|
||||
};
|
||||
|
||||
const dbPath = path.join(process.cwd(), "data", "bike-app.json");
|
||||
|
||||
function readDb(): Database {
|
||||
mkdirSync(path.dirname(dbPath), { recursive: true });
|
||||
try {
|
||||
return JSON.parse(readFileSync(dbPath, "utf8")) as Database;
|
||||
} catch {
|
||||
return { builds: [], reviews: [] };
|
||||
}
|
||||
}
|
||||
|
||||
function writeDb(data: Database) {
|
||||
mkdirSync(path.dirname(dbPath), { recursive: true });
|
||||
writeFileSync(dbPath, JSON.stringify(data, null, 2));
|
||||
function parseSetup(build: any) {
|
||||
return {
|
||||
...build,
|
||||
setup: build.setup ? JSON.parse(build.setup) : {},
|
||||
};
|
||||
}
|
||||
|
||||
export const store = {
|
||||
listBuilds() {
|
||||
return readDb().builds.sort((a, b) => String(b.createdAt).localeCompare(String(a.createdAt)));
|
||||
},
|
||||
createBuild(input: any) {
|
||||
const data = readDb();
|
||||
const build = {
|
||||
id: crypto.randomUUID(),
|
||||
...input,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
data.builds.unshift(build);
|
||||
writeDb(data);
|
||||
return build;
|
||||
},
|
||||
createReview(input: any) {
|
||||
const data = readDb();
|
||||
const review = {
|
||||
id: crypto.randomUUID(),
|
||||
...input,
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
data.reviews.unshift(review);
|
||||
writeDb(data);
|
||||
return review;
|
||||
},
|
||||
preferences() {
|
||||
const reviews = readDb().reviews;
|
||||
return {
|
||||
harshTendency: reviews.filter((review) => review.suspFeel >= 7).length,
|
||||
softTendency: reviews.filter((review) => review.suspFeel <= 3).length,
|
||||
};
|
||||
},
|
||||
async listBuilds() {
|
||||
const builds = await prisma.bikeBuild.findMany({
|
||||
orderBy: { createdAt: "desc" },
|
||||
});
|
||||
return builds.map(parseSetup);
|
||||
},
|
||||
async createBuild(input: any) {
|
||||
const build = await prisma.bikeBuild.create({
|
||||
data: {
|
||||
...input,
|
||||
setup: JSON.stringify(input.setup ?? {}),
|
||||
},
|
||||
});
|
||||
return parseSetup(build);
|
||||
},
|
||||
async createReview(input: any) {
|
||||
return prisma.rideReview.create({ data: input });
|
||||
},
|
||||
async preferences() {
|
||||
const reviews = await prisma.rideReview.findMany();
|
||||
return {
|
||||
harshTendency: reviews.filter((review) => review.suspFeel >= 7).length,
|
||||
softTendency: reviews.filter((review) => review.suspFeel <= 3).length,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const globalForPrisma = globalThis as unknown as { prisma: PrismaClient };
|
||||
|
||||
export const prisma = globalForPrisma.prisma ?? new PrismaClient();
|
||||
|
||||
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user