All checks were successful
Deploy production / deploy (push) Successful in 2m38s
143 lines
6.9 KiB
SQL
143 lines
6.9 KiB
SQL
CREATE SCHEMA "tasks";
|
|
--> statement-breakpoint
|
|
CREATE TABLE "tasks"."activity" (
|
|
"id" serial PRIMARY KEY,
|
|
"task_id" integer,
|
|
"goal_id" integer,
|
|
"kind" text NOT NULL,
|
|
"from_status" text,
|
|
"to_status" text,
|
|
"note" text,
|
|
"value" numeric(14,2),
|
|
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
CONSTRAINT "activity_target_check" CHECK ("task_id" IS NOT NULL OR "goal_id" IS NOT NULL),
|
|
CONSTRAINT "activity_kind_check" CHECK ("kind" IN ('created','status','edit','note','progress'))
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE TABLE "tasks"."areas" (
|
|
"id" serial PRIMARY KEY,
|
|
"slug" text NOT NULL,
|
|
"name" text NOT NULL,
|
|
"description" 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 "areas_slug_check" CHECK ("slug" ~ '^[a-z0-9][a-z0-9-]{0,62}$'),
|
|
CONSTRAINT "areas_name_check" CHECK (length(btrim("name")) > 0)
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE TABLE "tasks"."goals" (
|
|
"id" serial PRIMARY KEY,
|
|
"title" text NOT NULL,
|
|
"notes" text,
|
|
"status" text DEFAULT 'active' NOT NULL,
|
|
"area_id" integer,
|
|
"target_date" date,
|
|
"metric" text,
|
|
"metric_unit" text,
|
|
"metric_target" numeric(14,2),
|
|
"metric_current" numeric(14,2),
|
|
"flagged" boolean DEFAULT false NOT NULL,
|
|
"sort_order" integer DEFAULT 0 NOT NULL,
|
|
"source" text,
|
|
"achieved_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 "goals_title_check" CHECK (length(btrim("title")) > 0),
|
|
CONSTRAINT "goals_status_check" CHECK ("status" IN ('active','achieved','dropped'))
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE TABLE "tasks"."tasks" (
|
|
"id" serial PRIMARY KEY,
|
|
"title" text NOT NULL,
|
|
"notes" text,
|
|
"status" text DEFAULT 'todo' NOT NULL,
|
|
"priority" smallint DEFAULT 0 NOT NULL,
|
|
"flagged" boolean DEFAULT false NOT NULL,
|
|
"due_date" date,
|
|
"due_time" time,
|
|
"reminder_at" timestamp with time zone,
|
|
"recurrence" text,
|
|
"area_id" integer,
|
|
"goal_id" integer,
|
|
"parent_id" integer,
|
|
"tags" text[] DEFAULT '{}'::text[] NOT NULL,
|
|
"sort_order" integer DEFAULT 0 NOT NULL,
|
|
"source" text,
|
|
"external_id" text,
|
|
"started_at" timestamp with time zone,
|
|
"completed_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 "tasks_title_check" CHECK (length(btrim("title")) > 0),
|
|
CONSTRAINT "tasks_status_check" CHECK ("status" IN ('todo','in_progress','done','dropped')),
|
|
CONSTRAINT "tasks_priority_check" CHECK ("priority" BETWEEN 0 AND 3),
|
|
CONSTRAINT "tasks_parent_check" CHECK ("parent_id" IS NULL OR "parent_id" <> "id"),
|
|
CONSTRAINT "tasks_time_check" CHECK ("due_time" IS NULL OR "due_date" IS NOT NULL)
|
|
);
|
|
--> statement-breakpoint
|
|
CREATE INDEX "activity_task_idx" ON "tasks"."activity" ("task_id","created_at");--> statement-breakpoint
|
|
CREATE INDEX "activity_goal_idx" ON "tasks"."activity" ("goal_id","created_at");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "areas_slug_key" ON "tasks"."areas" ("slug");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "goals_title_key" ON "tasks"."goals" (lower("title"));--> statement-breakpoint
|
|
CREATE INDEX "tasks_area_idx" ON "tasks"."tasks" ("area_id");--> statement-breakpoint
|
|
CREATE INDEX "tasks_goal_idx" ON "tasks"."tasks" ("goal_id");--> statement-breakpoint
|
|
CREATE INDEX "tasks_parent_idx" ON "tasks"."tasks" ("parent_id");--> statement-breakpoint
|
|
CREATE INDEX "tasks_status_due_idx" ON "tasks"."tasks" ("status","due_date");--> statement-breakpoint
|
|
CREATE INDEX "tasks_tags_idx" ON "tasks"."tasks" USING gin ("tags");--> statement-breakpoint
|
|
CREATE UNIQUE INDEX "tasks_source_external_key" ON "tasks"."tasks" ("source","external_id") WHERE "external_id" IS NOT NULL;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."activity" ADD CONSTRAINT "activity_task_id_tasks_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"."tasks"("id") ON DELETE CASCADE;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."activity" ADD CONSTRAINT "activity_goal_id_goals_id_fkey" FOREIGN KEY ("goal_id") REFERENCES "tasks"."goals"("id") ON DELETE CASCADE;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."goals" ADD CONSTRAINT "goals_area_id_areas_id_fkey" FOREIGN KEY ("area_id") REFERENCES "tasks"."areas"("id") ON DELETE SET NULL;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."tasks" ADD CONSTRAINT "tasks_area_id_areas_id_fkey" FOREIGN KEY ("area_id") REFERENCES "tasks"."areas"("id") ON DELETE SET NULL;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."tasks" ADD CONSTRAINT "tasks_goal_id_goals_id_fkey" FOREIGN KEY ("goal_id") REFERENCES "tasks"."goals"("id") ON DELETE SET NULL;--> statement-breakpoint
|
|
ALTER TABLE "tasks"."tasks" ADD CONSTRAINT "tasks_parent_id_tasks_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "tasks"."tasks"("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['areas','goals','tasks'] LOOP
|
|
EXECUTE format('CREATE TRIGGER touch_updated_at BEFORE UPDATE ON tasks.%I FOR EACH ROW EXECUTE FUNCTION meta.touch_updated_at()', t);
|
|
END LOOP;
|
|
END IF;
|
|
END $$;
|
|
--> statement-breakpoint
|
|
-- Privacy: like health, the task tracker is readable/writable by the app role only
|
|
-- (no personal_ro grant). Grants only run on prod, where migrations run as `personal`.
|
|
REVOKE ALL ON SCHEMA tasks 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 tasks TO personal_app;
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA tasks TO personal_app;
|
|
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA tasks TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA tasks
|
|
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO personal_app;
|
|
ALTER DEFAULT PRIVILEGES FOR ROLE personal IN SCHEMA tasks
|
|
GRANT USAGE, SELECT ON SEQUENCES TO personal_app;
|
|
END IF;
|
|
END $$;
|
|
--> statement-breakpoint
|
|
-- Registered in meta.tables (structure only; the lists GUI only shows kind = 'list').
|
|
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 ('tasks','tasks','tasks-tasks','Tasks','core',true,true,1000),
|
|
('tasks','goals','tasks-goals','Goals','core',true,false,1000),
|
|
('tasks','areas','tasks-areas','Task areas','core',true,false,1000),
|
|
('tasks','activity','tasks-activity','Task activity','core',false,false,1000)
|
|
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 $$;
|