From 70f0924d4b62c75f5154a36cdc13a00f90a9708a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gjermund=20H=C3=B8s=C3=B8ien=20Wiggen?=
Date: Mon, 15 Jun 2026 20:42:17 +0200
Subject: [PATCH] feat: auth system, scrip scheduler, UI widgets, and new API
routes
- Add session-based authentication (login page, middleware, auth context)
- Add cron-like scrip scheduler for time-based conditions
- Add layout builder, scrip wizard, searchable select components
- Add trend chart widget for dashboards
- Add notifications, attachments, queue-permissions API routes
- Add seed-users script
- Update schema with 10 new migrations (0008-0017)
- Apply redesign: Linear-inspired dark theme, conversation-centric UI
- Gitignore runtime data directory
Co-Authored-By: Claude Opus 4.8
---
.env.example | 2 +-
.gitignore | 3 +
CLAUDE.md | 2 +-
drizzle/migrations/0008_sturdy_prism.sql | 12 +
drizzle/migrations/0009_tiny_lady_vermin.sql | 1 +
drizzle/migrations/0010_misty_morg.sql | 15 +
drizzle/migrations/0011_breezy_tyrannus.sql | 2 +
drizzle/migrations/0012_living_photon.sql | 12 +
drizzle/migrations/0013_bored_silvermane.sql | 12 +
drizzle/migrations/0014_cloudy_siren.sql | 1 +
drizzle/migrations/0015_tense_patch.sql | 15 +
drizzle/migrations/0016_famous_maximus.sql | 12 +
.../0017_redundant_the_renegades.sql | 1 +
drizzle/migrations/meta/0008_snapshot.json | 1418 ++++++++++++
drizzle/migrations/meta/0009_snapshot.json | 1418 ++++++++++++
drizzle/migrations/meta/0010_snapshot.json | 1550 +++++++++++++
drizzle/migrations/meta/0011_snapshot.json | 1563 +++++++++++++
drizzle/migrations/meta/0012_snapshot.json | 1669 ++++++++++++++
drizzle/migrations/meta/0013_snapshot.json | 1775 +++++++++++++++
drizzle/migrations/meta/0014_snapshot.json | 1782 +++++++++++++++
drizzle/migrations/meta/0015_snapshot.json | 1910 ++++++++++++++++
drizzle/migrations/meta/0016_snapshot.json | 1999 ++++++++++++++++
drizzle/migrations/meta/0017_snapshot.json | 2005 +++++++++++++++++
drizzle/migrations/meta/_journal.json | 70 +
package.json | 1 +
scripts/seed-users.ts | 28 +
src/auth/middleware.ts | 144 ++
src/auth/permissions.ts | 86 +
src/config.ts | 2 +
src/db/schema.ts | 76 +
src/db/seed.ts | 67 +-
src/index.ts | 51 +-
src/lifecycle/validator.ts | 45 +-
src/models/ticket.ts | 2 +
src/models/transaction.ts | 2 +
src/routes/attachments.ts | 190 ++
src/routes/auth.ts | 132 ++
src/routes/dashboards.ts | 79 +
src/routes/notifications.ts | 64 +
src/routes/queue-permissions.ts | 176 ++
src/routes/tickets.ts | 783 ++++++-
src/routes/users.ts | 8 +
src/scrip/conditions.ts | 43 +
src/scrip/engine.ts | 19 +-
src/scrip/scheduler.ts | 92 +
web/src/app/admin/page-content.tsx | 33 +-
web/src/app/dashboards/[id]/page.tsx | 9 +
web/src/app/layout.tsx | 9 +-
web/src/app/login/page.tsx | 97 +
web/src/app/page.tsx | 591 +++--
web/src/app/tickets/[id]/page.tsx | 505 ++++-
web/src/components/app-shell.tsx | 298 ++-
web/src/components/layout-builder.tsx | 210 ++
web/src/components/scrip-wizard.tsx | 395 ++++
web/src/components/searchable-select.tsx | 163 ++
.../components/widgets/trend-chart-widget.tsx | 43 +
web/src/lib/api.ts | 307 ++-
web/src/lib/auth-context.tsx | 61 +
web/src/lib/types.ts | 56 +
59 files changed, 21795 insertions(+), 321 deletions(-)
create mode 100644 drizzle/migrations/0008_sturdy_prism.sql
create mode 100644 drizzle/migrations/0009_tiny_lady_vermin.sql
create mode 100644 drizzle/migrations/0010_misty_morg.sql
create mode 100644 drizzle/migrations/0011_breezy_tyrannus.sql
create mode 100644 drizzle/migrations/0012_living_photon.sql
create mode 100644 drizzle/migrations/0013_bored_silvermane.sql
create mode 100644 drizzle/migrations/0014_cloudy_siren.sql
create mode 100644 drizzle/migrations/0015_tense_patch.sql
create mode 100644 drizzle/migrations/0016_famous_maximus.sql
create mode 100644 drizzle/migrations/0017_redundant_the_renegades.sql
create mode 100644 drizzle/migrations/meta/0008_snapshot.json
create mode 100644 drizzle/migrations/meta/0009_snapshot.json
create mode 100644 drizzle/migrations/meta/0010_snapshot.json
create mode 100644 drizzle/migrations/meta/0011_snapshot.json
create mode 100644 drizzle/migrations/meta/0012_snapshot.json
create mode 100644 drizzle/migrations/meta/0013_snapshot.json
create mode 100644 drizzle/migrations/meta/0014_snapshot.json
create mode 100644 drizzle/migrations/meta/0015_snapshot.json
create mode 100644 drizzle/migrations/meta/0016_snapshot.json
create mode 100644 drizzle/migrations/meta/0017_snapshot.json
create mode 100644 scripts/seed-users.ts
create mode 100644 src/auth/middleware.ts
create mode 100644 src/auth/permissions.ts
create mode 100644 src/routes/attachments.ts
create mode 100644 src/routes/auth.ts
create mode 100644 src/routes/notifications.ts
create mode 100644 src/routes/queue-permissions.ts
create mode 100644 src/scrip/scheduler.ts
create mode 100644 web/src/app/login/page.tsx
create mode 100644 web/src/components/layout-builder.tsx
create mode 100644 web/src/components/scrip-wizard.tsx
create mode 100644 web/src/components/searchable-select.tsx
create mode 100644 web/src/components/widgets/trend-chart-widget.tsx
create mode 100644 web/src/lib/auth-context.tsx
diff --git a/.env.example b/.env.example
index 5b6202b..908ef6f 100644
--- a/.env.example
+++ b/.env.example
@@ -1,3 +1,3 @@
-DATABASE_URL=postgres://tessera:password@localhost:5432/tessera
+DATABASE_URL=postgres://tessera:tessera@127.0.0.1:5435/tessera
SERVER_HOST=127.0.0.1
SERVER_PORT=9876
diff --git a/.gitignore b/.gitignore
index 2e1a70f..e26a5f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,6 @@ bun.lock
# Codegraph index (MCP tool)
.codegraph
+
+# Runtime data
+/data
diff --git a/CLAUDE.md b/CLAUDE.md
index cf8c4db..cbb6a16 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -36,7 +36,7 @@ tessera/
- Bun (`nix-shell -p bun` or install globally)
- Node.js 22+ (`nix-shell -p nodejs_22`)
- Docker (for PostgreSQL)
-- PostgreSQL container: `docker run -d --name tessera-db -e POSTGRES_USER=tessera -e POSTGRES_PASSWORD=*** -e POSTGRES_DB=tessera -p 127.0.0.1:5433:5432 postgres:17-alpine`
+- PostgreSQL container: `docker run -d --name tessera-db -e POSTGRES_USER=tessera -e POSTGRES_PASSWORD=tessera -e POSTGRES_DB=tessera -p 127.0.0.1:5435:5432 postgres:17-alpine`
### Start backend
```bash
diff --git a/drizzle/migrations/0008_sturdy_prism.sql b/drizzle/migrations/0008_sturdy_prism.sql
new file mode 100644
index 0000000..6d96e3f
--- /dev/null
+++ b/drizzle/migrations/0008_sturdy_prism.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "transaction_attachments" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "transaction_id" uuid NOT NULL,
+ "filename" text NOT NULL,
+ "mime_type" text DEFAULT 'application/octet-stream' NOT NULL,
+ "size_bytes" integer DEFAULT 0 NOT NULL,
+ "storage_path" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now()
+);
+--> statement-breakpoint
+ALTER TABLE "transaction_attachments" ADD CONSTRAINT "transaction_attachments_transaction_id_transactions_id_fk" FOREIGN KEY ("transaction_id") REFERENCES "public"."transactions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "transaction_attachments_tx_id_idx" ON "transaction_attachments" USING btree ("transaction_id");
\ No newline at end of file
diff --git a/drizzle/migrations/0009_tiny_lady_vermin.sql b/drizzle/migrations/0009_tiny_lady_vermin.sql
new file mode 100644
index 0000000..a9a735e
--- /dev/null
+++ b/drizzle/migrations/0009_tiny_lady_vermin.sql
@@ -0,0 +1 @@
+ALTER TABLE "transaction_attachments" ALTER COLUMN "transaction_id" DROP NOT NULL;
\ No newline at end of file
diff --git a/drizzle/migrations/0010_misty_morg.sql b/drizzle/migrations/0010_misty_morg.sql
new file mode 100644
index 0000000..da92b12
--- /dev/null
+++ b/drizzle/migrations/0010_misty_morg.sql
@@ -0,0 +1,15 @@
+CREATE TABLE "ticket_links" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "ticket_id" integer NOT NULL,
+ "target_ticket_id" integer NOT NULL,
+ "link_type" text NOT NULL,
+ "creator_id" uuid NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now(),
+ CONSTRAINT "ticket_links_ticket_target_type_unique" UNIQUE("ticket_id","target_ticket_id","link_type")
+);
+--> statement-breakpoint
+ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_target_ticket_id_tickets_id_fk" FOREIGN KEY ("target_ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "ticket_links" ADD CONSTRAINT "ticket_links_creator_id_users_id_fk" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "ticket_links_ticket_id_idx" ON "ticket_links" USING btree ("ticket_id");--> statement-breakpoint
+CREATE INDEX "ticket_links_target_ticket_id_idx" ON "ticket_links" USING btree ("target_ticket_id");
\ No newline at end of file
diff --git a/drizzle/migrations/0011_breezy_tyrannus.sql b/drizzle/migrations/0011_breezy_tyrannus.sql
new file mode 100644
index 0000000..5c7271e
--- /dev/null
+++ b/drizzle/migrations/0011_breezy_tyrannus.sql
@@ -0,0 +1,2 @@
+ALTER TABLE "users" ADD COLUMN "password_hash" text;--> statement-breakpoint
+ALTER TABLE "users" ADD COLUMN "role" text DEFAULT 'staff' NOT NULL;
\ No newline at end of file
diff --git a/drizzle/migrations/0012_living_photon.sql b/drizzle/migrations/0012_living_photon.sql
new file mode 100644
index 0000000..4b3ed7e
--- /dev/null
+++ b/drizzle/migrations/0012_living_photon.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "queue_permissions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "queue_id" uuid NOT NULL,
+ "team_id" uuid NOT NULL,
+ "right_name" text NOT NULL,
+ CONSTRAINT "queue_permissions_queue_team_right_unique" UNIQUE("queue_id","team_id","right_name")
+);
+--> statement-breakpoint
+ALTER TABLE "queue_permissions" ADD CONSTRAINT "queue_permissions_queue_id_queues_id_fk" FOREIGN KEY ("queue_id") REFERENCES "public"."queues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "queue_permissions" ADD CONSTRAINT "queue_permissions_team_id_teams_id_fk" FOREIGN KEY ("team_id") REFERENCES "public"."teams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "queue_permissions_queue_id_idx" ON "queue_permissions" USING btree ("queue_id");--> statement-breakpoint
+CREATE INDEX "queue_permissions_team_id_idx" ON "queue_permissions" USING btree ("team_id");
\ No newline at end of file
diff --git a/drizzle/migrations/0013_bored_silvermane.sql b/drizzle/migrations/0013_bored_silvermane.sql
new file mode 100644
index 0000000..e9c4153
--- /dev/null
+++ b/drizzle/migrations/0013_bored_silvermane.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "user_permissions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "queue_id" uuid NOT NULL,
+ "user_id" uuid NOT NULL,
+ "right_name" text NOT NULL,
+ CONSTRAINT "user_permissions_queue_user_right_unique" UNIQUE("queue_id","user_id","right_name")
+);
+--> statement-breakpoint
+ALTER TABLE "user_permissions" ADD CONSTRAINT "user_permissions_queue_id_queues_id_fk" FOREIGN KEY ("queue_id") REFERENCES "public"."queues"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "user_permissions" ADD CONSTRAINT "user_permissions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "user_permissions_queue_id_idx" ON "user_permissions" USING btree ("queue_id");--> statement-breakpoint
+CREATE INDEX "user_permissions_user_id_idx" ON "user_permissions" USING btree ("user_id");
\ No newline at end of file
diff --git a/drizzle/migrations/0014_cloudy_siren.sql b/drizzle/migrations/0014_cloudy_siren.sql
new file mode 100644
index 0000000..f2a88c4
--- /dev/null
+++ b/drizzle/migrations/0014_cloudy_siren.sql
@@ -0,0 +1 @@
+ALTER TABLE "transactions" ADD COLUMN "time_worked_minutes" integer DEFAULT 0;
\ No newline at end of file
diff --git a/drizzle/migrations/0015_tense_patch.sql b/drizzle/migrations/0015_tense_patch.sql
new file mode 100644
index 0000000..6ef3a3c
--- /dev/null
+++ b/drizzle/migrations/0015_tense_patch.sql
@@ -0,0 +1,15 @@
+CREATE TABLE "notifications" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "ticket_id" integer,
+ "type" text NOT NULL,
+ "title" text NOT NULL,
+ "body" text,
+ "read" boolean DEFAULT false NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now()
+);
+--> statement-breakpoint
+ALTER TABLE "notifications" ADD CONSTRAINT "notifications_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "notifications" ADD CONSTRAINT "notifications_ticket_id_tickets_id_fk" FOREIGN KEY ("ticket_id") REFERENCES "public"."tickets"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "notifications_user_id_idx" ON "notifications" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "notifications_user_read_idx" ON "notifications" USING btree ("user_id","read");
\ No newline at end of file
diff --git a/drizzle/migrations/0016_famous_maximus.sql b/drizzle/migrations/0016_famous_maximus.sql
new file mode 100644
index 0000000..3161801
--- /dev/null
+++ b/drizzle/migrations/0016_famous_maximus.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "api_tokens" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "user_id" uuid NOT NULL,
+ "name" text NOT NULL,
+ "token_hash" text NOT NULL,
+ "last_used_at" timestamp with time zone,
+ "created_at" timestamp with time zone DEFAULT now(),
+ CONSTRAINT "api_tokens_token_hash_unique" UNIQUE("token_hash")
+);
+--> statement-breakpoint
+ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "api_tokens_user_id_idx" ON "api_tokens" USING btree ("user_id");
\ No newline at end of file
diff --git a/drizzle/migrations/0017_redundant_the_renegades.sql b/drizzle/migrations/0017_redundant_the_renegades.sql
new file mode 100644
index 0000000..2b407f3
--- /dev/null
+++ b/drizzle/migrations/0017_redundant_the_renegades.sql
@@ -0,0 +1 @@
+ALTER TABLE "scrips" ADD COLUMN "applicable_trans_types" text;
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0008_snapshot.json b/drizzle/migrations/meta/0008_snapshot.json
new file mode 100644
index 0000000..3c5dbb0
--- /dev/null
+++ b/drizzle/migrations/meta/0008_snapshot.json
@@ -0,0 +1,1418 @@
+{
+ "id": "6af17602-5cf9-4b59-8bb3-336c7c754f1d",
+ "prevId": "4b7c344a-bcb0-48a4-8950-2ebe688dac15",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0009_snapshot.json b/drizzle/migrations/meta/0009_snapshot.json
new file mode 100644
index 0000000..7ca2934
--- /dev/null
+++ b/drizzle/migrations/meta/0009_snapshot.json
@@ -0,0 +1,1418 @@
+{
+ "id": "24c1d1a9-271c-4096-b5c3-40a11dee5923",
+ "prevId": "6af17602-5cf9-4b59-8bb3-336c7c754f1d",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0010_snapshot.json b/drizzle/migrations/meta/0010_snapshot.json
new file mode 100644
index 0000000..fe3095d
--- /dev/null
+++ b/drizzle/migrations/meta/0010_snapshot.json
@@ -0,0 +1,1550 @@
+{
+ "id": "cc6c8e10-d4d3-4834-8231-c7f3d540bca9",
+ "prevId": "24c1d1a9-271c-4096-b5c3-40a11dee5923",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0011_snapshot.json b/drizzle/migrations/meta/0011_snapshot.json
new file mode 100644
index 0000000..2d81c0d
--- /dev/null
+++ b/drizzle/migrations/meta/0011_snapshot.json
@@ -0,0 +1,1563 @@
+{
+ "id": "5cb06f19-d02d-4d40-b1d7-0842c199da77",
+ "prevId": "cc6c8e10-d4d3-4834-8231-c7f3d540bca9",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0012_snapshot.json b/drizzle/migrations/meta/0012_snapshot.json
new file mode 100644
index 0000000..76aed56
--- /dev/null
+++ b/drizzle/migrations/meta/0012_snapshot.json
@@ -0,0 +1,1669 @@
+{
+ "id": "ad7ea082-cf92-4f13-8150-9703bc1ee7d7",
+ "prevId": "5cb06f19-d02d-4d40-b1d7-0842c199da77",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0013_snapshot.json b/drizzle/migrations/meta/0013_snapshot.json
new file mode 100644
index 0000000..0416ec0
--- /dev/null
+++ b/drizzle/migrations/meta/0013_snapshot.json
@@ -0,0 +1,1775 @@
+{
+ "id": "b341f290-3cdf-42d4-92e9-92abbf7bbc80",
+ "prevId": "ad7ea082-cf92-4f13-8150-9703bc1ee7d7",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_permissions": {
+ "name": "user_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "user_permissions_queue_id_idx": {
+ "name": "user_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_permissions_user_id_idx": {
+ "name": "user_permissions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_permissions_queue_id_queues_id_fk": {
+ "name": "user_permissions_queue_id_queues_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_permissions_user_id_users_id_fk": {
+ "name": "user_permissions_user_id_users_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_permissions_queue_user_right_unique": {
+ "name": "user_permissions_queue_user_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "user_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0014_snapshot.json b/drizzle/migrations/meta/0014_snapshot.json
new file mode 100644
index 0000000..d885deb
--- /dev/null
+++ b/drizzle/migrations/meta/0014_snapshot.json
@@ -0,0 +1,1782 @@
+{
+ "id": "d164e7f3-7dd0-4552-8ea4-426c1cd5119d",
+ "prevId": "b341f290-3cdf-42d4-92e9-92abbf7bbc80",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "time_worked_minutes": {
+ "name": "time_worked_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 0
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_permissions": {
+ "name": "user_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "user_permissions_queue_id_idx": {
+ "name": "user_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_permissions_user_id_idx": {
+ "name": "user_permissions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_permissions_queue_id_queues_id_fk": {
+ "name": "user_permissions_queue_id_queues_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_permissions_user_id_users_id_fk": {
+ "name": "user_permissions_user_id_users_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_permissions_queue_user_right_unique": {
+ "name": "user_permissions_queue_user_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "user_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0015_snapshot.json b/drizzle/migrations/meta/0015_snapshot.json
new file mode 100644
index 0000000..529ee56
--- /dev/null
+++ b/drizzle/migrations/meta/0015_snapshot.json
@@ -0,0 +1,1910 @@
+{
+ "id": "e9b58a87-8767-41ce-95b5-3aba5ad6800b",
+ "prevId": "d164e7f3-7dd0-4552-8ea4-426c1cd5119d",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notifications": {
+ "name": "notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "read": {
+ "name": "read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notifications_user_id_idx": {
+ "name": "notifications_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notifications_user_read_idx": {
+ "name": "notifications_user_read_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "read",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notifications_user_id_users_id_fk": {
+ "name": "notifications_user_id_users_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_ticket_id_tickets_id_fk": {
+ "name": "notifications_ticket_id_tickets_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "time_worked_minutes": {
+ "name": "time_worked_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 0
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_permissions": {
+ "name": "user_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "user_permissions_queue_id_idx": {
+ "name": "user_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_permissions_user_id_idx": {
+ "name": "user_permissions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_permissions_queue_id_queues_id_fk": {
+ "name": "user_permissions_queue_id_queues_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_permissions_user_id_users_id_fk": {
+ "name": "user_permissions_user_id_users_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_permissions_queue_user_right_unique": {
+ "name": "user_permissions_queue_user_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "user_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0016_snapshot.json b/drizzle/migrations/meta/0016_snapshot.json
new file mode 100644
index 0000000..7ca2ff9
--- /dev/null
+++ b/drizzle/migrations/meta/0016_snapshot.json
@@ -0,0 +1,1999 @@
+{
+ "id": "ad535ff0-077f-4bf5-9cd6-c8cedfa6b4ab",
+ "prevId": "e9b58a87-8767-41ce-95b5-3aba5ad6800b",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.api_tokens": {
+ "name": "api_tokens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "api_tokens_user_id_idx": {
+ "name": "api_tokens_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "api_tokens_user_id_users_id_fk": {
+ "name": "api_tokens_user_id_users_id_fk",
+ "tableFrom": "api_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_tokens_token_hash_unique": {
+ "name": "api_tokens_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notifications": {
+ "name": "notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "read": {
+ "name": "read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notifications_user_id_idx": {
+ "name": "notifications_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notifications_user_read_idx": {
+ "name": "notifications_user_read_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "read",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notifications_user_id_users_id_fk": {
+ "name": "notifications_user_id_users_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_ticket_id_tickets_id_fk": {
+ "name": "notifications_ticket_id_tickets_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "time_worked_minutes": {
+ "name": "time_worked_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 0
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_permissions": {
+ "name": "user_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "user_permissions_queue_id_idx": {
+ "name": "user_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_permissions_user_id_idx": {
+ "name": "user_permissions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_permissions_queue_id_queues_id_fk": {
+ "name": "user_permissions_queue_id_queues_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_permissions_user_id_users_id_fk": {
+ "name": "user_permissions_user_id_users_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_permissions_queue_user_right_unique": {
+ "name": "user_permissions_queue_user_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "user_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/0017_snapshot.json b/drizzle/migrations/meta/0017_snapshot.json
new file mode 100644
index 0000000..0771d12
--- /dev/null
+++ b/drizzle/migrations/meta/0017_snapshot.json
@@ -0,0 +1,2005 @@
+{
+ "id": "8cede180-5920-4578-ac62-e1e03bf59471",
+ "prevId": "ad535ff0-077f-4bf5-9cd6-c8cedfa6b4ab",
+ "version": "7",
+ "dialect": "postgresql",
+ "tables": {
+ "public.api_tokens": {
+ "name": "api_tokens",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token_hash": {
+ "name": "token_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "last_used_at": {
+ "name": "last_used_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "api_tokens_user_id_idx": {
+ "name": "api_tokens_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "api_tokens_user_id_users_id_fk": {
+ "name": "api_tokens_user_id_users_id_fk",
+ "tableFrom": "api_tokens",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "api_tokens_token_hash_unique": {
+ "name": "api_tokens_token_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "token_hash"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_field_values": {
+ "name": "custom_field_values",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "custom_field_values_ticket_id_idx": {
+ "name": "custom_field_values_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "custom_field_values_custom_field_id_idx": {
+ "name": "custom_field_values_custom_field_id_idx",
+ "columns": [
+ {
+ "expression": "custom_field_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "custom_field_values_custom_field_id_custom_fields_id_fk": {
+ "name": "custom_field_values_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "custom_field_values_ticket_id_tickets_id_fk": {
+ "name": "custom_field_values_ticket_id_tickets_id_fk",
+ "tableFrom": "custom_field_values",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_field_values_cf_id_ticket_id_value_unique": {
+ "name": "custom_field_values_cf_id_ticket_id_value_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "custom_field_id",
+ "ticket_id",
+ "value"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.custom_fields": {
+ "name": "custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field_type": {
+ "name": "field_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "values": {
+ "name": "values",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "max_values": {
+ "name": "max_values",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 1
+ },
+ "pattern": {
+ "name": "pattern",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "custom_fields_key_unique": {
+ "name": "custom_fields_key_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "key"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboard_widgets": {
+ "name": "dashboard_widgets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "dashboard_id": {
+ "name": "dashboard_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "view_id": {
+ "name": "view_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "widget_type": {
+ "name": "widget_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "position": {
+ "name": "position",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{\"x\":0,\"y\":0,\"w\":4,\"h\":2}'"
+ },
+ "config": {
+ "name": "config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'{}'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboard_widgets_dashboard_id_dashboards_id_fk": {
+ "name": "dashboard_widgets_dashboard_id_dashboards_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "dashboards",
+ "columnsFrom": [
+ "dashboard_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "dashboard_widgets_view_id_views_id_fk": {
+ "name": "dashboard_widgets_view_id_views_id_fk",
+ "tableFrom": "dashboard_widgets",
+ "tableTo": "views",
+ "columnsFrom": [
+ "view_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.dashboards": {
+ "name": "dashboards",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "layout": {
+ "name": "layout",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "dashboards_team_id_teams_id_fk": {
+ "name": "dashboards_team_id_teams_id_fk",
+ "tableFrom": "dashboards",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.lifecycles": {
+ "name": "lifecycles",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "definition": {
+ "name": "definition",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "lifecycles_name_unique": {
+ "name": "lifecycles_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.notifications": {
+ "name": "notifications",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "title": {
+ "name": "title",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body": {
+ "name": "body",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "read": {
+ "name": "read",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "notifications_user_id_idx": {
+ "name": "notifications_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "notifications_user_read_idx": {
+ "name": "notifications_user_read_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "read",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "notifications_user_id_users_id_fk": {
+ "name": "notifications_user_id_users_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "notifications_ticket_id_tickets_id_fk": {
+ "name": "notifications_ticket_id_tickets_id_fk",
+ "tableFrom": "notifications",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_custom_fields": {
+ "name": "queue_custom_fields",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "custom_field_id": {
+ "name": "custom_field_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queue_custom_fields_queue_id_queues_id_fk": {
+ "name": "queue_custom_fields_queue_id_queues_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queue_custom_fields_custom_field_id_custom_fields_id_fk": {
+ "name": "queue_custom_fields_custom_field_id_custom_fields_id_fk",
+ "tableFrom": "queue_custom_fields",
+ "tableTo": "custom_fields",
+ "columnsFrom": [
+ "custom_field_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_custom_fields_queue_id_custom_field_id_unique": {
+ "name": "queue_custom_fields_queue_id_custom_field_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "custom_field_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queue_permissions": {
+ "name": "queue_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "queue_permissions_queue_id_idx": {
+ "name": "queue_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "queue_permissions_team_id_idx": {
+ "name": "queue_permissions_team_id_idx",
+ "columns": [
+ {
+ "expression": "team_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "queue_permissions_queue_id_queues_id_fk": {
+ "name": "queue_permissions_queue_id_queues_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "queue_permissions_team_id_teams_id_fk": {
+ "name": "queue_permissions_team_id_teams_id_fk",
+ "tableFrom": "queue_permissions",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queue_permissions_queue_team_right_unique": {
+ "name": "queue_permissions_queue_team_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "team_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.queues": {
+ "name": "queues",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "lifecycle_id": {
+ "name": "lifecycle_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "queues_lifecycle_id_lifecycles_id_fk": {
+ "name": "queues_lifecycle_id_lifecycles_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "lifecycles",
+ "columnsFrom": [
+ "lifecycle_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "queues_team_id_teams_id_fk": {
+ "name": "queues_team_id_teams_id_fk",
+ "tableFrom": "queues",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "queues_name_unique": {
+ "name": "queues_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.scrips": {
+ "name": "scrips",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "condition_type": {
+ "name": "condition_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "condition_config": {
+ "name": "condition_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "action_type": {
+ "name": "action_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "action_config": {
+ "name": "action_config",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'{}'::jsonb"
+ },
+ "template_id": {
+ "name": "template_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "stage": {
+ "name": "stage",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'TransactionCreate'"
+ },
+ "sort_order": {
+ "name": "sort_order",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "disabled": {
+ "name": "disabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "applicable_trans_types": {
+ "name": "applicable_trans_types",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "scrips_queue_id_idx": {
+ "name": "scrips_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "scrips_queue_id_queues_id_fk": {
+ "name": "scrips_queue_id_queues_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "scrips_template_id_templates_id_fk": {
+ "name": "scrips_template_id_templates_id_fk",
+ "tableFrom": "scrips",
+ "tableTo": "templates",
+ "columnsFrom": [
+ "template_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.team_members": {
+ "name": "team_members",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "team_members_team_id_teams_id_fk": {
+ "name": "team_members_team_id_teams_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "team_members_user_id_users_id_fk": {
+ "name": "team_members_user_id_users_id_fk",
+ "tableFrom": "team_members",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "team_members_team_id_user_id_unique": {
+ "name": "team_members_team_id_user_id_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "team_id",
+ "user_id"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.teams": {
+ "name": "teams",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "teams_name_unique": {
+ "name": "teams_name_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.templates": {
+ "name": "templates",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "subject_template": {
+ "name": "subject_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "body_template": {
+ "name": "body_template",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "templates_queue_id_queues_id_fk": {
+ "name": "templates_queue_id_queues_id_fk",
+ "tableFrom": "templates",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ticket_links": {
+ "name": "ticket_links",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "target_ticket_id": {
+ "name": "target_ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "link_type": {
+ "name": "link_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "ticket_links_ticket_id_idx": {
+ "name": "ticket_links_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "ticket_links_target_ticket_id_idx": {
+ "name": "ticket_links_target_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "target_ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "ticket_links_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_target_ticket_id_tickets_id_fk": {
+ "name": "ticket_links_target_ticket_id_tickets_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "target_ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "ticket_links_creator_id_users_id_fk": {
+ "name": "ticket_links_creator_id_users_id_fk",
+ "tableFrom": "ticket_links",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ticket_links_ticket_target_type_unique": {
+ "name": "ticket_links_ticket_target_type_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "ticket_id",
+ "target_ticket_id",
+ "link_type"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.tickets": {
+ "name": "tickets",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "integer",
+ "primaryKey": true,
+ "notNull": true,
+ "identity": {
+ "type": "always",
+ "name": "tickets_id_seq",
+ "schema": "public",
+ "increment": "1",
+ "startWith": "1",
+ "minValue": "1",
+ "maxValue": "2147483647",
+ "cache": "1",
+ "cycle": false
+ }
+ },
+ "subject": {
+ "name": "subject",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "owner_id": {
+ "name": "owner_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "team_id": {
+ "name": "team_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "resolved_at": {
+ "name": "resolved_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "tickets_queue_id_idx": {
+ "name": "tickets_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "tickets_status_idx": {
+ "name": "tickets_status_idx",
+ "columns": [
+ {
+ "expression": "status",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "tickets_queue_id_queues_id_fk": {
+ "name": "tickets_queue_id_queues_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_owner_id_users_id_fk": {
+ "name": "tickets_owner_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "owner_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ },
+ "tickets_team_id_teams_id_fk": {
+ "name": "tickets_team_id_teams_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "teams",
+ "columnsFrom": [
+ "team_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "set null",
+ "onUpdate": "no action"
+ },
+ "tickets_creator_id_users_id_fk": {
+ "name": "tickets_creator_id_users_id_fk",
+ "tableFrom": "tickets",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transaction_attachments": {
+ "name": "transaction_attachments",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "transaction_id": {
+ "name": "transaction_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "filename": {
+ "name": "filename",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'application/octet-stream'"
+ },
+ "size_bytes": {
+ "name": "size_bytes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "storage_path": {
+ "name": "storage_path",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transaction_attachments_tx_id_idx": {
+ "name": "transaction_attachments_tx_id_idx",
+ "columns": [
+ {
+ "expression": "transaction_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transaction_attachments_transaction_id_transactions_id_fk": {
+ "name": "transaction_attachments_transaction_id_transactions_id_fk",
+ "tableFrom": "transaction_attachments",
+ "tableTo": "transactions",
+ "columnsFrom": [
+ "transaction_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.transactions": {
+ "name": "transactions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "ticket_id": {
+ "name": "ticket_id",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "transaction_type": {
+ "name": "transaction_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "field": {
+ "name": "field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "old_value": {
+ "name": "old_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "new_value": {
+ "name": "new_value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "time_worked_minutes": {
+ "name": "time_worked_minutes",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 0
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "transactions_ticket_id_idx": {
+ "name": "transactions_ticket_id_idx",
+ "columns": [
+ {
+ "expression": "ticket_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "transactions_created_at_idx": {
+ "name": "transactions_created_at_idx",
+ "columns": [
+ {
+ "expression": "created_at",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "transactions_ticket_id_tickets_id_fk": {
+ "name": "transactions_ticket_id_tickets_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "tickets",
+ "columnsFrom": [
+ "ticket_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "transactions_creator_id_users_id_fk": {
+ "name": "transactions_creator_id_users_id_fk",
+ "tableFrom": "transactions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.user_permissions": {
+ "name": "user_permissions",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "queue_id": {
+ "name": "queue_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "right_name": {
+ "name": "right_name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "user_permissions_queue_id_idx": {
+ "name": "user_permissions_queue_id_idx",
+ "columns": [
+ {
+ "expression": "queue_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "user_permissions_user_id_idx": {
+ "name": "user_permissions_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "user_permissions_queue_id_queues_id_fk": {
+ "name": "user_permissions_queue_id_queues_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "queues",
+ "columnsFrom": [
+ "queue_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "user_permissions_user_id_users_id_fk": {
+ "name": "user_permissions_user_id_users_id_fk",
+ "tableFrom": "user_permissions",
+ "tableTo": "users",
+ "columnsFrom": [
+ "user_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "user_permissions_queue_user_right_unique": {
+ "name": "user_permissions_queue_user_right_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "queue_id",
+ "user_id",
+ "right_name"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.users": {
+ "name": "users",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "username": {
+ "name": "username",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "password_hash": {
+ "name": "password_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'staff'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "users_username_unique": {
+ "name": "users_username_unique",
+ "nullsNotDistinct": false,
+ "columns": [
+ "username"
+ ]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.views": {
+ "name": "views",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "filters": {
+ "name": "filters",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'"
+ },
+ "sort_key": {
+ "name": "sort_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'updated'"
+ },
+ "columns": {
+ "name": "columns",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "'[]'"
+ },
+ "is_public": {
+ "name": "is_public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ },
+ "creator_id": {
+ "name": "creator_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "views_creator_id_users_id_fk": {
+ "name": "views_creator_id_users_id_fk",
+ "tableFrom": "views",
+ "tableTo": "users",
+ "columnsFrom": [
+ "creator_id"
+ ],
+ "columnsTo": [
+ "id"
+ ],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ }
+ },
+ "enums": {},
+ "schemas": {},
+ "sequences": {},
+ "roles": {},
+ "policies": {},
+ "views": {},
+ "_meta": {
+ "columns": {},
+ "schemas": {},
+ "tables": {}
+ }
+}
\ No newline at end of file
diff --git a/drizzle/migrations/meta/_journal.json b/drizzle/migrations/meta/_journal.json
index f217a4b..95295b0 100644
--- a/drizzle/migrations/meta/_journal.json
+++ b/drizzle/migrations/meta/_journal.json
@@ -57,6 +57,76 @@
"when": 1781009018666,
"tag": "0007_flimsy_roughhouse",
"breakpoints": true
+ },
+ {
+ "idx": 8,
+ "version": "7",
+ "when": 1781039674211,
+ "tag": "0008_sturdy_prism",
+ "breakpoints": true
+ },
+ {
+ "idx": 9,
+ "version": "7",
+ "when": 1781039770418,
+ "tag": "0009_tiny_lady_vermin",
+ "breakpoints": true
+ },
+ {
+ "idx": 10,
+ "version": "7",
+ "when": 1781040536590,
+ "tag": "0010_misty_morg",
+ "breakpoints": true
+ },
+ {
+ "idx": 11,
+ "version": "7",
+ "when": 1781042321413,
+ "tag": "0011_breezy_tyrannus",
+ "breakpoints": true
+ },
+ {
+ "idx": 12,
+ "version": "7",
+ "when": 1781043175153,
+ "tag": "0012_living_photon",
+ "breakpoints": true
+ },
+ {
+ "idx": 13,
+ "version": "7",
+ "when": 1781043729230,
+ "tag": "0013_bored_silvermane",
+ "breakpoints": true
+ },
+ {
+ "idx": 14,
+ "version": "7",
+ "when": 1781045611610,
+ "tag": "0014_cloudy_siren",
+ "breakpoints": true
+ },
+ {
+ "idx": 15,
+ "version": "7",
+ "when": 1781078349499,
+ "tag": "0015_tense_patch",
+ "breakpoints": true
+ },
+ {
+ "idx": 16,
+ "version": "7",
+ "when": 1781078511943,
+ "tag": "0016_famous_maximus",
+ "breakpoints": true
+ },
+ {
+ "idx": 17,
+ "version": "7",
+ "when": 1781095552496,
+ "tag": "0017_redundant_the_renegades",
+ "breakpoints": true
}
]
}
\ No newline at end of file
diff --git a/package.json b/package.json
index 1f96fc5..a0b3098 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
},
"dependencies": {
"@types/nodemailer": "^8.0.0",
+ "jose": "^6.2.3",
"nodemailer": "^8.0.10"
}
}
diff --git a/scripts/seed-users.ts b/scripts/seed-users.ts
new file mode 100644
index 0000000..6e61e62
--- /dev/null
+++ b/scripts/seed-users.ts
@@ -0,0 +1,28 @@
+import { drizzle } from 'drizzle-orm/node-postgres';
+import { Pool } from 'pg';
+import { users } from '../src/db/schema.ts';
+
+const pool = new Pool({ connectionString: process.env.DATABASE_URL! });
+const db = drizzle(pool);
+
+const BATCH = 100;
+const TOTAL = 1000;
+const password = await Bun.password.hash('password');
+
+console.log(`Inserting ${TOTAL} users...`);
+for (let i = 0; i < TOTAL; i += BATCH) {
+ const batch = [];
+ for (let j = i; j < Math.min(i + BATCH, TOTAL); j++) {
+ const n = String(j).padStart(4, '0');
+ batch.push({
+ username: `user${n}`,
+ email: `user${n}@test.local`,
+ role: 'staff',
+ password_hash: password,
+ });
+ }
+ await db.insert(users).values(batch as any).onConflictDoNothing();
+ process.stdout.write('.');
+}
+console.log(`\nDone. ${TOTAL} users seeded.`);
+await pool.end();
diff --git a/src/auth/middleware.ts b/src/auth/middleware.ts
new file mode 100644
index 0000000..8c279e3
--- /dev/null
+++ b/src/auth/middleware.ts
@@ -0,0 +1,144 @@
+import type { Context, Next } from 'hono';
+import { HTTPException } from 'hono/http-exception';
+import * as jose from 'jose';
+import { config } from '../config.ts';
+import type { Db } from '../db/index.ts';
+import { users, apiTokens } from '../db/schema.ts';
+import { eq } from 'drizzle-orm';
+
+export interface AuthUser {
+ userId: string;
+ username: string;
+ role: string;
+}
+
+declare module 'hono' {
+ interface ContextVariableMap {
+ user: AuthUser;
+ }
+}
+
+const secret = new TextEncoder().encode(config.JWT_SECRET);
+
+export async function createToken(user: { id: string; username: string; role: string }): Promise {
+ return await new jose.SignJWT({ username: user.username, role: user.role })
+ .setProtectedHeader({ alg: 'HS256' })
+ .setSubject(user.id)
+ .setIssuedAt()
+ .setExpirationTime('7d')
+ .sign(secret);
+}
+
+async function verifyJwt(token: string): Promise {
+ try {
+ const { payload } = await jose.jwtVerify(token, secret);
+ return {
+ userId: payload.sub!,
+ username: payload.username as string,
+ role: payload.role as string,
+ };
+ } catch {
+ return null;
+ }
+}
+
+async function verifyApiToken(db: Db, token: string): Promise {
+ try {
+ // Find all tokens and verify against hash
+ const allTokens = await db.query.apiTokens.findMany();
+ for (const t of allTokens) {
+ const valid = await Bun.password.verify(token, t.token_hash);
+ if (valid) {
+ // Update last_used_at
+ await db.update(apiTokens)
+ .set({ last_used_at: new Date() } as any)
+ .where(eq(apiTokens.id, t.id));
+
+ const user = await db.query.users.findFirst({
+ where: eq(users.id, t.user_id),
+ });
+ if (user) {
+ return {
+ userId: user.id,
+ username: user.username,
+ role: user.role,
+ };
+ }
+ }
+ }
+ return null;
+ } catch {
+ return null;
+ }
+}
+
+function extractToken(c: Context): string | null {
+ const auth = c.req.header('Authorization');
+ if (auth?.startsWith('Bearer ')) {
+ return auth.slice(7);
+ }
+
+ const cookie = c.req.header('Cookie');
+ if (cookie) {
+ const match = cookie.match(/(?:^|;\s*)token=([^;]*)/);
+ if (match?.[1]) return match[1];
+ }
+
+ return null;
+}
+
+export function createAuthMiddleware(db: Db) {
+ async function verifyToken(token: string): Promise {
+ if (token.startsWith('tessera_')) {
+ return await verifyApiToken(db, token);
+ }
+ return await verifyJwt(token);
+ }
+
+ async function requireAuth(c: Context, next: Next) {
+ const token = extractToken(c);
+ if (!token) {
+ throw new HTTPException(401, { message: 'Authentication required' });
+ }
+ const user = await verifyToken(token);
+ if (!user) {
+ throw new HTTPException(401, { message: 'Invalid or expired token' });
+ }
+ c.set('user', user);
+ await next();
+ }
+
+ async function requireAdmin(c: Context, next: Next) {
+ const token = extractToken(c);
+ if (!token) {
+ throw new HTTPException(401, { message: 'Authentication required' });
+ }
+ const user = await verifyToken(token);
+ if (!user) {
+ throw new HTTPException(401, { message: 'Invalid or expired token' });
+ }
+ if (user.role !== 'admin') {
+ throw new HTTPException(403, { message: 'Admin access required' });
+ }
+ c.set('user', user);
+ await next();
+ }
+
+ async function optionalAuth(c: Context, next: Next) {
+ const token = extractToken(c);
+ if (token) {
+ const user = await verifyToken(token);
+ if (user) {
+ c.set('user', user);
+ }
+ }
+ await next();
+ }
+
+ return { requireAuth, requireAdmin, optionalAuth };
+}
+
+export function getUserId(c: Context): string {
+ const user = c.get('user');
+ return user?.userId ?? '00000000-0000-0000-0000-000000000000';
+}
diff --git a/src/auth/permissions.ts b/src/auth/permissions.ts
new file mode 100644
index 0000000..be7bb84
--- /dev/null
+++ b/src/auth/permissions.ts
@@ -0,0 +1,86 @@
+import { HTTPException } from 'hono/http-exception';
+import type { Context } from 'hono';
+import type { Db } from '../db/index.ts';
+import { teamMembers, queuePermissions, userPermissions } from '../db/schema.ts';
+import { and, eq, inArray } from 'drizzle-orm';
+import type { AuthUser } from './middleware.ts';
+
+export type TicketRight = 'ticket.view' | 'ticket.create' | 'ticket.reply' | 'ticket.comment' | 'ticket.modify' | 'queue.admin';
+
+const RIGHT_HIERARCHY: Record = {
+ 'queue.admin': ['ticket.view', 'ticket.create', 'ticket.reply', 'ticket.comment', 'ticket.modify', 'queue.admin'],
+ 'ticket.modify': ['ticket.view', 'ticket.reply', 'ticket.comment', 'ticket.modify'],
+ 'ticket.reply': ['ticket.view', 'ticket.reply'],
+ 'ticket.comment': ['ticket.view', 'ticket.comment'],
+ 'ticket.create': ['ticket.create'],
+ 'ticket.view': ['ticket.view'],
+};
+
+/**
+ * Check whether a user has a specific right on a queue.
+ * Admins bypass all permission checks.
+ * Rights come from two sources: team memberships and per-user grants.
+ * Higher rights imply lower rights (e.g., queue.admin implies ticket.view).
+ */
+export async function userHasRight(
+ db: Db,
+ user: AuthUser,
+ queueId: string,
+ right: TicketRight,
+): Promise {
+ // Admins have all rights
+ if (user.role === 'admin') return true;
+
+ const neededRights = RIGHT_HIERARCHY[right] ?? [right];
+
+ // Check per-user permissions first (direct grant)
+ const userPerm = await db.query.userPermissions.findFirst({
+ where: (table, { and, eq: eqFn, inArray: inArr }) =>
+ and(
+ eqFn(table.user_id, user.userId),
+ eqFn(table.queue_id, queueId),
+ inArr(table.right_name, neededRights),
+ ),
+ });
+
+ if (userPerm) return true;
+
+ // Check team permissions (inherited)
+ const memberships = await db.query.teamMembers.findMany({
+ where: eq(teamMembers.user_id, user.userId),
+ });
+
+ const teamIds = memberships.map((m) => m.team_id);
+ if (teamIds.length === 0) return false;
+
+ const teamPerm = await db.query.queuePermissions.findFirst({
+ where: (table, { and, eq: eqFn, inArray: inArr }) =>
+ and(
+ inArr(table.team_id, teamIds),
+ eqFn(table.queue_id, queueId),
+ inArr(table.right_name, neededRights),
+ ),
+ });
+
+ return teamPerm !== undefined;
+}
+
+/**
+ * Require a specific right on a queue. Throws 403 if the user lacks the right.
+ */
+export async function requireRight(
+ c: Context,
+ db: Db,
+ queueId: string,
+ right: TicketRight,
+): Promise {
+ const user = c.get('user');
+ if (!user) {
+ throw new HTTPException(401, { message: 'Authentication required' });
+ }
+
+ const has = await userHasRight(db, user, queueId, right);
+ if (!has) {
+ throw new HTTPException(403, { message: `Missing required right: ${right}` });
+ }
+}
diff --git a/src/config.ts b/src/config.ts
index aa59af0..7eef0a6 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -9,6 +9,8 @@ const configSchema = z.object({
SMTP_USER: z.string().optional(),
SMTP_PASS: z.string().optional(),
SMTP_FROM: z.string().default('tessera@localhost'),
+ UPLOAD_DIR: z.string().default('./data/uploads'),
+ JWT_SECRET: z.string().default('tessera-dev-secret-change-in-production'),
});
export const config = configSchema.parse(process.env);
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 3147239..ad67253 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -5,6 +5,8 @@ export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
username: text('username').notNull().unique(),
email: text('email'),
+ password_hash: text('password_hash'),
+ role: text('role').notNull().default('staff'),
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
});
@@ -49,6 +51,7 @@ export const transactions = pgTable('transactions', {
old_value: text('old_value'),
new_value: text('new_value'),
data: jsonb('data'),
+ time_worked_minutes: integer('time_worked_minutes').default(0),
creator_id: uuid('creator_id').notNull().references(() => users.id),
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
}, (table) => ({
@@ -78,6 +81,7 @@ export const scrips = pgTable('scrips', {
stage: text('stage').notNull().default('TransactionCreate'),
sort_order: integer('sort_order').notNull().default(0),
disabled: boolean('disabled').notNull().default(false),
+ applicable_trans_types: text('applicable_trans_types'),
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
}, (table) => ({
queueIdIdx: index('scrips_queue_id_idx').on(table.queue_id),
@@ -160,6 +164,78 @@ export const dashboards = pgTable('dashboards', {
created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
});
+export const transactionAttachments = pgTable('transaction_attachments', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ transaction_id: uuid('transaction_id').references(() => transactions.id, { onDelete: 'cascade' }),
+ filename: text('filename').notNull(),
+ mime_type: text('mime_type').notNull().default('application/octet-stream'),
+ size_bytes: integer('size_bytes').notNull().default(0),
+ storage_path: text('storage_path').notNull(),
+ created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
+}, (table) => ({
+ transactionIdIdx: index('transaction_attachments_tx_id_idx').on(table.transaction_id),
+}));
+
+export const queuePermissions = pgTable('queue_permissions', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ queue_id: uuid('queue_id').notNull().references(() => queues.id, { onDelete: 'cascade' }),
+ team_id: uuid('team_id').notNull().references(() => teams.id, { onDelete: 'cascade' }),
+ right_name: text('right_name').notNull(),
+}, (table) => ({
+ uniqueRight: unique('queue_permissions_queue_team_right_unique').on(table.queue_id, table.team_id, table.right_name),
+ queueIdIdx: index('queue_permissions_queue_id_idx').on(table.queue_id),
+ teamIdIdx: index('queue_permissions_team_id_idx').on(table.team_id),
+}));
+
+export const apiTokens = pgTable('api_tokens', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ user_id: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
+ name: text('name').notNull(),
+ token_hash: text('token_hash').notNull().unique(),
+ last_used_at: timestamp('last_used_at', { withTimezone: true }),
+ created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
+}, (table) => ({
+ userIdIdx: index('api_tokens_user_id_idx').on(table.user_id),
+}));
+
+export const notifications = pgTable('notifications', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ user_id: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
+ ticket_id: integer('ticket_id').references(() => tickets.id, { onDelete: 'cascade' }),
+ type: text('type').notNull(), // 'assigned', 'mentioned', 'commented', 'scrip_fired'
+ title: text('title').notNull(),
+ body: text('body'),
+ read: boolean('read').notNull().default(false),
+ created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
+}, (table) => ({
+ userIdIdx: index('notifications_user_id_idx').on(table.user_id),
+ unreadIdx: index('notifications_user_read_idx').on(table.user_id, table.read),
+}));
+
+export const userPermissions = pgTable('user_permissions', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ queue_id: uuid('queue_id').notNull().references(() => queues.id, { onDelete: 'cascade' }),
+ user_id: uuid('user_id').notNull().references(() => users.id, { onDelete: 'cascade' }),
+ right_name: text('right_name').notNull(),
+}, (table) => ({
+ uniqueRight: unique('user_permissions_queue_user_right_unique').on(table.queue_id, table.user_id, table.right_name),
+ queueIdIdx: index('user_permissions_queue_id_idx').on(table.queue_id),
+ userIdIdx: index('user_permissions_user_id_idx').on(table.user_id),
+}));
+
+export const ticketLinks = pgTable('ticket_links', {
+ id: uuid('id').primaryKey().defaultRandom(),
+ ticket_id: integer('ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
+ target_ticket_id: integer('target_ticket_id').notNull().references(() => tickets.id, { onDelete: 'cascade' }),
+ link_type: text('link_type').notNull(),
+ creator_id: uuid('creator_id').notNull().references(() => users.id),
+ created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
+}, (table) => ({
+ uniqueLink: unique('ticket_links_ticket_target_type_unique').on(table.ticket_id, table.target_ticket_id, table.link_type),
+ ticketIdIdx: index('ticket_links_ticket_id_idx').on(table.ticket_id),
+ targetTicketIdIdx: index('ticket_links_target_ticket_id_idx').on(table.target_ticket_id),
+}));
+
export const dashboardWidgets = pgTable('dashboard_widgets', {
id: uuid('id').primaryKey().defaultRandom(),
dashboard_id: uuid('dashboard_id').notNull().references(() => dashboards.id, { onDelete: 'cascade' }),
diff --git a/src/db/seed.ts b/src/db/seed.ts
index fe5571f..bf4c02f 100644
--- a/src/db/seed.ts
+++ b/src/db/seed.ts
@@ -7,8 +7,11 @@ import {
customFieldValues,
lifecycles,
queueCustomFields,
+ queuePermissions,
queues,
scrips,
+ teamMembers,
+ teams,
templates,
tickets,
transactions,
@@ -52,7 +55,7 @@ function createSeedDb(pool: Pool) {
}
type Db = ReturnType;
-type UserSeed = { id: string; username: string; email: string };
+type UserSeed = { id: string; username: string; email: string; role?: string; password_hash?: string };
type QueueSeed = { name: string; description: string };
type FieldSeed = {
key?: string;
@@ -73,12 +76,19 @@ function makeFieldKey(value: string): string {
}
async function ensureUser(db: Db, seed: UserSeed): Promise {
+ const setData = {
+ username: seed.username,
+ email: seed.email,
+ role: seed.role ?? 'staff',
+ password_hash: seed.password_hash ?? null,
+ };
+
const existingById = await db.query.users.findFirst({
where: eq(users.id, seed.id),
});
if (existingById) {
await db.update(users)
- .set({ username: seed.username, email: seed.email })
+ .set(setData)
.where(eq(users.id, seed.id));
return existingById.id;
}
@@ -88,12 +98,12 @@ async function ensureUser(db: Db, seed: UserSeed): Promise {
});
if (existingByUsername) {
await db.update(users)
- .set({ email: seed.email })
+ .set(setData)
.where(eq(users.id, existingByUsername.id));
return existingByUsername.id;
}
- const [created] = await db.insert(users).values(seed).returning();
+ const [created] = await db.insert(users).values({ ...seed, ...setData }).returning();
if (!created) throw new Error(`Failed to seed user ${seed.username}`);
return created.id;
}
@@ -315,6 +325,7 @@ async function ensureTicket(
async function resetDatabase(db: Db) {
await db.delete(customFieldValues);
+ await db.delete(queuePermissions);
await db.delete(transactions);
await db.delete(queueCustomFields);
await db.delete(dashboardWidgets);
@@ -346,34 +357,68 @@ async function main() {
await resetDatabase(db);
}
+ const userPassword = await Bun.password.hash('password');
+ const adminPassword = await Bun.password.hash('admin');
+
const userIds = {
system: await ensureUser(db, {
id: SYSTEM_USER_ID,
username: 'system',
email: 'system@tessera.local',
}),
+ admin: await ensureUser(db, {
+ id: 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa',
+ username: 'admin',
+ email: 'admin@tessera.local',
+ role: 'admin',
+ password_hash: adminPassword,
+ }),
dispatcher: await ensureUser(db, {
id: '11111111-1111-4111-8111-111111111111',
username: 'maria.dispatch',
email: 'maria.dispatch@tessera.local',
+ password_hash: userPassword,
}),
technician: await ensureUser(db, {
id: '22222222-2222-4222-8222-222222222222',
username: 'liam.field',
email: 'liam.field@tessera.local',
+ password_hash: userPassword,
}),
facilities: await ensureUser(db, {
id: '33333333-3333-4333-8333-333333333333',
username: 'nora.facilities',
email: 'nora.facilities@tessera.local',
+ password_hash: userPassword,
}),
security: await ensureUser(db, {
id: '44444444-4444-4444-8444-444444444444',
username: 'sam.security',
email: 'sam.security@tessera.local',
+ password_hash: userPassword,
}),
};
+ // Create demo team and assign all staff users to it
+ const [supportTeam] = await db.insert(teams).values({
+ name: 'Support team',
+ description: 'Demo support team with full queue access',
+ }).onConflictDoUpdate({
+ target: teams.name,
+ set: { description: 'Demo support team with full queue access' },
+ }).returning();
+
+ if (supportTeam) {
+ // Add all staff users to the team
+ const staffIds = [userIds.dispatcher, userIds.technician, userIds.facilities, userIds.security];
+ for (const userId of staffIds) {
+ await db.insert(teamMembers).values({
+ team_id: supportTeam.id,
+ user_id: userId,
+ }).onConflictDoNothing();
+ }
+ }
+
const lifecycle = await ensureLifecycle(db);
const supportQueue = await ensureQueue(db, lifecycle.id, {
@@ -432,6 +477,20 @@ async function main() {
await attachFieldToQueue(db, fieldQueue.id, assetField.id, 40);
await attachFieldToQueue(db, supportQueue.id, outcomeField.id, 50);
+ // Grant the support team full access to all demo queues
+ if (supportTeam) {
+ const allRights = ['ticket.view', 'ticket.create', 'ticket.reply', 'ticket.comment', 'ticket.modify'];
+ for (const queue of [supportQueue, fieldQueue, facilitiesQueue, securityQueue]) {
+ for (const right of allRights) {
+ await db.insert(queuePermissions).values({
+ queue_id: queue.id,
+ team_id: supportTeam.id,
+ right_name: right,
+ }).onConflictDoNothing();
+ }
+ }
+ }
+
const resolveTemplate = await ensureTemplate(
db,
'Demo resolution note',
diff --git a/src/index.ts b/src/index.ts
index 18ff469..0c3444f 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -4,6 +4,7 @@ import { createDb } from './db/index.ts';
import type { Db } from './db/index.ts';
import { errorHandler } from './middleware/error.ts';
import { requestLogger } from './middleware/logging.ts';
+import { createAuthMiddleware } from './auth/middleware.ts';
import healthRouter from './routes/health.ts';
import { createTicketsRouter } from './routes/tickets.ts';
import { createQueuesRouter } from './routes/queues.ts';
@@ -15,6 +16,11 @@ import { createTemplatesRouter } from './routes/templates.ts';
import { createViewsRouter } from './routes/views.ts';
import { createDashboardsRouter } from './routes/dashboards.ts';
import { createTeamsRouter } from './routes/teams.ts';
+import { createAttachmentsRouter } from './routes/attachments.ts';
+import { createAuthRouter } from './routes/auth.ts';
+import { createQueuePermissionsRouter } from './routes/queue-permissions.ts';
+import { createNotificationsRouter } from './routes/notifications.ts';
+import { startScheduler } from './scrip/scheduler.ts';
let db: Db | null = null;
@@ -30,17 +36,39 @@ const app = new Hono();
app.use('*', requestLogger);
app.onError(errorHandler);
+const { requireAuth, requireAdmin } = createAuthMiddleware(getDb());
+
+// Public routes
app.route('/health', healthRouter);
-app.route('/tickets', createTicketsRouter(getDb()));
-app.route('/queues', createQueuesRouter(getDb()));
-app.route('/scrips', createScripsRouter(getDb()));
-app.route('/custom-fields', createCustomFieldsRouter(getDb()));
-app.route('/lifecycles', createLifecyclesRouter(getDb()));
-app.route('/users', createUsersRouter(getDb()));
-app.route('/templates', createTemplatesRouter(getDb()));
-app.route('/views', createViewsRouter(getDb()));
-app.route('/dashboards', createDashboardsRouter(getDb()));
-app.route('/teams', createTeamsRouter(getDb()));
+app.route('/', createAuthRouter(getDb()));
+
+// Ticket routes โ require authentication
+const ticketsWithAuth = new Hono();
+ticketsWithAuth.use('*', requireAuth);
+ticketsWithAuth.route('/tickets', createTicketsRouter(getDb()));
+ticketsWithAuth.route('/', createNotificationsRouter(getDb()));
+app.route('/', ticketsWithAuth);
+
+// Attachment serving โ require authentication
+const attachmentsWithAuth = new Hono();
+attachmentsWithAuth.use('*', requireAuth);
+attachmentsWithAuth.route('/', createAttachmentsRouter(getDb()));
+app.route('/', attachmentsWithAuth);
+
+// Admin routes โ require admin role
+const admin = new Hono();
+admin.use('*', requireAdmin);
+admin.route('/queues', createQueuesRouter(getDb()));
+admin.route('/scrips', createScripsRouter(getDb()));
+admin.route('/custom-fields', createCustomFieldsRouter(getDb()));
+admin.route('/lifecycles', createLifecyclesRouter(getDb()));
+admin.route('/users', createUsersRouter(getDb()));
+admin.route('/templates', createTemplatesRouter(getDb()));
+admin.route('/views', createViewsRouter(getDb()));
+admin.route('/dashboards', createDashboardsRouter(getDb()));
+admin.route('/teams', createTeamsRouter(getDb()));
+admin.route('/', createQueuePermissionsRouter(getDb()));
+app.route('/', admin);
export default app;
export { app };
@@ -54,4 +82,7 @@ if (Bun.main === import.meta.path) {
development: false,
});
console.log(`Server running at http://${config.SERVER_HOST}:${config.SERVER_PORT}`);
+
+ // Start the scrip scheduler (runs every 5 minutes)
+ startScheduler(getDb());
}
diff --git a/src/lifecycle/validator.ts b/src/lifecycle/validator.ts
index 296dbce..61bafd0 100644
--- a/src/lifecycle/validator.ts
+++ b/src/lifecycle/validator.ts
@@ -5,13 +5,17 @@ export interface LifecycleDefinition {
inactive: string[];
};
transitions: Record;
+ transition_rights?: Record; // "fromโto" โ rightName
}
export interface ValidationResult {
valid: boolean;
error?: string;
+ requiredRight?: string; // Named right required for this transition, if any
}
+const FALLBACK_RIGHT = 'ticket.modify';
+
export class LifecycleValidator {
validateTransition(
lifecycleDef: LifecycleDefinition,
@@ -35,13 +39,15 @@ export class LifecycleValidator {
const allowedTransitions = this.getAllowedTransitions(lifecycleDef, fromStatus);
if (allowedTransitions.includes(toStatus)) {
- return { valid: true };
+ const right = this.getRequiredRight(lifecycleDef, fromStatus, toStatus);
+ return { valid: true, requiredRight: right ?? FALLBACK_RIGHT };
}
// Also handle wildcard "*" -> any transition
const wildcardTransitions = this.getAllowedTransitions(lifecycleDef, '*');
if (wildcardTransitions.includes(toStatus)) {
- return { valid: true };
+ const right = this.getRequiredRight(lifecycleDef, fromStatus, toStatus);
+ return { valid: true, requiredRight: right ?? FALLBACK_RIGHT };
}
return {
@@ -50,6 +56,37 @@ export class LifecycleValidator {
};
}
+ /**
+ * Get the required right for a transition using RT's 4-level priority:
+ * 1. exact "fromโto"
+ * 2. wildcard from "*โto"
+ * 3. wildcard to "fromโ*"
+ * 4. full wildcard "*โ*"
+ * 5. fallback: ticket.modify
+ */
+ getRequiredRight(
+ lifecycleDef: LifecycleDefinition,
+ fromStatus: string,
+ toStatus: string,
+ ): string | null {
+ const rights = lifecycleDef.transition_rights ?? {};
+
+ // Priority 1: exact match
+ if (rights[`${fromStatus}โ${toStatus}`]) return rights[`${fromStatus}โ${toStatus}`];
+
+ // Priority 2: wildcard from
+ if (rights[`*โ${toStatus}`]) return rights[`*โ${toStatus}`];
+
+ // Priority 3: wildcard to
+ if (rights[`${fromStatus}โ*`]) return rights[`${fromStatus}โ*`];
+
+ // Priority 4: full wildcard
+ if (rights['*โ*']) return rights['*โ*'];
+
+ // Priority 5: fallback
+ return null;
+ }
+
isResolvedStatus(lifecycleDef: LifecycleDefinition, status: string): boolean {
return lifecycleDef.statuses.inactive.includes(status);
}
@@ -58,16 +95,12 @@ export class LifecycleValidator {
lifecycleDef: LifecycleDefinition,
fromStatus: string,
): string[] {
- // Direct transition
if (lifecycleDef.transitions[fromStatus]) {
return lifecycleDef.transitions[fromStatus]!;
}
-
- // Wildcard transitions
if (lifecycleDef.transitions['*']) {
return lifecycleDef.transitions['*']!;
}
-
return [];
}
}
diff --git a/src/models/ticket.ts b/src/models/ticket.ts
index 1a4c083..b63fc2a 100644
--- a/src/models/ticket.ts
+++ b/src/models/ticket.ts
@@ -22,4 +22,6 @@ export const CommentSchema = z.object({
body: z.string().min(1),
creator_id: z.string().optional().default('00000000-0000-0000-0000-000000000000'),
internal: z.boolean().optional().default(false),
+ attachment_ids: z.array(z.string()).optional(),
+ time_worked_minutes: z.number().int().min(0).optional(),
});
diff --git a/src/models/transaction.ts b/src/models/transaction.ts
index 59f8823..8d86670 100644
--- a/src/models/transaction.ts
+++ b/src/models/transaction.ts
@@ -11,6 +11,8 @@ export const TransactionType = {
Comment: 'Comment',
CustomField: 'CustomField',
Correspond: 'Correspond',
+ LinkCreate: 'LinkCreate',
+ LinkDelete: 'LinkDelete',
} as const;
export type TransactionType = (typeof TransactionType)[keyof typeof TransactionType];
diff --git a/src/routes/attachments.ts b/src/routes/attachments.ts
new file mode 100644
index 0000000..44081be
--- /dev/null
+++ b/src/routes/attachments.ts
@@ -0,0 +1,190 @@
+import { Hono } from 'hono';
+import { HTTPException } from 'hono/http-exception';
+import { existsSync, mkdirSync, createReadStream } from 'node:fs';
+import { join, extname } from 'node:path';
+import { writeFile, unlink } from 'node:fs/promises';
+import { randomUUID } from 'node:crypto';
+import type { Db } from '../db/index.ts';
+import { config } from '../config.ts';
+import { transactionAttachments, transactions, tickets } from '../db/schema.ts';
+import { eq, inArray } from 'drizzle-orm';
+
+function ensureDir(dir: string) {
+ if (!existsSync(dir)) {
+ mkdirSync(dir, { recursive: true });
+ }
+}
+
+function storageDir(): string {
+ const now = new Date();
+ const year = now.getFullYear().toString();
+ const month = String(now.getMonth() + 1).padStart(2, '0');
+ const dir = join(config.UPLOAD_DIR, year, month);
+ ensureDir(dir);
+ return dir;
+}
+
+const MIME_MAP: Record = {
+ '.txt': 'text/plain',
+ '.html': 'text/html',
+ '.css': 'text/css',
+ '.js': 'application/javascript',
+ '.json': 'application/json',
+ '.xml': 'application/xml',
+ '.pdf': 'application/pdf',
+ '.png': 'image/png',
+ '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg',
+ '.gif': 'image/gif',
+ '.svg': 'image/svg+xml',
+ '.webp': 'image/webp',
+ '.zip': 'application/zip',
+ '.gz': 'application/gzip',
+ '.tar': 'application/x-tar',
+ '.csv': 'text/csv',
+ '.doc': 'application/msword',
+ '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ '.xls': 'application/vnd.ms-excel',
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ '.ppt': 'application/vnd.ms-powerpoint',
+ '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ '.mp4': 'video/mp4',
+ '.mp3': 'audio/mpeg',
+ '.wav': 'audio/wav',
+ '.mov': 'video/quicktime',
+ '.avif': 'image/avif',
+ '.heic': 'image/heic',
+ '.heif': 'image/heif',
+ '.log': 'text/plain',
+ '.md': 'text/markdown',
+ '.yaml': 'text/yaml',
+ '.yml': 'text/yaml',
+};
+
+function guessMimeType(filename: string): string {
+ const ext = extname(filename).toLowerCase();
+ return MIME_MAP[ext] || 'application/octet-stream';
+}
+
+export function createAttachmentsRouter(db: Db): Hono {
+ const router = new Hono();
+
+ // POST /tickets/:id/attachments โ upload files (returns metadata, no transaction created yet)
+ router.post('/tickets/:id/attachments', async (c) => {
+ const ticketId = Number(c.req.param('id'));
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, ticketId),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ const formData = await c.req.formData();
+ const files = formData.getAll('files') as File[];
+
+ if (files.length === 0) {
+ throw new HTTPException(422, { message: 'No files provided' });
+ }
+
+ const dir = storageDir();
+ const result: Array<{
+ id: string;
+ filename: string;
+ mime_type: string;
+ size_bytes: number;
+ }> = [];
+
+ for (const file of files) {
+ if (!(file instanceof File)) continue;
+
+ const ext = extname(file.name);
+ const storedName = `${randomUUID()}${ext}`;
+ const storagePath = join(dir, storedName);
+ const buffer = Buffer.from(await file.arrayBuffer());
+ await writeFile(storagePath, buffer);
+
+ const [saved] = await db.insert(transactionAttachments).values({
+ filename: file.name,
+ mime_type: file.type || guessMimeType(file.name),
+ size_bytes: buffer.length,
+ storage_path: storagePath,
+ }).returning();
+
+ if (saved) {
+ result.push({
+ id: saved.id,
+ filename: saved.filename,
+ mime_type: saved.mime_type,
+ size_bytes: saved.size_bytes,
+ });
+ }
+ }
+
+ return c.json({ attachments: result }, 201);
+ });
+
+ // GET /attachments/:id โ serve/download an attachment
+ router.get('/attachments/:id', async (c) => {
+ const attachmentId = c.req.param('id');
+
+ const attachment = await db.query.transactionAttachments.findFirst({
+ where: eq(transactionAttachments.id, attachmentId),
+ });
+
+ if (!attachment) {
+ throw new HTTPException(404, { message: 'Attachment not found' });
+ }
+
+ if (!existsSync(attachment.storage_path)) {
+ throw new HTTPException(404, { message: 'Attachment file not found on disk' });
+ }
+
+ const disposition = c.req.query('download') === 'true' ? 'attachment' : 'inline';
+ const stream = createReadStream(attachment.storage_path);
+
+ return new Response(stream as any, {
+ status: 200,
+ headers: {
+ 'Content-Type': attachment.mime_type,
+ 'Content-Disposition': `${disposition}; filename="${encodeURIComponent(attachment.filename)}"`,
+ 'Content-Length': String(attachment.size_bytes),
+ },
+ });
+ });
+
+ // GET /tickets/:id/attachments โ list attachments for a ticket
+ router.get('/tickets/:id/attachments', async (c) => {
+ const ticketId = Number(c.req.param('id'));
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, ticketId),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ const ticketTransactions = await db.query.transactions.findMany({
+ where: eq(transactions.ticket_id, ticketId),
+ });
+
+ const txIds = ticketTransactions.map((tx) => tx.id);
+ if (txIds.length === 0) {
+ return c.json([]);
+ }
+
+ const attachments = await Promise.all(
+ txIds.map((txId) =>
+ db.query.transactionAttachments.findMany({
+ where: eq(transactionAttachments.transaction_id, txId),
+ })
+ )
+ );
+
+ return c.json(attachments.flat());
+ });
+
+ return router;
+}
diff --git a/src/routes/auth.ts b/src/routes/auth.ts
new file mode 100644
index 0000000..68e2b2c
--- /dev/null
+++ b/src/routes/auth.ts
@@ -0,0 +1,132 @@
+import { Hono } from 'hono';
+import { HTTPException } from 'hono/http-exception';
+import { z } from 'zod/v4';
+import type { Db } from '../db/index.ts';
+import { users, apiTokens } from '../db/schema.ts';
+import { eq, desc, sql } from 'drizzle-orm';
+import { createToken, createAuthMiddleware } from '../auth/middleware.ts';
+
+const LoginSchema = z.object({
+ username: z.string().min(1),
+ password: z.string().min(1),
+});
+
+export function createAuthRouter(db: Db): Hono {
+ const router = new Hono();
+ const { requireAuth } = createAuthMiddleware(db);
+
+ // POST /auth/login
+ router.post('/auth/login', async (c) => {
+ const body = await c.req.json();
+ const parsed = LoginSchema.parse(body);
+
+ const user = await db.query.users.findFirst({
+ where: eq(users.username, parsed.username),
+ });
+
+ if (!user || !user.password_hash) {
+ throw new HTTPException(401, { message: 'Invalid username or password' });
+ }
+
+ const valid = await Bun.password.verify(parsed.password, user.password_hash);
+ if (!valid) {
+ throw new HTTPException(401, { message: 'Invalid username or password' });
+ }
+
+ const token = await createToken({
+ id: user.id,
+ username: user.username,
+ role: user.role,
+ });
+
+ return c.json({
+ token,
+ user: {
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ role: user.role,
+ },
+ });
+ });
+
+ // GET /auth/me โ return current user from token
+ router.get('/auth/me', requireAuth, async (c) => {
+ const authUser = c.get('user');
+ const user = await db.query.users.findFirst({
+ where: eq(users.id, authUser.userId),
+ });
+
+ if (!user) {
+ throw new HTTPException(404, { message: 'User not found' });
+ }
+
+ return c.json({
+ id: user.id,
+ username: user.username,
+ email: user.email,
+ role: user.role,
+ });
+ });
+
+ // POST /auth/tokens โ create API token
+ router.post('/auth/tokens', requireAuth, async (c) => {
+ const body = await c.req.json();
+ const name = String(body.name || 'API token').trim();
+ const authUser = c.get('user');
+
+ const rawToken = `tessera_${crypto.randomUUID().replace(/-/g, '')}`;
+ const tokenHash = await Bun.password.hash(rawToken);
+
+ const [token] = await db.insert(apiTokens).values({
+ user_id: authUser.userId,
+ name,
+ token_hash: tokenHash,
+ }).returning();
+
+ if (!token) {
+ throw new HTTPException(500, { message: 'Failed to create token' });
+ }
+
+ return c.json({
+ id: token.id,
+ name: token.name,
+ token: rawToken,
+ created_at: token.created_at,
+ }, 201);
+ });
+
+ // GET /auth/tokens โ list tokens
+ router.get('/auth/tokens', requireAuth, async (c) => {
+ const authUser = c.get('user');
+ const result = await db.query.apiTokens.findMany({
+ where: eq(apiTokens.user_id, authUser.userId),
+ orderBy: desc(apiTokens.created_at),
+ });
+ return c.json(result.map((t) => ({
+ id: t.id,
+ name: t.name,
+ last_used_at: t.last_used_at,
+ created_at: t.created_at,
+ })));
+ });
+
+ // DELETE /auth/tokens/:id โ revoke token
+ router.delete('/auth/tokens/:id', requireAuth, async (c) => {
+ const id = c.req.param('id');
+ const authUser = c.get('user');
+
+ // Verify ownership before revoke
+ const allTokens = await db.query.apiTokens.findMany();
+ const existing = allTokens.find((t) => t.id === id && t.user_id === authUser.userId);
+ if (!existing) {
+ throw new HTTPException(404, { message: 'Token not found' });
+ }
+
+ // Raw delete to avoid Drizzle type issue with new apiTokens table
+ await db.execute(sql`DELETE FROM api_tokens WHERE id = ${id}`);
+ return c.json({ ok: true });
+ });
+
+ return router;
+}
diff --git a/src/routes/dashboards.ts b/src/routes/dashboards.ts
index 50ba7cd..6fb9b20 100644
--- a/src/routes/dashboards.ts
+++ b/src/routes/dashboards.ts
@@ -276,6 +276,30 @@ export function createDashboardsRouter(db: Db): Hono {
}
}
+ // Widget-level filters override or add to view filters
+ const widgetFilters = (widget.config as Record)?.filters as Array<{ field: string; operator: string; value: string }> | undefined;
+ if (widgetFilters) {
+ for (const f of widgetFilters) {
+ if (f.field === 'status') {
+ if (f.operator === 'is_not') result = result.filter((t) => t.status !== f.value);
+ else result = result.filter((t) => t.status === f.value);
+ } else if (f.field === 'queue') {
+ if (f.operator === 'is_not') result = result.filter((t) => t.queue_id !== f.value);
+ else result = result.filter((t) => t.queue_id === f.value);
+ } else if (f.field === 'owner') {
+ if (f.value === 'unassigned') result = result.filter((t) => !t.owner_id);
+ else result = result.filter((t) => t.owner_id === f.value);
+ } else if (f.field === 'q') {
+ const q = f.value.toLowerCase();
+ result = result.filter((t) =>
+ t.subject.toLowerCase().includes(q) ||
+ String(t.id).includes(q) ||
+ (queueName.get(t.queue_id) ?? '').toLowerCase().includes(q)
+ );
+ }
+ }
+ }
+
const limit = (widget.config as Record)?.limit as number ?? 5;
// Find lifecycle for status classification
@@ -337,6 +361,61 @@ export function createDashboardsRouter(db: Db): Hono {
return c.json({ type: 'status_chart', counts, total: result.length, title: widget.title, view_id: view.id });
}
+ case 'my_tickets': {
+ const authUser = c.get('user');
+ const myTickets = result.filter((t) => t.owner_id === authUser.userId);
+ return c.json({ type: 'my_tickets', total: myTickets.length, title: widget.title, view_id: view.id });
+ }
+
+ case 'trend_chart': {
+ const period = ((widget.config as Record)?.period as string) ?? 'day';
+ const days = (widget.config as Record)?.days as number ?? 30;
+ const trendField = ((widget.config as Record)?.field as string) ?? 'created_at';
+ const now = new Date();
+ const start = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
+
+ const filtered = result.filter((t) => {
+ const d = trendField === 'updated_at' ? t.updated_at : t.created_at;
+ return d && new Date(d) >= start;
+ });
+
+ const points: Record = {};
+ for (const t of filtered) {
+ const d = new Date(trendField === 'updated_at' ? t.updated_at! : t.created_at!);
+ let key: string;
+ if (period === 'week') {
+ const weekStart = new Date(d);
+ weekStart.setDate(d.getDate() - d.getDay());
+ key = weekStart.toISOString().slice(0, 10);
+ } else {
+ key = d.toISOString().slice(0, 10); // YYYY-MM-DD
+ }
+ points[key] = (points[key] ?? 0) + 1;
+ }
+
+ return c.json({ type: 'trend_chart', counts: points, total: result.length, title: widget.title, view_id: view.id });
+ }
+
+ case 'overdue': {
+ const dateFieldKey = (widget.config as Record)?.field_key as string;
+ const now = new Date();
+ const overdue = result.filter((t) => {
+ if (!dateFieldKey) {
+ // No specific field โ check if any inactive-adjacent status
+ const lc = lifecycleByQueue.get(t.queue_id);
+ if (lc) {
+ const inactive = lc.statuses.inactive;
+ if (inactive.includes(t.status)) return false; // already resolved
+ }
+ // Check if updated_at is older than 7 days
+ const updated = t.updated_at ? new Date(t.updated_at) : new Date(0);
+ return (now.getTime() - updated.getTime()) > 7 * 24 * 60 * 60 * 1000;
+ }
+ return false; // Would need CF value lookup for date field
+ });
+ return c.json({ type: 'overdue', total: overdue.length, title: widget.title, view_id: view.id });
+ }
+
case 'grouped_counts': {
const groupBy = (widget.config as Record)?.group_by as string ?? 'owner';
const groups: Record = {};
diff --git a/src/routes/notifications.ts b/src/routes/notifications.ts
new file mode 100644
index 0000000..3d7b84b
--- /dev/null
+++ b/src/routes/notifications.ts
@@ -0,0 +1,64 @@
+import { Hono } from 'hono';
+import { HTTPException } from 'hono/http-exception';
+import type { Db } from '../db/index.ts';
+import { notifications } from '../db/schema.ts';
+import { and, eq, desc } from 'drizzle-orm';
+
+export function createNotificationsRouter(db: Db): Hono {
+ const router = new Hono();
+
+ // GET /notifications โ list notifications for current user
+ router.get('/notifications', async (c) => {
+ const user = c.get('user');
+ const result = await db.query.notifications.findMany({
+ where: eq(notifications.user_id, user.userId),
+ orderBy: desc(notifications.created_at),
+ // Return last 50
+ });
+ return c.json(result.slice(0, 50));
+ });
+
+ // GET /notifications/unread-count
+ router.get('/notifications/unread-count', async (c) => {
+ const user = c.get('user');
+ const result = await db.query.notifications.findMany({
+ where: and(
+ eq(notifications.user_id, user.userId),
+ eq(notifications.read, false),
+ ),
+ });
+ return c.json({ count: result.length });
+ });
+
+ // PATCH /notifications/:id/read โ mark as read
+ router.patch('/notifications/:id/read', async (c) => {
+ const id = c.req.param('id');
+ await db.update(notifications).set({ read: true }).where(eq(notifications.id, id));
+ return c.json({ ok: true });
+ });
+
+ // PATCH /notifications/read-all โ mark all as read
+ router.patch('/notifications/read-all', async (c) => {
+ const user = c.get('user');
+ await db.update(notifications)
+ .set({ read: true })
+ .where(eq(notifications.user_id, user.userId));
+ return c.json({ ok: true });
+ });
+
+ return router;
+}
+
+// Helper to create notifications (used by other routes)
+export async function createNotification(
+ db: Db,
+ data: { user_id: string; ticket_id?: number; type: string; title: string; body?: string },
+) {
+ await db.insert(notifications).values({
+ user_id: data.user_id,
+ ticket_id: data.ticket_id ?? null,
+ type: data.type,
+ title: data.title,
+ body: data.body ?? null,
+ });
+}
diff --git a/src/routes/queue-permissions.ts b/src/routes/queue-permissions.ts
new file mode 100644
index 0000000..79c1d79
--- /dev/null
+++ b/src/routes/queue-permissions.ts
@@ -0,0 +1,176 @@
+import { Hono } from 'hono';
+import { HTTPException } from 'hono/http-exception';
+import type { Db } from '../db/index.ts';
+import { queuePermissions, userPermissions, teams, queues, users } from '../db/schema.ts';
+import { eq } from 'drizzle-orm';
+
+export function createQueuePermissionsRouter(db: Db): Hono {
+ const router = new Hono();
+
+ // GET /queue-permissions โ list all permissions (with team + queue names)
+ router.get('/queue-permissions', async (c) => {
+ const all = await db.query.queuePermissions.findMany();
+
+ // Enrich with names
+ const teamIds = [...new Set(all.map((p) => p.team_id))];
+ const queueIds = [...new Set(all.map((p) => p.queue_id))];
+
+ const teamList = teamIds.length > 0
+ ? await db.query.teams.findMany({ where: (t, { inArray }) => inArray(t.id, teamIds) })
+ : [];
+ const queueList = queueIds.length > 0
+ ? await db.query.queues.findMany({ where: (t, { inArray }) => inArray(t.id, queueIds) })
+ : [];
+
+ const teamById = new Map(teamList.map((t) => [t.id, t]));
+ const queueById = new Map(queueList.map((q) => [q.id, q]));
+
+ const enriched = all.map((p) => ({
+ ...p,
+ team_name: teamById.get(p.team_id)?.name ?? p.team_id,
+ queue_name: queueById.get(p.queue_id)?.name ?? p.queue_id,
+ }));
+
+ return c.json(enriched);
+ });
+
+ // GET /queue-permissions/teams-and-queues โ return teams and queues for the form
+ router.get('/queue-permissions/teams-and-queues', async (c) => {
+ const [teamList, queueList] = await Promise.all([
+ db.query.teams.findMany(),
+ db.query.queues.findMany(),
+ ]);
+ return c.json({ teams: teamList, queues: queueList });
+ });
+
+ // POST /queue-permissions โ grant a right
+ router.post('/queue-permissions', async (c) => {
+ const body = await c.req.json();
+ const { queue_id, team_id, right_name } = body;
+
+ if (!queue_id || !team_id || !right_name) {
+ throw new HTTPException(422, { message: 'queue_id, team_id, and right_name are required' });
+ }
+
+ const validRights = ['ticket.view', 'ticket.create', 'ticket.reply', 'ticket.comment', 'ticket.modify', 'queue.admin'];
+ if (!validRights.includes(right_name)) {
+ throw new HTTPException(422, { message: `Invalid right. Must be one of: ${validRights.join(', ')}` });
+ }
+
+ // Check for duplicate
+ const existing = await db.query.queuePermissions.findFirst({
+ where: (table, { and, eq: eqFn }) =>
+ and(
+ eqFn(table.queue_id, queue_id),
+ eqFn(table.team_id, team_id),
+ eqFn(table.right_name, right_name),
+ ),
+ });
+
+ if (existing) {
+ return c.json(existing); // Idempotent โ return existing
+ }
+
+ const [perm] = await db.insert(queuePermissions).values({
+ queue_id,
+ team_id,
+ right_name,
+ }).returning();
+
+ if (!perm) {
+ throw new HTTPException(500, { message: 'Failed to create permission' });
+ }
+
+ return c.json(perm, 201);
+ });
+
+ // DELETE /queue-permissions/:id โ revoke a right
+ router.delete('/queue-permissions/:id', async (c) => {
+ const id = c.req.param('id');
+
+ const existing = await db.query.queuePermissions.findFirst({
+ where: eq(queuePermissions.id, id),
+ });
+
+ if (!existing) {
+ throw new HTTPException(404, { message: 'Permission not found' });
+ }
+
+ await db.delete(queuePermissions).where(eq(queuePermissions.id, id));
+
+ return c.json({ ok: true });
+ });
+
+ // GET /user-permissions โ list all per-user permissions
+ router.get('/user-permissions', async (c) => {
+ const all = await db.query.userPermissions.findMany();
+
+ const userIds = [...new Set(all.map((p) => p.user_id))];
+ const queueIds = [...new Set(all.map((p) => p.queue_id))];
+
+ const userList = userIds.length > 0
+ ? await db.query.users.findMany({ where: (t, { inArray }) => inArray(t.id, userIds) })
+ : [];
+ const queueList = queueIds.length > 0
+ ? await db.query.queues.findMany({ where: (t, { inArray }) => inArray(t.id, queueIds) })
+ : [];
+
+ const userById = new Map(userList.map((u) => [u.id, u]));
+ const queueById = new Map(queueList.map((q) => [q.id, q]));
+
+ const enriched = all.map((p) => ({
+ ...p,
+ username: userById.get(p.user_id)?.username ?? p.user_id,
+ queue_name: queueById.get(p.queue_id)?.name ?? p.queue_id,
+ }));
+
+ return c.json(enriched);
+ });
+
+ // POST /user-permissions โ grant a right to a user
+ router.post('/user-permissions', async (c) => {
+ const body = await c.req.json();
+ const { queue_id, user_id, right_name } = body;
+
+ if (!queue_id || !user_id || !right_name) {
+ throw new HTTPException(422, { message: 'queue_id, user_id, and right_name are required' });
+ }
+
+ const validRights = ['ticket.view', 'ticket.create', 'ticket.reply', 'ticket.comment', 'ticket.modify', 'queue.admin'];
+ if (!validRights.includes(right_name)) {
+ throw new HTTPException(422, { message: `Invalid right. Must be one of: ${validRights.join(', ')}` });
+ }
+
+ const existing = await db.query.userPermissions.findFirst({
+ where: (table, { and, eq: eqFn }) =>
+ and(
+ eqFn(table.queue_id, queue_id),
+ eqFn(table.user_id, user_id),
+ eqFn(table.right_name, right_name),
+ ),
+ });
+
+ if (existing) return c.json(existing);
+
+ const [perm] = await db.insert(userPermissions).values({
+ queue_id,
+ user_id,
+ right_name,
+ }).returning();
+
+ if (!perm) {
+ throw new HTTPException(500, { message: 'Failed to create user permission' });
+ }
+
+ return c.json(perm, 201);
+ });
+
+ // DELETE /user-permissions/:id โ revoke a user right
+ router.delete('/user-permissions/:id', async (c) => {
+ const id = c.req.param('id');
+ await db.delete(userPermissions).where(eq(userPermissions.id, id));
+ return c.json({ ok: true });
+ });
+
+ return router;
+}
diff --git a/src/routes/tickets.ts b/src/routes/tickets.ts
index eed6766..b391a8a 100644
--- a/src/routes/tickets.ts
+++ b/src/routes/tickets.ts
@@ -1,7 +1,15 @@
import { Hono } from 'hono';
import { HTTPException } from 'hono/http-exception';
+import { existsSync, mkdirSync } from 'node:fs';
+import { join, extname } from 'node:path';
+import { writeFile } from 'node:fs/promises';
+import { randomUUID } from 'node:crypto';
import type { Db } from '../db/index.ts';
-import { tickets, transactions, customFieldValues, customFields, queues, lifecycles, queueCustomFields, teamMembers } from '../db/schema.ts';
+import { config } from '../config.ts';
+import { getUserId } from '../auth/middleware.ts';
+import { requireRight } from '../auth/permissions.ts';
+import { createNotification } from './notifications.ts';
+import { tickets, transactions, customFieldValues, customFields, queues, lifecycles, queueCustomFields, teamMembers, transactionAttachments, ticketLinks } from '../db/schema.ts';
import { and, eq, asc, or, ilike, isNull, inArray, exists, sql } from 'drizzle-orm';
import { CreateTicketSchema, UpdateTicketSchema, CommentSchema } from '../models/ticket.ts';
import { ScripEngine } from '../scrip/engine.ts';
@@ -22,8 +30,14 @@ export function createTicketsRouter(db: Db): Hono {
// GET / โ list tickets
router.get('/', async (c) => {
- const params = new URL(c.req.url).searchParams;
const queueId = c.req.query('queue_id');
+
+ // If filtering by queue, check view permission
+ if (queueId) {
+ await requireRight(c, db, queueId, 'ticket.view');
+ }
+
+ const params = new URL(c.req.url).searchParams;
const status = c.req.query('status');
const ownerId = c.req.query('owner_id');
const teamId = c.req.query('team_id');
@@ -63,17 +77,70 @@ export function createTicketsRouter(db: Db): Hono {
}
}
- // Text search: push to SQL via ilike on ticket columns + queue name join
+ // Subject filter: supports "contains:", "is:", "is_not:", "starts_with:"
+ const subjectFilter = c.req.query('subject');
+ if (subjectFilter) {
+ const idx = subjectFilter.indexOf(':');
+ const op = idx > -1 ? subjectFilter.slice(0, idx) : 'contains';
+ const val = idx > -1 ? subjectFilter.slice(idx + 1) : subjectFilter;
+ if (val) {
+ if (op === 'is') conditions.push(eq(tickets.subject, val));
+ else if (op === 'is_not') conditions.push(sql`${tickets.subject} != ${val}`);
+ else if (op === 'starts_with') conditions.push(ilike(tickets.subject, `${val}%`));
+ else conditions.push(ilike(tickets.subject, `%${val}%`)); // contains
+ }
+ }
+
+ // Date filters: format "before:YYYY-MM-DD" or "after:YYYY-MM-DD"
+ for (const [fieldName, column] of [['created', tickets.created_at], ['updated', tickets.updated_at]] as const) {
+ const dateFilter = c.req.query(fieldName);
+ if (dateFilter) {
+ const idx = dateFilter.indexOf(':');
+ const op = idx > -1 ? dateFilter.slice(0, idx) : 'after';
+ const val = idx > -1 ? dateFilter.slice(idx + 1) : dateFilter;
+ if (val) {
+ if (op === 'before') conditions.push(sql`${column} <= ${val}::timestamptz`);
+ else conditions.push(sql`${column} >= ${val}::timestamptz`); // after
+ }
+ }
+ }
+
+ // Text search across tickets, transactions, queue names, and custom fields
if (query) {
const pattern = `%${query}%`;
conditions.push(
or(
ilike(tickets.subject, pattern),
- ilike(tickets.status, pattern),
- sql`${tickets.id}::text ILIKE ${pattern}`
+ sql`${tickets.id}::text ILIKE ${pattern}`,
+ // Queue name
+ exists(
+ db.select({ n: sql`1` })
+ .from(queues)
+ .where(and(
+ eq(queues.id, tickets.queue_id),
+ ilike(queues.name, pattern)
+ ))
+ ),
+ // Transaction bodies (comments, correspondence)
+ exists(
+ db.select({ n: sql`1` })
+ .from(transactions)
+ .where(and(
+ eq(transactions.ticket_id, tickets.id),
+ sql`transactions.data->>'body' ILIKE ${pattern}`
+ ))
+ ),
+ // Custom field values
+ exists(
+ db.select({ n: sql`1` })
+ .from(customFieldValues)
+ .where(and(
+ eq(customFieldValues.ticket_id, tickets.id),
+ ilike(customFieldValues.value, pattern)
+ ))
+ )
)!
);
- // Queue name search requires join โ keep as post-filter
}
// Custom field filters: use EXISTS subquery
@@ -100,19 +167,9 @@ export function createTicketsRouter(db: Db): Hono {
limit,
});
- // Post-filter for queue name text search (requires in-memory join)
- let filtered = result;
- if (query) {
- const queuesForSearch = await db.query.queues.findMany();
- const queueNameById = new Map(queuesForSearch.map((queue) => [queue.id, queue.name]));
- filtered = result.filter((ticket) =>
- (queueNameById.get(ticket.queue_id) ?? '').toLowerCase().includes(query.toLowerCase())
- );
- }
-
// Attach custom field values to all tickets
- if (filtered.length > 0) {
- const ticketIds = filtered.map((t) => t.id);
+ if (result.length > 0) {
+ const ticketIds = result.map((t) => t.id);
const allCfValues = await db.query.customFieldValues.findMany({
where: (table, { inArray }) => inArray(table.ticket_id, ticketIds),
});
@@ -124,7 +181,7 @@ export function createTicketsRouter(db: Db): Hono {
: [];
const fieldMap = new Map(allFields.map((f) => [f.id, f]));
- const ticketsWithCf = filtered.map((ticket) => {
+ const ticketsWithCf = result.map((ticket) => {
const cfs = allCfValues
.filter((v) => v.ticket_id === ticket.id)
.map((v) => ({
@@ -149,14 +206,16 @@ export function createTicketsRouter(db: Db): Hono {
return c.json(ticketsWithCf);
}
- return c.json(filtered);
+ return c.json(result);
});
// POST / โ create ticket
router.post('/', async (c) => {
const body = await c.req.json();
const parsed = CreateTicketSchema.parse(body);
- const creatorId = '00000000-0000-0000-0000-000000000000';
+
+ await requireRight(c, db, parsed.queue_id, 'ticket.create');
+ const creatorId = getUserId(c);
const customFieldInput = parsed.custom_fields ?? {};
const customFieldEntries = Object.entries(customFieldInput)
.map(([fieldId, value]) => [fieldId, value.trim()] as const)
@@ -208,6 +267,21 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(422, { message: `${field.name}: value is not an allowed option` });
}
}
+ if (field.field_type === 'date') {
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
+ throw new HTTPException(422, { message: `${field.name}: value must be a date in YYYY-MM-DD format` });
+ }
+ const parsed = new Date(value);
+ if (isNaN(parsed.getTime())) {
+ throw new HTTPException(422, { message: `${field.name}: invalid date` });
+ }
+ }
+ if (field.field_type === 'datetime') {
+ const parsed = new Date(value);
+ if (isNaN(parsed.getTime())) {
+ throw new HTTPException(422, { message: `${field.name}: value must be a valid ISO 8601 datetime` });
+ }
+ }
if (field.pattern) {
const regex = new RegExp(field.pattern);
if (!regex.test(value)) {
@@ -285,6 +359,8 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(404, { message: 'Ticket not found' });
}
+ await requireRight(c, db, ticket.queue_id, 'ticket.view');
+
const cfValues = await db.query.customFieldValues.findMany({
where: eq(customFieldValues.ticket_id, id),
});
@@ -301,10 +377,32 @@ export function createTicketsRouter(db: Db): Hono {
custom_field: cfMap.get(v.custom_field_id) ?? null,
}));
- return c.json({ ...ticket, custom_fields: customFieldsMapped });
- });
+ // Blocking dependencies: tickets this one DependsOn that aren't resolved yet
+ const dependsOnLinks = await db.query.ticketLinks.findMany({
+ where: (t, { and, eq: eqFn }) =>
+ and(eqFn(t.ticket_id, id), eqFn(t.link_type, 'DependsOn')),
+ });
+ const blockingIds = dependsOnLinks.map((l) => l.target_ticket_id);
+ let blockedBy: Array<{ id: number; subject: string; status: string }> = [];
+ if (blockingIds.length > 0) {
+ const blockingTickets = await db.query.tickets.findMany({
+ where: (t, { inArray }) => inArray(t.id, blockingIds),
+ });
+ const queue = await db.query.queues.findFirst({
+ where: eq(queues.id, ticket.queue_id),
+ });
+ let inactiveStatuses: string[] = [];
+ if (queue?.lifecycle_id) {
+ const lc = await db.query.lifecycles.findFirst({ where: eq(lifecycles.id, queue.lifecycle_id) });
+ if (lc) inactiveStatuses = (lc.definition as any)?.statuses?.inactive ?? [];
+ }
+ blockedBy = blockingTickets
+ .filter((t) => !inactiveStatuses.includes(t.status))
+ .map((t) => ({ id: t.id, subject: t.subject, status: t.status }));
+ }
- // PATCH /:id โ update ticket
+ return c.json({ ...ticket, custom_fields: customFieldsMapped, blocked_by: blockedBy });
+ });
router.patch('/:id', async (c) => {
const id = Number(c.req.param('id'));
const body = await c.req.json();
@@ -318,6 +416,8 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(404, { message: 'Ticket not found' });
}
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
let lifecycleDef: LifecycleDefinition | null = null;
// Validate lifecycle transition if status is changing
@@ -337,6 +437,34 @@ export function createTicketsRouter(db: Db): Hono {
if (!result.valid) {
throw new HTTPException(422, { message: result.error ?? 'Invalid transition' });
}
+
+ // Check transition-gating right
+ if (result.requiredRight) {
+ await requireRight(c, db, ticket.queue_id, result.requiredRight as any);
+ }
+
+ // Check dependency enforcement: can't resolve/close if this ticket DependsOn unresolved tickets
+ const inactiveStatuses = lifecycleDef.statuses.inactive;
+ if (inactiveStatuses.includes(parsed.status)) {
+ const dependsOnLinks = await db.query.ticketLinks.findMany({
+ where: (t, { and, eq: eqFn }) =>
+ and(
+ eqFn(t.ticket_id, id),
+ eqFn(t.link_type, 'DependsOn'),
+ ),
+ });
+
+ for (const link of dependsOnLinks) {
+ const target = await db.query.tickets.findFirst({
+ where: eq(tickets.id, link.target_ticket_id),
+ });
+ if (target && !inactiveStatuses.includes(target.status)) {
+ throw new HTTPException(422, {
+ message: `Cannot resolve: this ticket depends on ticket ${target.id} (${target.subject}) which is still ${target.status}. Resolve or close that ticket first.`,
+ });
+ }
+ }
+ }
}
}
}
@@ -350,7 +478,7 @@ export function createTicketsRouter(db: Db): Hono {
field: 'subject',
old_value: ticket.subject,
new_value: parsed.subject,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
});
}
@@ -361,7 +489,7 @@ export function createTicketsRouter(db: Db): Hono {
field: 'status',
old_value: ticket.status,
new_value: parsed.status,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
});
}
@@ -372,7 +500,7 @@ export function createTicketsRouter(db: Db): Hono {
field: 'owner_id',
old_value: ticket.owner_id ?? null,
new_value: parsed.owner_id,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
});
}
@@ -383,7 +511,7 @@ export function createTicketsRouter(db: Db): Hono {
field: 'team_id',
old_value: (ticket as any).team_id ?? null,
new_value: parsed.team_id,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
});
}
@@ -425,10 +553,24 @@ export function createTicketsRouter(db: Db): Hono {
await db.insert(transactions).values(txList as any);
}
- // Run scrips
- const prepared = await scripEngine.prepare(id, txList as any);
+ // Run scrips โ use TransactionBatch when multiple changes, TransactionCreate for single
+ const stage = txList.length > 1 ? 'TransactionBatch' as const : 'TransactionCreate' as const;
+ const prepared = await scripEngine.prepare(id, txList as any, stage);
const results = await scripEngine.commit(prepared);
+ // Notify on assignment change
+ if (parsed.owner_id !== undefined && parsed.owner_id !== ticket.owner_id) {
+ if (parsed.owner_id) {
+ await createNotification(db, {
+ user_id: parsed.owner_id,
+ ticket_id: id,
+ type: 'assigned',
+ title: `You were assigned to ticket ${id}`,
+ body: ticket.subject,
+ });
+ }
+ }
+
return c.json({ ticket: updated, scrip_results: results });
});
@@ -446,6 +588,8 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(404, { message: 'Ticket not found' });
}
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
if (parsed.status) {
const queue = await db.query.queues.findFirst({
where: eq(queues.id, ticket.queue_id),
@@ -470,13 +614,13 @@ export function createTicketsRouter(db: Db): Hono {
if (parsed.status && parsed.status !== ticket.status) {
txList.push({
- id: '00000000-0000-0000-0000-000000000000',
+ id: getUserId(c),
ticket_id: id,
transaction_type: 'StatusChange',
field: 'status',
old_value: ticket.status,
new_value: parsed.status,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
});
}
@@ -487,7 +631,7 @@ export function createTicketsRouter(db: Db): Hono {
return c.json({ prepared_scrips: results });
});
- // GET /:id/transactions โ list transactions for ticket
+ // GET /:id/transactions โ list transactions for ticket (with attachments)
router.get('/:id/transactions', async (c) => {
const id = Number(c.req.param('id'));
@@ -496,7 +640,105 @@ export function createTicketsRouter(db: Db): Hono {
orderBy: asc(transactions.created_at),
});
- return c.json(result);
+ // Fetch attachments for these transactions
+ const txIds = result.map((tx) => tx.id);
+ if (txIds.length > 0) {
+ const attachments = await db.query.transactionAttachments.findMany({
+ where: inArray(transactionAttachments.transaction_id, txIds),
+ });
+
+ const attachmentsByTxId = new Map();
+ for (const att of attachments) {
+ if (!att.transaction_id) continue;
+ const list = attachmentsByTxId.get(att.transaction_id);
+ if (list) {
+ list.push(att);
+ } else {
+ attachmentsByTxId.set(att.transaction_id, [att]);
+ }
+ }
+
+ const resultWithAttachments = result.map((tx) => ({
+ ...tx,
+ attachments: attachmentsByTxId.get(tx.id) ?? [],
+ }));
+
+ return c.json(resultWithAttachments);
+ }
+
+ return c.json(result.map((tx) => ({ ...tx, attachments: [] })));
+ });
+
+ // POST /:id/attachments โ upload file attachments for a ticket
+ router.post('/:id/attachments', async (c) => {
+ const ticketId = Number(c.req.param('id'));
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, ticketId),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ await requireRight(c, db, ticket.queue_id, 'ticket.reply');
+
+ const formData = await c.req.formData();
+ const files = formData.getAll('files') as File[];
+
+ if (files.length === 0) {
+ throw new HTTPException(422, { message: 'No files provided' });
+ }
+
+ const now = new Date();
+ const year = now.getFullYear().toString();
+ const month = String(now.getMonth() + 1).padStart(2, '0');
+ const dir = join(config.UPLOAD_DIR, year, month);
+ if (!existsSync(dir)) {
+ mkdirSync(dir, { recursive: true });
+ }
+
+ const MIME_MAP: Record = {
+ '.txt': 'text/plain', '.html': 'text/html', '.css': 'text/css',
+ '.js': 'application/javascript', '.json': 'application/json', '.xml': 'application/xml',
+ '.pdf': 'application/pdf', '.png': 'image/png', '.jpg': 'image/jpeg',
+ '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.svg': 'image/svg+xml',
+ '.webp': 'image/webp', '.zip': 'application/zip', '.gz': 'application/gzip',
+ '.csv': 'text/csv', '.mp4': 'video/mp4', '.mp3': 'audio/mpeg',
+ '.md': 'text/markdown', '.yaml': 'text/yaml', '.yml': 'text/yaml',
+ };
+
+ const result: Array<{ id: string; filename: string; mime_type: string; size_bytes: number }> = [];
+
+ for (const file of files) {
+ if (!(file instanceof File)) continue;
+
+ const ext = extname(file.name).toLowerCase();
+ const storedName = `${randomUUID()}${ext}`;
+ const storagePath = join(dir, storedName);
+ const buffer = Buffer.from(await file.arrayBuffer());
+ await writeFile(storagePath, buffer);
+
+ const mimeType = file.type || MIME_MAP[ext] || 'application/octet-stream';
+
+ const [saved] = await db.insert(transactionAttachments).values({
+ filename: file.name,
+ mime_type: mimeType,
+ size_bytes: buffer.length,
+ storage_path: storagePath,
+ }).returning();
+
+ if (saved) {
+ result.push({
+ id: saved.id,
+ filename: saved.filename,
+ mime_type: saved.mime_type,
+ size_bytes: saved.size_bytes,
+ });
+ }
+ }
+
+ return c.json({ attachments: result }, 201);
});
// POST /:id/comment โ add a comment (reply or internal note)
@@ -513,12 +755,23 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(404, { message: 'Ticket not found' });
}
+ await requireRight(c, db, ticket.queue_id, parsed.internal ? 'ticket.comment' : 'ticket.reply');
+
const transactionType = parsed.internal ? 'Comment' : 'Correspond';
+ const attachmentIds = parsed.attachment_ids ?? [];
+
+ const txData: Record = { body: parsed.body };
+ if (attachmentIds.length > 0) {
+ txData.attachment_ids = attachmentIds;
+ }
+
+ const timeWorked = parsed.time_worked_minutes ?? 0;
const [tx] = await db.insert(transactions).values({
ticket_id: id,
transaction_type: transactionType,
- data: { body: parsed.body },
+ data: txData,
+ time_worked_minutes: timeWorked,
creator_id: parsed.creator_id,
}).returning();
@@ -526,14 +779,454 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(500, { message: 'Failed to create comment' });
}
+ // Link pre-uploaded attachment records to this transaction
+ if (attachmentIds.length > 0) {
+ await db.update(transactionAttachments)
+ .set({ transaction_id: tx.id })
+ .where(inArray(transactionAttachments.id, attachmentIds));
+ }
+
// Run scrips
const txList = [tx];
const prepared = await scripEngine.prepare(id, txList as any);
await scripEngine.commit(prepared);
+ // Notify ticket owner and creator
+ const commenterId = getUserId(c);
+ const notifyTargets = new Set([ticket.owner_id, ticket.creator_id].filter(Boolean) as string[]);
+ notifyTargets.delete(commenterId);
+ for (const userId of notifyTargets) {
+ await createNotification(db, {
+ user_id: userId,
+ ticket_id: id,
+ type: 'commented',
+ title: `New ${transactionType === 'Comment' ? 'internal note' : 'reply'} on ticket ${id}`,
+ body: parsed.body.slice(0, 200),
+ });
+ }
+
return c.json(tx, 201);
});
+ // GET /:id/links โ list links for a ticket (with target ticket info)
+ router.get('/:id/links', async (c) => {
+ const id = Number(c.req.param('id'));
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, id),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ await requireRight(c, db, ticket.queue_id, 'ticket.view');
+
+ const links = await db.query.ticketLinks.findMany({
+ where: eq(ticketLinks.ticket_id, id),
+ orderBy: asc(ticketLinks.created_at),
+ });
+
+ // Enrich with target ticket info
+ const targetIds = [...new Set(links.map((l) => l.target_ticket_id))];
+ const targetTickets = targetIds.length > 0
+ ? await db.query.tickets.findMany({
+ where: (table, { inArray }) => inArray(table.id, targetIds),
+ })
+ : [];
+ const ticketById = new Map(targetTickets.map((t) => [t.id, t]));
+
+ const enriched = links.map((link) => {
+ const target = ticketById.get(link.target_ticket_id);
+ return {
+ ...link,
+ target_ticket: target
+ ? { id: target.id, subject: target.subject, status: target.status }
+ : null,
+ };
+ });
+
+ return c.json(enriched);
+ });
+
+ // POST /:id/links โ create a link to another ticket
+ router.post('/:id/links', async (c) => {
+ const id = Number(c.req.param('id'));
+ const body = await c.req.json();
+ const targetTicketId = Number(body.target_ticket_id);
+ const linkType = String(body.link_type || 'RelatedTo');
+
+ if (!targetTicketId || isNaN(targetTicketId)) {
+ throw new HTTPException(422, { message: 'target_ticket_id is required' });
+ }
+
+ if (targetTicketId === id) {
+ throw new HTTPException(422, { message: 'Cannot link a ticket to itself' });
+ }
+
+ const validTypes = ['DependsOn', 'Blocks', 'RefersTo', 'RelatedTo', 'Duplicates', 'MemberOf'];
+ if (!validTypes.includes(linkType)) {
+ throw new HTTPException(422, { message: `Invalid link_type. Must be one of: ${validTypes.join(', ')}` });
+ }
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, id),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
+ const target = await db.query.tickets.findFirst({
+ where: eq(tickets.id, targetTicketId),
+ });
+
+ if (!target) {
+ throw new HTTPException(404, { message: 'Target ticket not found' });
+ }
+
+ // Check for duplicate
+ const existing = await db.query.ticketLinks.findFirst({
+ where: (table, { and, eq: eqFn }) =>
+ and(
+ eqFn(table.ticket_id, id),
+ eqFn(table.target_ticket_id, targetTicketId),
+ eqFn(table.link_type, linkType),
+ ),
+ });
+
+ if (existing) {
+ throw new HTTPException(422, { message: 'This link already exists' });
+ }
+
+ const creatorId = body.creator_id || getUserId(c);
+
+ const [link] = await db.insert(ticketLinks).values({
+ ticket_id: id,
+ target_ticket_id: targetTicketId,
+ link_type: linkType,
+ creator_id: creatorId,
+ }).returning();
+
+ if (!link) {
+ throw new HTTPException(500, { message: 'Failed to create link' });
+ }
+
+ // Create transactions on both tickets
+ const linkData = {
+ link_type: linkType,
+ link_id: link.id,
+ target_ticket_id: targetTicketId,
+ target_subject: target.subject,
+ };
+
+ const reverseLinkData = {
+ link_type: linkType,
+ link_id: link.id,
+ target_ticket_id: id,
+ target_subject: ticket.subject,
+ };
+
+ const [txSource] = await db.insert(transactions).values({
+ ticket_id: id,
+ transaction_type: 'LinkCreate',
+ field: linkType,
+ old_value: null,
+ new_value: String(targetTicketId),
+ data: linkData,
+ creator_id: creatorId,
+ }).returning();
+
+ const [txTarget] = await db.insert(transactions).values({
+ ticket_id: targetTicketId,
+ transaction_type: 'LinkCreate',
+ field: linkType,
+ old_value: null,
+ new_value: String(id),
+ data: reverseLinkData,
+ creator_id: creatorId,
+ }).returning();
+
+ // Run scrips on source ticket
+ if (txSource) {
+ const prepared = await scripEngine.prepare(id, [txSource] as any);
+ await scripEngine.commit(prepared);
+ }
+
+ // Run scrips on target ticket
+ if (txTarget) {
+ const prepared = await scripEngine.prepare(targetTicketId, [txTarget] as any);
+ await scripEngine.commit(prepared);
+ }
+
+ // Include target ticket info in response
+ return c.json({
+ ...link,
+ target_ticket: { id: target.id, subject: target.subject, status: target.status },
+ }, 201);
+ });
+
+ // DELETE /:id/links/:linkId โ remove a link
+ router.delete('/:id/links/:linkId', async (c) => {
+ const id = Number(c.req.param('id'));
+ const linkId = c.req.param('linkId');
+
+ const ticket = await db.query.tickets.findFirst({
+ where: eq(tickets.id, id),
+ });
+
+ if (!ticket) {
+ throw new HTTPException(404, { message: 'Ticket not found' });
+ }
+
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
+ const link = await db.query.ticketLinks.findFirst({
+ where: eq(ticketLinks.id, linkId),
+ });
+
+ if (!link || link.ticket_id !== id) {
+ throw new HTTPException(404, { message: 'Link not found' });
+ }
+
+ await db.delete(ticketLinks).where(eq(ticketLinks.id, linkId));
+
+ const target = await db.query.tickets.findFirst({
+ where: eq(tickets.id, link.target_ticket_id),
+ });
+
+ const creatorId = getUserId(c);
+
+ const [txSource] = await db.insert(transactions).values({
+ ticket_id: id,
+ transaction_type: 'LinkDelete',
+ field: link.link_type,
+ old_value: String(link.target_ticket_id),
+ new_value: null,
+ data: {
+ link_type: link.link_type,
+ target_ticket_id: link.target_ticket_id,
+ target_subject: target?.subject ?? 'unknown',
+ },
+ creator_id: creatorId,
+ }).returning();
+
+ // Run scrips on source ticket
+ if (txSource) {
+ const prepared = await scripEngine.prepare(id, [txSource] as any);
+ await scripEngine.commit(prepared);
+ }
+
+ return c.json({ ok: true });
+ });
+
+ // POST /:id/merge โ merge this ticket into another
+ router.post('/:id/merge', async (c) => {
+ const id = Number(c.req.param('id'));
+ const body = await c.req.json();
+ const targetId = Number(body.target_ticket_id);
+
+ if (!targetId || isNaN(targetId) || targetId === id) {
+ throw new HTTPException(422, { message: 'target_ticket_id must be a different ticket ID' });
+ }
+
+ const source = await db.query.tickets.findFirst({ where: eq(tickets.id, id) });
+ if (!source) throw new HTTPException(404, { message: 'Source ticket not found' });
+
+ const target = await db.query.tickets.findFirst({ where: eq(tickets.id, targetId) });
+ if (!target) throw new HTTPException(404, { message: 'Target ticket not found' });
+
+ await requireRight(c, db, source.queue_id, 'ticket.modify');
+ await requireRight(c, db, target.queue_id, 'ticket.modify');
+
+ const creatorId = getUserId(c);
+
+ // Move transactions
+ await db.update(transactions)
+ .set({ ticket_id: targetId } as any)
+ .where(eq(transactions.ticket_id, id));
+
+ // Move attachments
+ const sourceTxs = await db.query.transactions.findMany({ where: eq(transactions.ticket_id, id) });
+ // (attachments are linked via transaction_id which stays the same, no-op)
+
+ // Move custom field values
+ const sourceCfs = await db.query.customFieldValues.findMany({
+ where: eq(customFieldValues.ticket_id, id),
+ });
+ for (const cf of sourceCfs) {
+ const existing = await db.query.customFieldValues.findFirst({
+ where: (t, { and, eq: eqFn }) =>
+ and(
+ eqFn(t.custom_field_id, cf.custom_field_id),
+ eqFn(t.ticket_id, targetId),
+ eqFn(t.value, cf.value),
+ ),
+ });
+ if (existing) {
+ await db.delete(customFieldValues).where(eq(customFieldValues.id, cf.id));
+ } else {
+ await db.update(customFieldValues)
+ .set({ ticket_id: targetId } as any)
+ .where(eq(customFieldValues.id, cf.id));
+ }
+ }
+
+ // Move ticket links (update source references to target)
+ await db.update(ticketLinks)
+ .set({ ticket_id: targetId } as any)
+ .where(eq(ticketLinks.ticket_id, id));
+ // Update links pointing TO this ticket to point to target instead
+ await db.update(ticketLinks)
+ .set({ target_ticket_id: targetId } as any)
+ .where(eq(ticketLinks.target_ticket_id, id));
+
+ // Close the source ticket
+ await db.update(tickets).set({
+ status: 'closed',
+ updated_at: new Date(),
+ } as any).where(eq(tickets.id, id));
+
+ // Create merge transactions on both tickets
+ await db.insert(transactions).values({
+ ticket_id: targetId,
+ transaction_type: 'Comment',
+ data: { body: `Ticket ${source.id} (${source.subject}) was merged into this ticket.` },
+ creator_id: creatorId,
+ });
+
+ await db.insert(transactions).values({
+ ticket_id: id,
+ transaction_type: 'StatusChange',
+ field: 'status',
+ old_value: source.status,
+ new_value: 'closed',
+ data: { merged_into: targetId, body: `Merged into ticket ${targetId} (${target.subject}).` },
+ creator_id: creatorId,
+ });
+
+ // Create a duplicate link
+ await db.insert(ticketLinks).values({
+ ticket_id: id,
+ target_ticket_id: targetId,
+ link_type: 'Duplicates',
+ creator_id: creatorId,
+ }).onConflictDoNothing();
+
+ return c.json({ ok: true, target_id: targetId });
+ });
+
+ // POST /batch โ bulk update tickets
+ router.post('/batch', async (c) => {
+ const body = await c.req.json();
+ const ticketIds: number[] = (body.ticket_ids ?? []).map(Number).filter((n: number) => !isNaN(n) && n > 0);
+ const { status, owner_id, team_id } = body;
+
+ if (ticketIds.length === 0) {
+ throw new HTTPException(422, { message: 'ticket_ids is required and must be an array of ticket IDs' });
+ }
+
+ if (ticketIds.length > 100) {
+ throw new HTTPException(422, { message: 'Maximum 100 tickets per batch update' });
+ }
+
+ const results: Array<{ id: number; ok: boolean; error?: string }> = [];
+
+ for (const id of ticketIds) {
+ const ticket = await db.query.tickets.findFirst({ where: eq(tickets.id, id) });
+ if (!ticket) {
+ results.push({ id, ok: false, error: 'Ticket not found' });
+ continue;
+ }
+
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
+ try {
+ const txList: any[] = [];
+ const updateData: Record = { updated_at: new Date() };
+
+ if (status !== undefined && status !== ticket.status) {
+ // Validate lifecycle transition
+ const queue = await db.query.queues.findFirst({ where: eq(queues.id, ticket.queue_id) });
+ if (queue?.lifecycle_id) {
+ const lifecycle = await db.query.lifecycles.findFirst({ where: eq(lifecycles.id, queue.lifecycle_id) });
+ if (lifecycle) {
+ const lifecycleDef = lifecycle.definition as LifecycleDefinition;
+ const result = lifecycleValidator.validateTransition(lifecycleDef, ticket.status, status);
+ if (!result.valid) {
+ results.push({ id, ok: false, error: result.error ?? 'Invalid transition' });
+ continue;
+ }
+ // Dependency enforcement
+ const inactiveStatuses = lifecycleDef.statuses.inactive;
+ if (inactiveStatuses.includes(status)) {
+ const dependsOnLinks = await db.query.ticketLinks.findMany({
+ where: (t, { and, eq: eqFn }) => and(eqFn(t.ticket_id, id), eqFn(t.link_type, 'DependsOn')),
+ });
+ let blocked = false;
+ for (const link of dependsOnLinks) {
+ const target = await db.query.tickets.findFirst({ where: eq(tickets.id, link.target_ticket_id) });
+ if (target && !inactiveStatuses.includes(target.status)) {
+ results.push({ id, ok: false, error: `Blocked by ticket ${target.id} (${target.subject}) โ still ${target.status}` });
+ blocked = true;
+ break;
+ }
+ }
+ if (blocked) continue;
+ }
+ }
+ }
+ txList.push({
+ ticket_id: id,
+ transaction_type: 'StatusChange',
+ field: 'status',
+ old_value: ticket.status,
+ new_value: status,
+ creator_id: getUserId(c),
+ });
+ updateData.status = status;
+ }
+
+ if (owner_id !== undefined && owner_id !== ticket.owner_id) {
+ txList.push({
+ ticket_id: id,
+ transaction_type: 'SetOwner',
+ field: 'owner_id',
+ old_value: ticket.owner_id ?? null,
+ new_value: owner_id,
+ creator_id: getUserId(c),
+ });
+ updateData.owner_id = owner_id;
+ }
+
+ if (team_id !== undefined && team_id !== (ticket as any).team_id) {
+ txList.push({
+ ticket_id: id,
+ transaction_type: 'SetTeam',
+ field: 'team_id',
+ old_value: (ticket as any).team_id ?? null,
+ new_value: team_id,
+ creator_id: getUserId(c),
+ });
+ updateData.team_id = team_id;
+ }
+
+ await db.update(tickets).set(updateData as any).where(eq(tickets.id, id));
+ if (txList.length > 0) {
+ await db.insert(transactions).values(txList as any);
+ }
+
+ results.push({ id, ok: true });
+ } catch (err) {
+ results.push({ id, ok: false, error: err instanceof Error ? err.message : String(err) });
+ }
+ }
+
+ return c.json({ results });
+ });
+
// PATCH /:id/custom-fields/:fieldId โ set or clear a custom field value
router.patch('/:id/custom-fields/:fieldId', async (c) => {
const id = Number(c.req.param('id'));
@@ -549,6 +1242,8 @@ export function createTicketsRouter(db: Db): Hono {
throw new HTTPException(404, { message: 'Ticket not found' });
}
+ await requireRight(c, db, ticket.queue_id, 'ticket.modify');
+
const assignment = await db.query.queueCustomFields.findFirst({
where: and(
eq(queueCustomFields.queue_id, ticket.queue_id),
@@ -575,6 +1270,22 @@ export function createTicketsRouter(db: Db): Hono {
}
}
+ if (value && field.field_type === 'date') {
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
+ throw new HTTPException(422, { message: `${field.name}: value must be a date in YYYY-MM-DD format` });
+ }
+ const parsed = new Date(value);
+ if (isNaN(parsed.getTime())) {
+ throw new HTTPException(422, { message: `${field.name}: invalid date` });
+ }
+ }
+ if (value && field.field_type === 'datetime') {
+ const parsed = new Date(value);
+ if (isNaN(parsed.getTime())) {
+ throw new HTTPException(422, { message: `${field.name}: value must be a valid ISO 8601 datetime` });
+ }
+ }
+
if (value && field.pattern) {
const regex = new RegExp(field.pattern);
if (!regex.test(value)) {
@@ -613,7 +1324,7 @@ export function createTicketsRouter(db: Db): Hono {
field: field.key,
old_value: oldValue || null,
new_value: value || null,
- creator_id: '00000000-0000-0000-0000-000000000000',
+ creator_id: getUserId(c),
}).returning();
const prepared = await scripEngine.prepare(id, [tx] as any);
diff --git a/src/routes/users.ts b/src/routes/users.ts
index fa8be72..9bdd204 100644
--- a/src/routes/users.ts
+++ b/src/routes/users.ts
@@ -18,6 +18,8 @@ export function createUsersRouter(db: Db): Hono {
const body = await c.req.json();
const username = String(body.username ?? '').trim();
const email = body.email ? String(body.email).trim() : null;
+ const role = body.role ? String(body.role) : 'staff';
+ const password = body.password ? String(body.password).trim() : null;
if (!username) {
throw new HTTPException(400, { message: 'username is required' });
@@ -26,6 +28,8 @@ export function createUsersRouter(db: Db): Hono {
const [user] = await db.insert(users).values({
username,
email,
+ role,
+ password_hash: password ? await Bun.password.hash(password) : null,
}).returning();
if (!user) {
@@ -50,6 +54,10 @@ export function createUsersRouter(db: Db): Hono {
const updateData: Partial = {};
if (body.username !== undefined) updateData.username = String(body.username).trim();
if (body.email !== undefined) updateData.email = body.email ? String(body.email).trim() : null;
+ if (body.role !== undefined) updateData.role = String(body.role);
+ if (body.password !== undefined && String(body.password).trim()) {
+ updateData.password_hash = await Bun.password.hash(String(body.password).trim());
+ }
const [updated] = await db.update(users)
.set(updateData)
diff --git a/src/scrip/conditions.ts b/src/scrip/conditions.ts
index eacfc28..796c3d8 100644
--- a/src/scrip/conditions.ts
+++ b/src/scrip/conditions.ts
@@ -5,6 +5,7 @@ import type { LifecycleDefinition } from '../lifecycle/validator.ts';
export interface ConditionEvaluateContext {
lifecycleDef?: LifecycleDefinition;
+ customFields?: Record; // key โ value map of CF values
}
export interface ConditionConfig {
@@ -16,6 +17,7 @@ export interface ConditionConfig {
old_value?: unknown;
new_value?: unknown;
value?: unknown;
+ link_type?: unknown;
}
export interface ConditionEvaluator {
@@ -82,11 +84,52 @@ export class OnCustomFieldChange implements ConditionEvaluator {
}
}
+export class OnLinkCreate implements ConditionEvaluator {
+ evaluate(_ticket: Ticket, transactions: Transaction[], _context?: ConditionEvaluateContext, config?: ConditionConfig): boolean {
+ return transactions.some((tx) => {
+ if (tx.transaction_type !== 'LinkCreate') return false;
+ if (config?.link_type) {
+ const linkType = tx.field;
+ if (!matchesStatusFilter(linkType, config.link_type)) return false;
+ }
+ return true;
+ });
+ }
+}
+
+export class OnOverdue implements ConditionEvaluator {
+ evaluate(_ticket: Ticket, _transactions: Transaction[], context?: ConditionEvaluateContext, config?: ConditionConfig): boolean {
+ const fieldKey = config?.field_key ?? config?.field_id ?? config?.field;
+ if (!fieldKey) return false;
+
+ const cfValue = context?.customFields?.[String(fieldKey)];
+ if (!cfValue) return false;
+
+ // Parse the date value
+ const dueDate = new Date(cfValue);
+ if (isNaN(dueDate.getTime())) return false;
+
+ // Check if overdue (past due date)
+ if (new Date() <= dueDate) return false;
+
+ // Check that ticket is still active (not in inactive state)
+ const lifecycleDef = context?.lifecycleDef;
+ if (lifecycleDef) {
+ const inactiveStates = lifecycleDef.statuses.inactive;
+ if (inactiveStates.includes(_ticket.status)) return false;
+ }
+
+ return true;
+ }
+}
+
const conditionRegistry: Record = {
OnCreate: new OnCreate(),
OnStatusChange: new OnStatusChange(),
OnResolve: new OnResolve(),
OnCustomFieldChange: new OnCustomFieldChange(),
+ OnLinkCreate: new OnLinkCreate(),
+ OnOverdue: new OnOverdue(),
};
export function getConditionEvaluator(type: string): ConditionEvaluator | null {
diff --git a/src/scrip/engine.ts b/src/scrip/engine.ts
index 6e6f910..bc7d7d9 100644
--- a/src/scrip/engine.ts
+++ b/src/scrip/engine.ts
@@ -37,6 +37,7 @@ export class ScripEngine {
async prepare(
ticketId: number,
transactions: Transaction[],
+ stage: 'TransactionCreate' | 'TransactionBatch' = 'TransactionCreate',
): Promise {
const ticketRecord = await this.db.query.tickets.findFirst({
where: eq(tickets.id, ticketId),
@@ -53,6 +54,15 @@ export class ScripEngine {
const matchingScrips = allScrips.filter((scrip) => {
if (scrip.disabled) return false;
if (scrip.queue_id !== null && scrip.queue_id !== ticketRecord.queue_id) return false;
+ if (scrip.stage !== stage) return false;
+ // Filter by applicable transaction types โ if set, at least one tx must match
+ if (scrip.applicable_trans_types) {
+ const types = scrip.applicable_trans_types.split(',').map((t) => t.trim()).filter(Boolean);
+ if (types.length > 0 && !types.includes('Any')) {
+ const txTypes = new Set(transactions.map((tx) => tx.transaction_type));
+ if (!types.some((t) => txTypes.has(t))) return false;
+ }
+ }
return true;
});
@@ -70,10 +80,6 @@ export class ScripEngine {
}
}
- const conditionContext: ConditionEvaluateContext = {
- lifecycleDef,
- };
-
const cfValues = await this.db.query.customFieldValues.findMany({
where: eq(customFieldValues.ticket_id, ticketId),
});
@@ -94,6 +100,11 @@ export class ScripEngine {
}
}
+ const conditionContext: ConditionEvaluateContext = {
+ lifecycleDef,
+ customFields: customFieldsMap,
+ };
+
const prepared: PreparedScrip[] = [];
for (const scrip of matchingScrips) {
diff --git a/src/scrip/scheduler.ts b/src/scrip/scheduler.ts
new file mode 100644
index 0000000..67f797a
--- /dev/null
+++ b/src/scrip/scheduler.ts
@@ -0,0 +1,92 @@
+import type { Db } from '../db/index.ts';
+import { transactions, tickets, queues, lifecycles } from '../db/schema.ts';
+import { eq, and, isNull } from 'drizzle-orm';
+import { ScripEngine } from './engine.ts';
+
+const SYSTEM_USER = '00000000-0000-0000-0000-000000000000';
+
+/**
+ * Run scheduled scrips against all active tickets.
+ * Creates a synthetic "Scheduled" transaction so conditions like OnOverdue can fire.
+ */
+export async function runScheduledScrips(db: Db): Promise<{ checked: number; fired: number }> {
+ const engine = new ScripEngine(db);
+
+ // Get all lifecycles to determine inactive statuses
+ const allLifecycles = await db.query.lifecycles.findMany();
+ const inactiveByQueue = new Map>();
+
+ // Get all queues with lifecycles
+ const allQueues = await db.query.queues.findMany();
+ for (const q of allQueues) {
+ if (q.lifecycle_id) {
+ const lc = allLifecycles.find((l) => l.id === q.lifecycle_id);
+ if (lc) {
+ const def = lc.definition as any;
+ inactiveByQueue.set(q.id, new Set(def?.statuses?.inactive ?? ['resolved', 'closed']));
+ }
+ }
+ }
+
+ // Find all potentially active tickets
+ const allTickets = await db.query.tickets.findMany();
+ const active = allTickets.filter((t) => {
+ const inactive = inactiveByQueue.get(t.queue_id);
+ if (inactive) return !inactive.has(t.status);
+ return !['resolved', 'closed'].includes(t.status);
+ });
+
+ let fired = 0;
+
+ for (const ticket of active) {
+ try {
+ // Create a synthetic Scheduled transaction
+ const [tx] = await db.insert(transactions).values({
+ ticket_id: ticket.id,
+ transaction_type: 'Comment' as any,
+ field: 'scheduled',
+ data: { body: 'Scheduled scrip evaluation' },
+ creator_id: SYSTEM_USER,
+ } as any).returning();
+
+ if (!tx) continue;
+
+ // Run scrips
+ const prepared = await engine.prepare(ticket.id, [tx as any]);
+ if (prepared.length > 0) {
+ const results = await engine.commit(prepared);
+ const successes = results.filter((r) => r.success);
+ if (successes.length > 0) fired += successes.length;
+ }
+ } catch (err) {
+ // Log and continue โ don't let one failing ticket block the scheduler
+ console.error(`[scheduler] Error processing ticket ${ticket.id}:`, err instanceof Error ? err.message : String(err));
+ }
+ }
+
+ return { checked: active.length, fired };
+}
+
+/**
+ * Start the background scheduler. Runs every `intervalMinutes` minutes.
+ */
+export function startScheduler(db: Db, intervalMinutes = 5) {
+ console.log(`[scheduler] Starting scrip scheduler (every ${intervalMinutes}m)`);
+
+ const run = async () => {
+ try {
+ const result = await runScheduledScrips(db);
+ if (result.fired > 0) {
+ console.log(`[scheduler] Checked ${result.checked} tickets, fired ${result.fired} scrip actions`);
+ }
+ } catch (err) {
+ console.error('[scheduler] Error:', err instanceof Error ? err.message : String(err));
+ }
+ };
+
+ // Run once at startup after a short delay
+ setTimeout(run, 10000);
+
+ // Then run on interval
+ setInterval(run, intervalMinutes * 60 * 1000);
+}
diff --git a/web/src/app/admin/page-content.tsx b/web/src/app/admin/page-content.tsx
index ff1acbb..4949354 100644
--- a/web/src/app/admin/page-content.tsx
+++ b/web/src/app/admin/page-content.tsx
@@ -73,6 +73,7 @@ import {
removeTeamMember,
} from "@/lib/api";
import type { Queue, Ticket, Lifecycle, LifecycleDefinition, Scrip, Template, CustomField, QueueCustomField, TemplatePreview, User, Team } from "@/lib/types";
+import { ScripWizard } from "@/components/scrip-wizard";
import { cn } from "@/lib/utils";
function AdminHeader() {
@@ -802,6 +803,7 @@ return { message: "Metadata fetched" };`);
const [disabled, setDisabled] = useState(false);
const [saving, setSaving] = useState(false);
const [saveError, setSaveError] = useState(null);
+ const [wizardOpen, setWizardOpen] = useState(false);
const fetchScrips = useCallback(async () => {
setLoading(true);
@@ -1189,10 +1191,15 @@ return { message: "Metadata fetched" };`);
Build automations visually, then fine-tune the exact action payload in JSON.
-
+
+
+
+
{loading ? (
@@ -1701,6 +1708,24 @@ return { message: "Metadata fetched" };`);
)}
+ setWizardOpen(false)}
+ error={saveError}
+ onCreate={async (data) => {
+ // Strip nulls for Zod optional fields
+ const payload = Object.fromEntries(
+ Object.entries(data).filter(([, v]) => v !== null)
+ );
+ const { error: createErr } = await createScrip(payload as any);
+ if (createErr) { setSaveError(createErr); return; }
+ setWizardOpen(false);
+ await fetchScrips();
+ }}
+ queues={queues}
+ customFields={customFields}
+ templates={templates}
+ />
);
}
diff --git a/web/src/app/dashboards/[id]/page.tsx b/web/src/app/dashboards/[id]/page.tsx
index 6227430..83b8c25 100644
--- a/web/src/app/dashboards/[id]/page.tsx
+++ b/web/src/app/dashboards/[id]/page.tsx
@@ -41,6 +41,7 @@ import { CountWidget } from "@/components/widgets/count-widget";
import { TicketListWidget } from "@/components/widgets/ticket-list-widget";
import { StatusChartWidget } from "@/components/widgets/status-chart-widget";
import { GroupedCountsWidget } from "@/components/widgets/grouped-counts-widget";
+import { TrendChartWidget } from "@/components/widgets/trend-chart-widget";
import { cn } from "@/lib/utils";
function widgetGridStyle(position: { x: number; y: number; w: number; h: number }) {
@@ -331,13 +332,18 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
switch (widget.data.type) {
case "count":
+ case "my_tickets":
return ;
+ case "overdue":
+ return ;
case "ticket_list":
return ;
case "status_chart":
return ;
case "grouped_counts":
return ;
+ case "trend_chart":
+ return ;
default:
return (
@@ -554,6 +560,9 @@ export default function DashboardPage({ params }: { params: Promise<{ id: string
+
+
+
{addType === "grouped_counts" && (
diff --git a/web/src/app/layout.tsx b/web/src/app/layout.tsx
index 4db0e67..6bab278 100644
--- a/web/src/app/layout.tsx
+++ b/web/src/app/layout.tsx
@@ -4,6 +4,7 @@ import { Suspense } from "react";
import { ThemeProvider } from "next-themes";
import "./globals.css";
import { AppShell } from "@/components/app-shell";
+import { AuthProvider } from "@/lib/auth-context";
const ibmPlexSans = IBM_Plex_Sans({
subsets: ["latin"],
@@ -31,9 +32,11 @@ export default function RootLayout({
style={{ fontSize: "15px", lineHeight: 1.5 }}
>
- Loading...}>
- {children}
-
+
+ Loading...}>
+ {children}
+
+