feat: scaffold Flask + React app with schema and JSON error handling
Claude-Session: https://claude.ai/code/session_01M9MLit5Ko3X4s7rzC5Kv7X
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id SERIAL PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
password_hash TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS users_username_lower_key ON users (lower(username));
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
token_hash BYTEA PRIMARY KEY,
|
||||
user_id INT NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
expires_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS sessions_user_id_idx ON sessions (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS genres (
|
||||
id SMALLSERIAL PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
-- WHERE NOT EXISTS instead of ON CONFLICT: ON CONFLICT consumes a sequence value per row
|
||||
-- on every startup, which would eventually overflow SMALLSERIAL.
|
||||
INSERT INTO genres (name)
|
||||
SELECT seed.name
|
||||
FROM (VALUES ('Fiction'), ('Non-Fiction'), ('Mystery'), ('Thriller'), ('Science Fiction'),
|
||||
('Fantasy'), ('Romance'), ('Horror'), ('Historical Fiction'), ('Biography'),
|
||||
('Memoir'), ('Self-Help'), ('Science'), ('History'), ('Poetry'), ('Other')) AS seed(name)
|
||||
WHERE NOT EXISTS (SELECT 1 FROM genres WHERE genres.name = seed.name);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS books (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
title TEXT NOT NULL CHECK (btrim(title) <> ''),
|
||||
author TEXT NOT NULL CHECK (btrim(author) <> ''),
|
||||
genre_id SMALLINT NOT NULL REFERENCES genres,
|
||||
total_pages INT NOT NULL CHECK (total_pages > 0),
|
||||
current_page INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
CONSTRAINT books_current_page_range CHECK (current_page BETWEEN 0 AND total_pages)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS books_user_id_idx ON books (user_id);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS notes (
|
||||
id SERIAL PRIMARY KEY,
|
||||
book_id INT NOT NULL REFERENCES books ON DELETE CASCADE,
|
||||
body TEXT NOT NULL CHECK (btrim(body) <> ''),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS notes_book_id_idx ON notes (book_id);
|
||||
Reference in New Issue
Block a user