Initial lookbook implementation

Pinterest-style visual bookmarking app with:
- URL metadata extraction (OG/Twitter meta, oEmbed fallback)
- Image caching in Postgres with 480px thumbnails
- Multi-tag filtering with Ctrl/Cmd for OR mode
- Fuzzy tag suggestions and inline tag editing
- Browser console auth() with first-use password setup
- Brutalist UI with Commit Mono font and Pico CSS
- Light/dark mode via browser preference
This commit is contained in:
soup 2026-01-16 21:14:23 -05:00
commit fc625fb9cf
Signed by: soup
SSH key fingerprint: SHA256:GYxje8eQkJ6HZKzVWDdyOUF1TyDiprruGhE0Ym8qYDY
486 changed files with 195373 additions and 0 deletions

View file

@ -0,0 +1,73 @@
-- +goose Up
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE item (
id BIGSERIAL PRIMARY KEY,
pub_id UUID NOT NULL DEFAULT gen_random_uuid(),
source_url TEXT NOT NULL,
title TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
site_name TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
UNIQUE (pub_id)
);
CREATE INDEX idx_item_created_at ON item(created_at DESC);
CREATE INDEX idx_item_deleted_at ON item(deleted_at);
CREATE TABLE image (
id BIGSERIAL PRIMARY KEY,
item_id BIGINT NOT NULL REFERENCES item(id) ON DELETE CASCADE,
original_url TEXT NOT NULL DEFAULT '',
content_type TEXT NOT NULL DEFAULT '',
bytes BYTEA NOT NULL,
width INT NOT NULL DEFAULT 0,
height INT NOT NULL DEFAULT 0,
is_thumb BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_image_item ON image(item_id);
CREATE INDEX idx_image_thumb ON image(item_id, is_thumb);
CREATE TABLE tag (
id BIGSERIAL PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE item_tag (
item_id BIGINT NOT NULL REFERENCES item(id) ON DELETE CASCADE,
tag_id BIGINT NOT NULL REFERENCES tag(id) ON DELETE CASCADE,
PRIMARY KEY (item_id, tag_id)
);
CREATE INDEX idx_item_tag_item ON item_tag(item_id);
CREATE INDEX idx_item_tag_tag ON item_tag(tag_id);
CREATE TABLE auth (
id BIGSERIAL PRIMARY KEY,
password_hash BYTEA NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE session (
id BIGSERIAL PRIMARY KEY,
token TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL
);
CREATE INDEX idx_session_token ON session(token);
CREATE INDEX idx_session_expires_at ON session(expires_at);
-- +goose Down
DROP TABLE IF EXISTS session;
DROP TABLE IF EXISTS auth;
DROP TABLE IF EXISTS item_tag;
DROP TABLE IF EXISTS tag;
DROP TABLE IF EXISTS image;
DROP TABLE IF EXISTS item;
DROP EXTENSION IF EXISTS pgcrypto;