Files
website/drizzle/20261026012817_notes/migration.sql
Stan Runge e33afe5387
All checks were successful
Deploy production / deploy (push) Successful in 1m19s
notes: private Obsidian vault tracker at /trackers/notes
Vault files stay the source of truth (bind-mounted read-write at /notes):
atomic writes chowned to the vault owner, hash-checked saves (409 on a
newer file), deletes to .trash. notes.* is a full-text/link index kept
current by a recursive watcher plus a 60s rescan; live updates over the
owner-only /ws/notes channel (paths and hashes only). Wikilinks, embeds,
backlinks, tags, callouts, image paste. Owner-only everywhere (404).
2026-10-26 01:28:57 +00:00

48 lines
2.1 KiB
SQL

CREATE SCHEMA "notes";
--> statement-breakpoint
CREATE TABLE "notes"."files" (
"path" text PRIMARY KEY,
"kind" text NOT NULL,
"folder" text NOT NULL,
"title" text NOT NULL,
"ext" text NOT NULL,
"size" bigint NOT NULL,
"mtime" timestamp with time zone NOT NULL,
"hash" text NOT NULL,
"tags" text[] DEFAULT '{}'::text[] NOT NULL,
"frontmatter" jsonb,
"body" text,
"search" tsvector,
"indexed_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "notes"."links" (
"src_path" text,
"seq" integer,
"target" text NOT NULL,
"target_key" text NOT NULL,
"embed" boolean DEFAULT false NOT NULL,
CONSTRAINT "links_pkey" PRIMARY KEY("src_path","seq")
);
--> statement-breakpoint
CREATE INDEX "files_search_idx" ON "notes"."files" USING gin ("search");--> statement-breakpoint
CREATE INDEX "files_tags_idx" ON "notes"."files" USING gin ("tags");--> statement-breakpoint
CREATE INDEX "files_folder_idx" ON "notes"."files" ("folder");--> statement-breakpoint
CREATE INDEX "files_kind_idx" ON "notes"."files" ("kind");--> statement-breakpoint
CREATE INDEX "links_target_key_idx" ON "notes"."links" ("target_key");--> statement-breakpoint
ALTER TABLE "notes"."links" ADD CONSTRAINT "links_src_path_files_path_fkey" FOREIGN KEY ("src_path") REFERENCES "notes"."files"("path") ON DELETE CASCADE ON UPDATE CASCADE;--> statement-breakpoint
-- Privacy: notes are always private. Like tasks and health, only the app role can read or write
-- (no personal_ro); the owner-only checks live in the app. The tables are only a search/link
-- index rebuilt from the Obsidian vault files. Grants only run on prod (migrations as `personal`).
REVOKE ALL ON SCHEMA notes FROM PUBLIC;
--> statement-breakpoint
DO $$
BEGIN
IF current_user = 'personal' AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN
GRANT USAGE ON SCHEMA notes TO personal_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA notes TO personal_app;
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA notes
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app;
END IF;
END $$;