dashboards: migrate from filesystem to postgresql
Replace JSON file storage with DB tables for dashboard layouts, screens, projects, and terminal defaults. Fresh drizzle migration with dashboardDefaults table and new columns on screens/projects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+65
-38
@@ -48,10 +48,8 @@ CREATE TABLE "user_integrations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"provider" text NOT NULL,
|
||||
"access_token" text,
|
||||
"refresh_token" text,
|
||||
"expires_at" timestamp with time zone,
|
||||
"profile" jsonb,
|
||||
"server_integration_id" integer,
|
||||
"config" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_user_integrations_user_provider" UNIQUE("user_id","provider")
|
||||
@@ -115,28 +113,16 @@ CREATE TABLE "chat_sessions" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "projects" (
|
||||
CREATE TABLE "dashboard_defaults" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"meta" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"layout" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"terminals" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"terminals" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"host_terminals" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_projects_user_slug" UNIQUE("user_id","slug")
|
||||
CONSTRAINT "dashboard_defaults_user_id_unique" UNIQUE("user_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "screens" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"layout" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_screens_user_name" UNIQUE("user_id","name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "workspaces" (
|
||||
CREATE TABLE "dashboards" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
@@ -147,7 +133,31 @@ CREATE TABLE "workspaces" (
|
||||
"sort_order" integer DEFAULT 0 NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_workspaces_user_id" UNIQUE("user_id","id")
|
||||
CONSTRAINT "uq_dashboards_user_id" UNIQUE("user_id","id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "projects" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"slug" text NOT NULL,
|
||||
"meta" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"layout" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"terminals" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"host_terminals" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_projects_user_slug" UNIQUE("user_id","slug")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "screens" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"layout" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
||||
"terminals" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"host_terminals" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_screens_user_name" UNIQUE("user_id","name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "extensions" (
|
||||
@@ -185,20 +195,6 @@ CREATE TABLE "processes" (
|
||||
CONSTRAINT "uq_processes_scope_user_dir" UNIQUE("scope","user_id","dir_name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "resources" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"scope" text NOT NULL,
|
||||
"dir_name" text NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"description" text,
|
||||
"body" text,
|
||||
"version" integer DEFAULT 1 NOT NULL,
|
||||
"config" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_resources_scope_dir" UNIQUE("scope","dir_name")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "skills" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"scope" text NOT NULL,
|
||||
@@ -296,18 +292,49 @@ CREATE TABLE "server_config" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "server_integrations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"provider" text NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"config" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "server_integrations_provider_unique" UNIQUE("provider")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "email_accounts" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"provider" text NOT NULL,
|
||||
"email" text NOT NULL,
|
||||
"display_name" text,
|
||||
"imap_host" text NOT NULL,
|
||||
"imap_port" integer NOT NULL,
|
||||
"imap_secure" boolean DEFAULT true NOT NULL,
|
||||
"auth_type" text NOT NULL,
|
||||
"credentials" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"enabled" boolean DEFAULT true NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
"status" text DEFAULT 'connected' NOT NULL,
|
||||
"sync_meta" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
||||
CONSTRAINT "uq_email_accounts_user_email" UNIQUE("user_id","email")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "passkey_challenges" ADD CONSTRAINT "passkey_challenges_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "passkeys" ADD CONSTRAINT "passkeys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "dock_configs" ADD CONSTRAINT "dock_configs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_integrations" ADD CONSTRAINT "user_integrations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_integrations" ADD CONSTRAINT "user_integrations_server_integration_id_server_integrations_id_fk" FOREIGN KEY ("server_integration_id") REFERENCES "public"."server_integrations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_settings" ADD CONSTRAINT "user_settings_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_state" ADD CONSTRAINT "user_state_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "chat_groups" ADD CONSTRAINT "chat_groups_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "chat_messages" ADD CONSTRAINT "chat_messages_session_id_chat_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."chat_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "chat_sessions" ADD CONSTRAINT "chat_sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "dashboard_defaults" ADD CONSTRAINT "dashboard_defaults_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "dashboards" ADD CONSTRAINT "dashboards_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "projects" ADD CONSTRAINT "projects_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "screens" ADD CONSTRAINT "screens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "workspaces" ADD CONSTRAINT "workspaces_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "extensions" ADD CONSTRAINT "extensions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "processes" ADD CONSTRAINT "processes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "skills" ADD CONSTRAINT "skills_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
@@ -316,6 +343,7 @@ ALTER TABLE "tools" ADD CONSTRAINT "tools_user_id_users_id_fk" FOREIGN KEY ("use
|
||||
ALTER TABLE "queue_jobs" ADD CONSTRAINT "queue_jobs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "task_logs" ADD CONSTRAINT "task_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "terminal_containers" ADD CONSTRAINT "terminal_containers_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "email_accounts" ADD CONSTRAINT "email_accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "idx_token_blacklist_expires" ON "token_blacklist" USING btree ("expires_at");--> statement-breakpoint
|
||||
CREATE INDEX "idx_chat_groups_user" ON "chat_groups" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_messages_session_order" ON "chat_messages" USING btree ("session_id","sort_order");--> statement-breakpoint
|
||||
@@ -327,7 +355,6 @@ CREATE INDEX "idx_extensions_user" ON "extensions" USING btree ("user_id");--> s
|
||||
CREATE INDEX "idx_item_chats_type_item" ON "item_chats" USING btree ("item_type","item_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_processes_scope" ON "processes" USING btree ("scope");--> statement-breakpoint
|
||||
CREATE INDEX "idx_processes_user" ON "processes" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_resources_scope" ON "resources" USING btree ("scope");--> statement-breakpoint
|
||||
CREATE INDEX "idx_skills_scope" ON "skills" USING btree ("scope");--> statement-breakpoint
|
||||
CREATE INDEX "idx_skills_user" ON "skills" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "idx_tasks_scope" ON "tasks" USING btree ("scope");--> statement-breakpoint
|
||||
@@ -1,4 +0,0 @@
|
||||
ALTER TABLE "workspaces" RENAME TO "dashboards";--> statement-breakpoint
|
||||
ALTER TABLE "dashboards" DROP CONSTRAINT "workspaces_user_id_users_id_fk";--> statement-breakpoint
|
||||
ALTER TABLE "dashboards" ADD CONSTRAINT "dashboards_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "dashboards" RENAME CONSTRAINT "uq_workspaces_user_id" TO "uq_dashboards_user_id";
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"id": "65f7442c-6226-471f-b6b3-d559e820ab61",
|
||||
"id": "7187e43e-021e-4623-8c74-8e5e7bc73926",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
@@ -347,29 +347,18 @@
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"access_token": {
|
||||
"name": "access_token",
|
||||
"type": "text",
|
||||
"server_integration_id": {
|
||||
"name": "server_integration_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"refresh_token": {
|
||||
"name": "refresh_token",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"profile": {
|
||||
"name": "profile",
|
||||
"config": {
|
||||
"name": "config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
@@ -400,6 +389,19 @@
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
},
|
||||
"user_integrations_server_integration_id_server_integrations_id_fk": {
|
||||
"name": "user_integrations_server_integration_id_server_integrations_id_fk",
|
||||
"tableFrom": "user_integrations",
|
||||
"tableTo": "server_integrations",
|
||||
"columnsFrom": [
|
||||
"server_integration_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "set null",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
@@ -929,6 +931,177 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.dashboard_defaults": {
|
||||
"name": "dashboard_defaults",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"terminals": {
|
||||
"name": "terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"host_terminals": {
|
||||
"name": "host_terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"dashboard_defaults_user_id_users_id_fk": {
|
||||
"name": "dashboard_defaults_user_id_users_id_fk",
|
||||
"tableFrom": "dashboard_defaults",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"dashboard_defaults_user_id_unique": {
|
||||
"name": "dashboard_defaults_user_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.dashboards": {
|
||||
"name": "dashboards",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"config": {
|
||||
"name": "config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"layout": {
|
||||
"name": "layout",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"terminals": {
|
||||
"name": "terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"host_terminals": {
|
||||
"name": "host_terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"dashboards_user_id_users_id_fk": {
|
||||
"name": "dashboards_user_id_users_id_fk",
|
||||
"tableFrom": "dashboards",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"uq_dashboards_user_id": {
|
||||
"name": "uq_dashboards_user_id",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.projects": {
|
||||
"name": "projects",
|
||||
"schema": "",
|
||||
@@ -972,6 +1145,13 @@
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"host_terminals": {
|
||||
"name": "host_terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
@@ -1047,6 +1227,20 @@
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"terminals": {
|
||||
"name": "terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"host_terminals": {
|
||||
"name": "host_terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
@@ -1086,109 +1280,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.workspaces": {
|
||||
"name": "workspaces",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"config": {
|
||||
"name": "config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"layout": {
|
||||
"name": "layout",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"terminals": {
|
||||
"name": "terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"host_terminals": {
|
||||
"name": "host_terminals",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"workspaces_user_id_users_id_fk": {
|
||||
"name": "workspaces_user_id_users_id_fk",
|
||||
"tableFrom": "workspaces",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"uq_workspaces_user_id": {
|
||||
"name": "uq_workspaces_user_id",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id",
|
||||
"id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.extensions": {
|
||||
"name": "extensions",
|
||||
"schema": "",
|
||||
@@ -1514,108 +1605,6 @@
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.resources": {
|
||||
"name": "resources",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"dir_name": {
|
||||
"name": "dir_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"body": {
|
||||
"name": "body",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": 1
|
||||
},
|
||||
"config": {
|
||||
"name": "config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"idx_resources_scope": {
|
||||
"name": "idx_resources_scope",
|
||||
"columns": [
|
||||
{
|
||||
"expression": "scope",
|
||||
"isExpression": false,
|
||||
"asc": true,
|
||||
"nulls": "last"
|
||||
}
|
||||
],
|
||||
"isUnique": false,
|
||||
"concurrently": false,
|
||||
"method": "btree",
|
||||
"with": {}
|
||||
}
|
||||
},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"uq_resources_scope_dir": {
|
||||
"name": "uq_resources_scope_dir",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"scope",
|
||||
"dir_name"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.skills": {
|
||||
"name": "skills",
|
||||
"schema": "",
|
||||
@@ -2427,6 +2416,200 @@
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.server_integrations": {
|
||||
"name": "server_integrations",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"config": {
|
||||
"name": "config",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"server_integrations_provider_unique": {
|
||||
"name": "server_integrations_provider_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"provider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.email_accounts": {
|
||||
"name": "email_accounts",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"user_id": {
|
||||
"name": "user_id",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"provider": {
|
||||
"name": "provider",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"email": {
|
||||
"name": "email",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"imap_host": {
|
||||
"name": "imap_host",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"imap_port": {
|
||||
"name": "imap_port",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"imap_secure": {
|
||||
"name": "imap_secure",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"auth_type": {
|
||||
"name": "auth_type",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"credentials": {
|
||||
"name": "credentials",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"enabled": {
|
||||
"name": "enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'connected'"
|
||||
},
|
||||
"sync_meta": {
|
||||
"name": "sync_meta",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'{}'::jsonb"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {
|
||||
"email_accounts_user_id_users_id_fk": {
|
||||
"name": "email_accounts_user_id_users_id_fk",
|
||||
"tableFrom": "email_accounts",
|
||||
"tableTo": "users",
|
||||
"columnsFrom": [
|
||||
"user_id"
|
||||
],
|
||||
"columnsTo": [
|
||||
"id"
|
||||
],
|
||||
"onDelete": "cascade",
|
||||
"onUpdate": "no action"
|
||||
}
|
||||
},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"uq_email_accounts_user_email": {
|
||||
"name": "uq_email_accounts_user_email",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"user_id",
|
||||
"email"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
|
||||
@@ -5,15 +5,8 @@
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1772121115932,
|
||||
"tag": "0000_clammy_meteorite",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1772265600000,
|
||||
"tag": "0001_rename_workspaces_to_dashboards",
|
||||
"when": 1772795711164,
|
||||
"tag": "0000_futuristic_ink",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
||||
@@ -52,5 +52,17 @@ export {
|
||||
getAllSyncedAccounts,
|
||||
} from './queries/email-accounts';
|
||||
|
||||
export {
|
||||
getAllDashboardState,
|
||||
upsertDashboard,
|
||||
deleteDashboard,
|
||||
upsertScreen,
|
||||
deleteScreen,
|
||||
upsertProject,
|
||||
deleteProject,
|
||||
getDefaults,
|
||||
upsertDefaults,
|
||||
} from './queries/dashboards';
|
||||
|
||||
export { db } from './db';
|
||||
export * as schema from './schema';
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
import { eq, and } from 'drizzle-orm';
|
||||
import { db } from '../db';
|
||||
import { dashboards, screens, projects, dashboardDefaults } from '../schema';
|
||||
|
||||
// ── Full state read ──
|
||||
|
||||
export async function getAllDashboardState(userId: number): Promise<Record<string, unknown>> {
|
||||
const result: Record<string, unknown> = {};
|
||||
|
||||
const [dashRows, screenRows, projectRows, defaultsRow] = await Promise.all([
|
||||
db.select().from(dashboards).where(eq(dashboards.userId, userId)),
|
||||
db.select().from(screens).where(eq(screens.userId, userId)),
|
||||
db.select().from(projects).where(eq(projects.userId, userId)),
|
||||
db
|
||||
.select()
|
||||
.from(dashboardDefaults)
|
||||
.where(eq(dashboardDefaults.userId, userId))
|
||||
.then((rows) => rows[0]),
|
||||
]);
|
||||
|
||||
// Dashboards → workspaces array + per-dashboard keys
|
||||
if (dashRows.length > 0) {
|
||||
const sorted = [...dashRows].sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
result['workspaces'] = sorted.map((d) => ({ id: d.id, name: d.name, ...(d.config as object) }));
|
||||
|
||||
for (const d of dashRows) {
|
||||
result[`ws-layout-${d.id}`] = d.layout;
|
||||
result[`ws-terminals-${d.id}`] = d.terminals;
|
||||
result[`ws-host-terminals-${d.id}`] = d.hostTerminals;
|
||||
}
|
||||
}
|
||||
|
||||
// Defaults
|
||||
if (defaultsRow) {
|
||||
result['ws-terminals-default'] = defaultsRow.terminals;
|
||||
result['ws-host-terminals-default'] = defaultsRow.hostTerminals;
|
||||
}
|
||||
|
||||
// Screens
|
||||
for (const s of screenRows) {
|
||||
result[`screens/${s.name}`] = s.layout;
|
||||
}
|
||||
|
||||
// Projects
|
||||
const projectList = projectRows.map((p) => ({
|
||||
...(p.meta as object),
|
||||
id: p.slug,
|
||||
cwd: `/Projects/${p.slug}`,
|
||||
}));
|
||||
result['projects'] = projectList;
|
||||
|
||||
for (const p of projectRows) {
|
||||
result[`proj-layout-${p.slug}`] = p.layout;
|
||||
result[`proj-terminals-${p.slug}`] = p.terminals;
|
||||
result[`proj-host-terminals-${p.slug}`] = p.hostTerminals;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ── Dashboard CRUD ──
|
||||
|
||||
type UpsertDashboardData = {
|
||||
name?: string;
|
||||
config?: unknown;
|
||||
layout?: unknown;
|
||||
terminals?: unknown;
|
||||
hostTerminals?: unknown;
|
||||
sortOrder?: number;
|
||||
};
|
||||
|
||||
export async function upsertDashboard(userId: number, id: string, data: UpsertDashboardData): Promise<void> {
|
||||
const now = new Date();
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(dashboards)
|
||||
.where(and(eq(dashboards.userId, userId), eq(dashboards.id, id)))
|
||||
.then((rows) => rows[0]);
|
||||
|
||||
if (existing) {
|
||||
const set: Record<string, unknown> = { updatedAt: now };
|
||||
if (data.name !== undefined) set.name = data.name;
|
||||
if (data.config !== undefined) set.config = data.config;
|
||||
if (data.layout !== undefined) set.layout = data.layout;
|
||||
if (data.terminals !== undefined) set.terminals = data.terminals;
|
||||
if (data.hostTerminals !== undefined) set.hostTerminals = data.hostTerminals;
|
||||
if (data.sortOrder !== undefined) set.sortOrder = data.sortOrder;
|
||||
await db.update(dashboards).set(set).where(eq(dashboards.id, id));
|
||||
} else {
|
||||
await db.insert(dashboards).values({
|
||||
id,
|
||||
userId,
|
||||
name: data.name ?? id,
|
||||
config: data.config ?? {},
|
||||
layout: data.layout ?? [],
|
||||
terminals: data.terminals ?? [],
|
||||
hostTerminals: data.hostTerminals ?? [],
|
||||
sortOrder: data.sortOrder ?? 0,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteDashboard(userId: number, id: string): Promise<void> {
|
||||
await db.delete(dashboards).where(and(eq(dashboards.userId, userId), eq(dashboards.id, id)));
|
||||
}
|
||||
|
||||
// ── Screen CRUD ──
|
||||
|
||||
type UpsertScreenData = {
|
||||
layout?: unknown;
|
||||
terminals?: unknown;
|
||||
hostTerminals?: unknown;
|
||||
};
|
||||
|
||||
export async function upsertScreen(userId: number, name: string, data: UpsertScreenData): Promise<void> {
|
||||
const now = new Date();
|
||||
const set: Record<string, unknown> = { updatedAt: now };
|
||||
if (data.layout !== undefined) set.layout = data.layout;
|
||||
if (data.terminals !== undefined) set.terminals = data.terminals;
|
||||
if (data.hostTerminals !== undefined) set.hostTerminals = data.hostTerminals;
|
||||
|
||||
await db
|
||||
.insert(screens)
|
||||
.values({
|
||||
userId,
|
||||
name,
|
||||
layout: (data.layout ?? []) as never,
|
||||
terminals: (data.terminals ?? {}) as never,
|
||||
hostTerminals: (data.hostTerminals ?? {}) as never,
|
||||
updatedAt: now,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: [screens.userId, screens.name],
|
||||
set,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteScreen(userId: number, name: string): Promise<void> {
|
||||
await db.delete(screens).where(and(eq(screens.userId, userId), eq(screens.name, name)));
|
||||
}
|
||||
|
||||
// ── Project CRUD ──
|
||||
|
||||
type UpsertProjectData = {
|
||||
meta?: unknown;
|
||||
layout?: unknown;
|
||||
terminals?: unknown;
|
||||
hostTerminals?: unknown;
|
||||
};
|
||||
|
||||
export async function upsertProject(userId: number, slug: string, data: UpsertProjectData): Promise<void> {
|
||||
const now = new Date();
|
||||
const existing = await db
|
||||
.select()
|
||||
.from(projects)
|
||||
.where(and(eq(projects.userId, userId), eq(projects.slug, slug)))
|
||||
.then((rows) => rows[0]);
|
||||
|
||||
if (existing) {
|
||||
const set: Record<string, unknown> = { updatedAt: now };
|
||||
if (data.meta !== undefined) set.meta = data.meta;
|
||||
if (data.layout !== undefined) set.layout = data.layout;
|
||||
if (data.terminals !== undefined) set.terminals = data.terminals;
|
||||
if (data.hostTerminals !== undefined) set.hostTerminals = data.hostTerminals;
|
||||
await db
|
||||
.update(projects)
|
||||
.set(set)
|
||||
.where(and(eq(projects.userId, userId), eq(projects.slug, slug)));
|
||||
} else {
|
||||
await db.insert(projects).values({
|
||||
userId,
|
||||
slug,
|
||||
meta: (data.meta ?? {}) as never,
|
||||
layout: (data.layout ?? []) as never,
|
||||
terminals: (data.terminals ?? []) as never,
|
||||
hostTerminals: (data.hostTerminals ?? {}) as never,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteProject(userId: number, slug: string): Promise<void> {
|
||||
await db.delete(projects).where(and(eq(projects.userId, userId), eq(projects.slug, slug)));
|
||||
}
|
||||
|
||||
// ── Defaults ──
|
||||
|
||||
type UpsertDefaultsData = {
|
||||
terminals?: unknown;
|
||||
hostTerminals?: unknown;
|
||||
};
|
||||
|
||||
export async function getDefaults(userId: number): Promise<{ terminals: unknown; hostTerminals: unknown }> {
|
||||
const [row] = await db.select().from(dashboardDefaults).where(eq(dashboardDefaults.userId, userId));
|
||||
return { terminals: row?.terminals ?? {}, hostTerminals: row?.hostTerminals ?? {} };
|
||||
}
|
||||
|
||||
export async function upsertDefaults(userId: number, data: UpsertDefaultsData): Promise<void> {
|
||||
const now = new Date();
|
||||
const set: Record<string, unknown> = { updatedAt: now };
|
||||
if (data.terminals !== undefined) set.terminals = data.terminals;
|
||||
if (data.hostTerminals !== undefined) set.hostTerminals = data.hostTerminals;
|
||||
|
||||
await db
|
||||
.insert(dashboardDefaults)
|
||||
.values({
|
||||
userId,
|
||||
terminals: (data.terminals ?? {}) as never,
|
||||
hostTerminals: (data.hostTerminals ?? {}) as never,
|
||||
updatedAt: now,
|
||||
})
|
||||
.onConflictDoUpdate({
|
||||
target: dashboardDefaults.userId,
|
||||
set,
|
||||
});
|
||||
}
|
||||
@@ -1,40 +1,66 @@
|
||||
import { pgTable, serial, text, integer, timestamp, jsonb, unique } from 'drizzle-orm/pg-core';
|
||||
import { users } from './auth';
|
||||
|
||||
export const dashboards = pgTable('dashboards', {
|
||||
id: text('id').primaryKey(),
|
||||
userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
config: jsonb('config').notNull().default({}),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
terminals: jsonb('terminals').notNull().default([]),
|
||||
hostTerminals: jsonb('host_terminals').notNull().default([]),
|
||||
sortOrder: integer('sort_order').notNull().default(0),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => [
|
||||
unique('uq_dashboards_user_id').on(table.userId, table.id),
|
||||
]);
|
||||
export const dashboards = pgTable(
|
||||
'dashboards',
|
||||
{
|
||||
id: text('id').primaryKey(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
config: jsonb('config').notNull().default({}),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
terminals: jsonb('terminals').notNull().default([]),
|
||||
hostTerminals: jsonb('host_terminals').notNull().default([]),
|
||||
sortOrder: integer('sort_order').notNull().default(0),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => [unique('uq_dashboards_user_id').on(table.userId, table.id)],
|
||||
);
|
||||
|
||||
export const screens = pgTable('screens', {
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => [
|
||||
unique('uq_screens_user_name').on(table.userId, table.name),
|
||||
]);
|
||||
export const screens = pgTable(
|
||||
'screens',
|
||||
{
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
name: text('name').notNull(),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
terminals: jsonb('terminals').notNull().default({}),
|
||||
hostTerminals: jsonb('host_terminals').notNull().default({}),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => [unique('uq_screens_user_name').on(table.userId, table.name)],
|
||||
);
|
||||
|
||||
export const projects = pgTable('projects', {
|
||||
export const projects = pgTable(
|
||||
'projects',
|
||||
{
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' }),
|
||||
slug: text('slug').notNull(),
|
||||
meta: jsonb('meta').notNull().default({}),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
terminals: jsonb('terminals').notNull().default([]),
|
||||
hostTerminals: jsonb('host_terminals').notNull().default({}),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
},
|
||||
(table) => [unique('uq_projects_user_slug').on(table.userId, table.slug)],
|
||||
);
|
||||
|
||||
export const dashboardDefaults = pgTable('dashboard_defaults', {
|
||||
id: serial('id').primaryKey(),
|
||||
userId: integer('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
|
||||
slug: text('slug').notNull(),
|
||||
meta: jsonb('meta').notNull().default({}),
|
||||
layout: jsonb('layout').notNull().default([]),
|
||||
terminals: jsonb('terminals').notNull().default([]),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
userId: integer('user_id')
|
||||
.notNull()
|
||||
.references(() => users.id, { onDelete: 'cascade' })
|
||||
.unique(),
|
||||
terminals: jsonb('terminals').notNull().default({}),
|
||||
hostTerminals: jsonb('host_terminals').notNull().default({}),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
}, (table) => [
|
||||
unique('uq_projects_user_slug').on(table.userId, table.slug),
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user