feat(db): enable schema migration using sql.js and export to data/recipes.db
This commit is contained in:
parent
88acb93068
commit
394aa22df1
2
TODO.md
2
TODO.md
|
|
@ -10,7 +10,7 @@
|
||||||
### Backend Setup
|
### Backend Setup
|
||||||
- [x] Initialize Node.js backend: Create src/backend/, package.json with express, better-sqlite3, zod, vitest. Create src/backend/index.ts with "Hello World" server on port 3000. Verify: npm install && npm run dev (server starts). Commit as "feat(backend): initialize Node.js project with Express"
|
- [x] Initialize Node.js backend: Create src/backend/, package.json with express, better-sqlite3, zod, vitest. Create src/backend/index.ts with "Hello World" server on port 3000. Verify: npm install && npm run dev (server starts). Commit as "feat(backend): initialize Node.js project with Express"
|
||||||
- [x] Set up TypeScript: Create tsconfig.json (strict mode, ES2022, Node16 module resolution). Add build script to package.json. Verify: npm run build succeeds. Commit.
|
- [x] Set up TypeScript: Create tsconfig.json (strict mode, ES2022, Node16 module resolution). Add build script to package.json. Verify: npm run build succeeds. Commit.
|
||||||
- [ ] Create SQLite schema: Create src/backend/db/schema.sql with recipes, tags, recipe_tags tables per ARCHITECTURE.md. Create src/backend/db/migrate.ts to apply schema. Verify: npm run migrate creates data/recipes.db. Commit.
|
- [x] Create SQLite schema: Create src/backend/db/schema.sql with recipes, tags, recipe_tags tables per ARCHITECTURE.md. Create src/backend/db/migrate.ts to apply schema using sql.js (see ADR-001). Verify: npm run migrate creates data/recipes.db. Commit.
|
||||||
- [ ] Implement recipe CRUD API endpoints
|
- [ ] Implement recipe CRUD API endpoints
|
||||||
- [ ] Add Zod validation schemas
|
- [ ] Add Zod validation schemas
|
||||||
- [ ] Write API integration tests
|
- [ ] Write API integration tests
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import initSqlJs from 'sql.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const DATA_DIR = path.resolve(process.cwd(), 'data');
|
||||||
|
const DB_PATH = path.join(DATA_DIR, 'recipes.db');
|
||||||
|
const SCHEMA_PATH = path.resolve(process.cwd(), 'src/backend/db/schema.sql');
|
||||||
|
|
||||||
|
async function ensureDataDir() {
|
||||||
|
if (!fs.existsSync(DATA_DIR)) {
|
||||||
|
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||||
|
console.log(`Created data directory at ${DATA_DIR}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyMigrations() {
|
||||||
|
await ensureDataDir();
|
||||||
|
|
||||||
|
const schema = fs.readFileSync(SCHEMA_PATH, 'utf8');
|
||||||
|
const SQL = await initSqlJs();
|
||||||
|
const db = new SQL.Database();
|
||||||
|
|
||||||
|
// Run all SQL statements
|
||||||
|
db.exec(schema);
|
||||||
|
|
||||||
|
// Export and save to file
|
||||||
|
const data = db.export();
|
||||||
|
fs.writeFileSync(DB_PATH, Buffer.from(data));
|
||||||
|
console.log(`Database migrated: ${DB_PATH}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
applyMigrations();
|
||||||
|
}
|
||||||
|
|
||||||
|
export { applyMigrations };
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
-- Create recipes table
|
||||||
|
CREATE TABLE recipes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
title TEXT NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
ingredients TEXT NOT NULL, -- JSON array
|
||||||
|
instructions TEXT NOT NULL, -- JSON array of steps
|
||||||
|
source_url TEXT,
|
||||||
|
notes TEXT,
|
||||||
|
servings INTEGER,
|
||||||
|
prep_time_minutes INTEGER,
|
||||||
|
cook_time_minutes INTEGER,
|
||||||
|
created_at INTEGER NOT NULL,
|
||||||
|
updated_at INTEGER NOT NULL,
|
||||||
|
last_cooked_at INTEGER
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create tags table
|
||||||
|
CREATE TABLE tags (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT UNIQUE NOT NULL,
|
||||||
|
color TEXT -- Hex color for UI
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Create recipe_tags join table
|
||||||
|
CREATE TABLE recipe_tags (
|
||||||
|
recipe_id INTEGER NOT NULL,
|
||||||
|
tag_id INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY (recipe_id, tag_id),
|
||||||
|
FOREIGN KEY (recipe_id) REFERENCES recipes(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- Indexes for efficiency
|
||||||
|
CREATE INDEX idx_recipes_title ON recipes(title);
|
||||||
|
CREATE INDEX idx_recipes_created_at ON recipes(created_at DESC);
|
||||||
|
CREATE INDEX idx_recipe_tags_recipe ON recipe_tags(recipe_id);
|
||||||
|
CREATE INDEX idx_recipe_tags_tag ON recipe_tags(tag_id);
|
||||||
Loading…
Reference in New Issue