Groceries: private tracker at /trackers/groceries (stores, products, price history per store, favorites, cheapest highlighted); seeded AH, Jumbo, Lidl, Plus
All checks were successful
Deploy production / deploy (push) Successful in 2m40s
All checks were successful
Deploy production / deploy (push) Successful in 2m40s
This commit is contained in:
112
drizzle/20261024132343_groceries/migration.sql
Normal file
112
drizzle/20261024132343_groceries/migration.sql
Normal file
@@ -0,0 +1,112 @@
|
||||
CREATE SCHEMA "groceries";
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "groceries"."prices" (
|
||||
"id" serial PRIMARY KEY,
|
||||
"product_id" integer NOT NULL,
|
||||
"store_id" integer NOT NULL,
|
||||
"price_cents" integer NOT NULL,
|
||||
"unit_price_cents" integer,
|
||||
"promo" boolean DEFAULT false NOT NULL,
|
||||
"observed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"source" text DEFAULT 'manual' NOT NULL,
|
||||
"store_product_id" text,
|
||||
"url" text,
|
||||
"notes" text,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "prices_price_check" CHECK ("price_cents" >= 0),
|
||||
CONSTRAINT "prices_unit_price_check" CHECK ("unit_price_cents" IS NULL OR "unit_price_cents" >= 0)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "groceries"."products" (
|
||||
"id" serial PRIMARY KEY,
|
||||
"name" text NOT NULL,
|
||||
"brand" text,
|
||||
"category" text,
|
||||
"size" text,
|
||||
"unit_amount" numeric(10,3),
|
||||
"unit" text,
|
||||
"favorite" boolean DEFAULT false NOT NULL,
|
||||
"notes" text,
|
||||
"archived_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "products_name_check" CHECK (length(btrim("name")) > 0),
|
||||
CONSTRAINT "products_unit_check" CHECK ("unit" IS NULL OR "unit" IN ('piece','kg','l')),
|
||||
CONSTRAINT "products_unit_amount_check" CHECK ("unit_amount" IS NULL OR "unit_amount" > 0)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "groceries"."stores" (
|
||||
"id" serial PRIMARY KEY,
|
||||
"slug" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"website" text,
|
||||
"color" text,
|
||||
"sort_order" integer DEFAULT 0 NOT NULL,
|
||||
"archived_at" timestamp with time zone,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "stores_slug_check" CHECK ("slug" ~ '^[a-z0-9][a-z0-9-]{0,62}$'),
|
||||
CONSTRAINT "stores_name_check" CHECK (length(btrim("name")) > 0)
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE INDEX "prices_product_store_idx" ON "groceries"."prices" ("product_id","store_id","observed_at");--> statement-breakpoint
|
||||
CREATE INDEX "prices_store_idx" ON "groceries"."prices" ("store_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "products_name_brand_key" ON "groceries"."products" (lower("name"),lower(coalesce("brand", '')));--> statement-breakpoint
|
||||
CREATE INDEX "products_category_idx" ON "groceries"."products" ("category");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "stores_slug_key" ON "groceries"."stores" ("slug");--> statement-breakpoint
|
||||
ALTER TABLE "groceries"."prices" ADD CONSTRAINT "prices_product_id_products_id_fkey" FOREIGN KEY ("product_id") REFERENCES "groceries"."products"("id") ON DELETE CASCADE;--> statement-breakpoint
|
||||
ALTER TABLE "groceries"."prices" ADD CONSTRAINT "prices_store_id_stores_id_fkey" FOREIGN KEY ("store_id") REFERENCES "groceries"."stores"("id") ON DELETE CASCADE;
|
||||
--> statement-breakpoint
|
||||
-- Hand-written below this line (not generated by drizzle-kit).
|
||||
-- updated_at triggers, same as every other core table.
|
||||
DO $$
|
||||
DECLARE t text;
|
||||
BEGIN
|
||||
IF to_regprocedure('meta.touch_updated_at()') IS NOT NULL THEN
|
||||
FOREACH t IN ARRAY ARRAY['stores','products','prices'] LOOP
|
||||
EXECUTE format('CREATE TRIGGER touch_updated_at BEFORE UPDATE ON groceries.%I FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at()', t);
|
||||
END LOOP;
|
||||
END IF;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
-- Private like tasks/health: app role only, no personal_ro. Grants only run on prod,
|
||||
-- where migrations run as `personal`.
|
||||
REVOKE ALL ON SCHEMA groceries 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 groceries TO personal_app;
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA groceries TO personal_app;
|
||||
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA groceries TO personal_app;
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA groceries
|
||||
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app;
|
||||
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA groceries
|
||||
GRANT USAGE, SELECT ON SEQUENCES TO personal_app;
|
||||
END IF;
|
||||
END $$;
|
||||
--> statement-breakpoint
|
||||
INSERT INTO groceries.stores (slug, name, website, color, sort_order) VALUES
|
||||
('ah', 'Albert Heijn', 'https://www.ah.nl', '#00a0e2', 10),
|
||||
('jumbo', 'Jumbo', 'https://www.jumbo.com', '#eeb717', 20),
|
||||
('lidl', 'Lidl', 'https://www.lidl.nl', '#0050aa', 30),
|
||||
('plus', 'Plus', 'https://www.plus.nl', '#e2001a', 40)
|
||||
ON CONFLICT DO NOTHING;
|
||||
--> statement-breakpoint
|
||||
-- Registered in meta.tables so the /lists database browser shows them (kind 'core').
|
||||
DO $$
|
||||
BEGIN
|
||||
IF to_regclass('meta.tables') IS NOT NULL THEN
|
||||
INSERT INTO meta.tables (schema_name, table_name, slug, display_name, kind, editable, has_done, sort_order)
|
||||
VALUES ('groceries','products','groceries-products','Grocery products','core',true,false,1100),
|
||||
('groceries','prices','groceries-prices','Grocery prices','core',true,false,1100),
|
||||
('groceries','stores','groceries-stores','Grocery stores','core',true,false,1100)
|
||||
ON CONFLICT DO NOTHING;
|
||||
-- deploy-remote.sh runs meta.sync() as the app role after migrating.
|
||||
IF has_function_privilege('meta.sync(integer)', 'EXECUTE') THEN
|
||||
PERFORM meta.sync();
|
||||
END IF;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
15861
drizzle/20261024132343_groceries/snapshot.json
Normal file
15861
drizzle/20261024132343_groceries/snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user