@tangen/db 0.0.0-stage → 0.1.54
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +73 -2
- package/drizzle/0000_flimsy_shotgun.sql +339 -0
- package/drizzle/0001_app_data.sql +11 -0
- package/drizzle/0002_add_indexes.sql +36 -0
- package/drizzle/0003_extension_data_rename.sql +3 -0
- package/drizzle/0004_extension_data_published_globals.sql +12 -0
- package/drizzle/0005_auth.sql +51 -0
- package/drizzle/0006_auth_identities.sql +13 -0
- package/drizzle/0007_ai.sql +18 -0
- package/drizzle/0008_ai_chat_threads.sql +5 -0
- package/drizzle/0009_forms_source_slug.sql +11 -0
- package/drizzle/0010_rbac_roles_status.sql +14 -0
- package/drizzle/0011_jobs_v2.sql +67 -0
- package/drizzle/0012_search_platform.sql +36 -0
- package/drizzle/0013_search_synonym_label.sql +2 -0
- package/drizzle/0014_object_types_disabled.sql +2 -0
- package/drizzle/0015_playground_collections.sql +25 -0
- package/drizzle/0016_catchup_activity_promotion.sql +36 -0
- package/drizzle/0017_fields_disabled.sql +3 -0
- package/drizzle/0018_object_types_plural.sql +1 -0
- package/drizzle/0019_member_profile.sql +2 -0
- package/drizzle/0020_paused_jobs.sql +7 -0
- package/drizzle/0021_form_submission_notes.sql +2 -0
- package/drizzle/0022_paused_jobs_env_reason.sql +4 -0
- package/drizzle/0023_scale_indexes.sql +3 -0
- package/drizzle/0024_migration_runs.sql +24 -0
- package/drizzle/0025_porter_transfer.sql +30 -0
- package/drizzle/0026_cron_timezone.sql +1 -0
- package/drizzle/0027_rate_limit_counters.sql +6 -0
- package/drizzle/0028_realtime_events.sql +10 -0
- package/drizzle/0030_delivery_artifacts.sql +10 -0
- package/drizzle/0031_realtime_presence.sql +8 -0
- package/drizzle/0032_schema_pushes.sql +15 -0
- package/drizzle/0033_studio_design_capability.sql +37 -0
- package/drizzle/0034_object_types_provided_by.sql +12 -0
- package/drizzle/0035_custom_table_owners.sql +24 -0
- package/drizzle/0036_revision_version_unique.sql +42 -0
- package/drizzle/0037_job_queue_dedupe.sql +20 -0
- package/drizzle/0038_search_documents.sql +87 -0
- package/drizzle/0039_job_prune_indexes.sql +25 -0
- package/drizzle/0040_extension_data_object_idx.sql +10 -0
- package/drizzle/0041_knowledge.sql +79 -0
- package/drizzle/0042_schema_pushes_checksum.sql +7 -0
- package/drizzle/0043_guard_adoption.sql +12 -0
- package/drizzle/0044_retention_indexes.sql +7 -0
- package/drizzle/0045_extension_install_per_env.sql +40 -0
- package/drizzle/0046_extension_migrations.sql +18 -0
- package/drizzle/0047_environment_role.sql +25 -0
- package/drizzle/0048_environment_role_adoption.sql +30 -0
- package/drizzle/0049_release_lifecycle.sql +55 -0
- package/drizzle/0050_member_env_membership.sql +21 -0
- package/drizzle/0051_remove_environments.sql +350 -0
- package/drizzle/0052_release_actor_columns.sql +9 -0
- package/drizzle/0053_kernel_event_record.sql +26 -0
- package/drizzle/0054_kernel_run_identity.sql +13 -0
- package/drizzle/0055_kernel_identities.sql +19 -0
- package/drizzle/0056_forms_leave_core.sql +7 -0
- package/drizzle/0057_auth_leave_core.sql +8 -0
- package/drizzle/0058_member_grants.sql +11 -0
- package/drizzle/0059_extension_granted_grants.sql +7 -0
- package/drizzle/0060_connections.sql +21 -0
- package/drizzle/meta/0000_snapshot.json +1692 -0
- package/drizzle/meta/0002_snapshot.json +1935 -0
- package/drizzle/meta/0003_snapshot.json +1935 -0
- package/drizzle/meta/_journal.json +426 -0
- package/package.json +85 -4
- package/src/cache-keys.ts +43 -0
- package/src/client.ts +136 -0
- package/src/content-ddl.ts +70 -0
- package/src/content-tables.ts +110 -0
- package/src/custom-tables-loader.ts +65 -0
- package/src/ensure-system-schema.ts +195 -0
- package/src/extension-install-state.ts +220 -0
- package/src/extension-migrations.ts +270 -0
- package/src/generated/content-tables.ts +271 -0
- package/src/generated/custom-tables.ts +18 -0
- package/src/index.ts +24 -0
- package/src/instrument.ts +139 -0
- package/src/knowledge-schema.ts +232 -0
- package/src/migrate.ts +67 -0
- package/src/prepare-policy.ts +118 -0
- package/src/reconcile.ts +115 -0
- package/src/schema/ai-chats.ts +36 -0
- package/src/schema/ai-settings.ts +24 -0
- package/src/schema/api-keys.ts +37 -0
- package/src/schema/audit-log.ts +54 -0
- package/src/schema/connections.ts +35 -0
- package/src/schema/custom-table-owners.ts +32 -0
- package/src/schema/delivery-artifacts.ts +33 -0
- package/src/schema/entity-tags.ts +33 -0
- package/src/schema/extension-data.ts +38 -0
- package/src/schema/extension-migrations.ts +42 -0
- package/src/schema/fields.ts +37 -0
- package/src/schema/globals.ts +35 -0
- package/src/schema/identities.ts +27 -0
- package/src/schema/index.ts +51 -0
- package/src/schema/jobs.ts +104 -0
- package/src/schema/knowledge.ts +85 -0
- package/src/schema/media.ts +52 -0
- package/src/schema/member-grants.ts +34 -0
- package/src/schema/members.ts +49 -0
- package/src/schema/migration-runs.ts +46 -0
- package/src/schema/object-types.ts +41 -0
- package/src/schema/objects.ts +38 -0
- package/src/schema/paused-jobs.ts +25 -0
- package/src/schema/playground.ts +43 -0
- package/src/schema/porter-transfer.ts +47 -0
- package/src/schema/realtime.ts +45 -0
- package/src/schema/releases.ts +95 -0
- package/src/schema/roles.ts +27 -0
- package/src/schema/saved-views.ts +33 -0
- package/src/schema/schema-pushes.ts +43 -0
- package/src/schema/search-documents.ts +47 -0
- package/src/schema/search-query-log.ts +24 -0
- package/src/schema/search-recommendations.ts +27 -0
- package/src/schema/search-synonyms.ts +23 -0
- package/src/schema/webhook-deliveries.ts +37 -0
- package/src/schema/webhooks.ts +36 -0
- package/src/schema/workspace-activity.ts +35 -0
- package/src/schema/workspace-domains.ts +24 -0
- package/src/schema/workspace-installed-apps.ts +48 -0
- package/src/schema/workspaces.ts +24 -0
- package/src/schema-kit.ts +64 -0
- package/src/sealed.ts +84 -0
- package/src/seed/skeleton.ts +77 -0
- package/src/sql/apply-gin-indexes.ts +31 -0
- package/src/sql/gin-indexes.sql +11 -0
package/README.md
CHANGED
|
@@ -1,3 +1,74 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @tangen/db
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Database client and system schema for the Tangen headless CMS — a typed
|
|
4
|
+
Drizzle ORM client over postgres.js plus the platform's system table
|
|
5
|
+
definitions (workspaces, environments, object types, fields, members, …).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
bun add @tangen/db
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createDb, objectTypes, environments } from '@tangen/db'
|
|
17
|
+
|
|
18
|
+
const db = createDb(process.env.DATABASE_URL!, {
|
|
19
|
+
max: 10, // pool size (optional)
|
|
20
|
+
prepare: false, // opt out of prepared execution (optional; default true)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const types = await db.select().from(objectTypes)
|
|
24
|
+
await db.end()
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Pool options may also come from environment variables:
|
|
28
|
+
`TANGEN_DB_POOL_MAX`, `TANGEN_DB_IDLE_TIMEOUT`, `TANGEN_DB_CONNECT_TIMEOUT`,
|
|
29
|
+
`TANGEN_DB_PREPARE`.
|
|
30
|
+
|
|
31
|
+
### `TANGEN_DB_PREPARE`
|
|
32
|
+
|
|
33
|
+
**Defaults to `true`.** Tangen owns this contract rather than inheriting the
|
|
34
|
+
driver's, so an unset variable has one documented meaning. Precedence is
|
|
35
|
+
`createDb`'s `prepare` option, then `TANGEN_DB_PREPARE`, then the default; only
|
|
36
|
+
the exact strings `true` and `false` are recognised, and `tangen doctor` prints
|
|
37
|
+
the effective value with its source so a typo is visible.
|
|
38
|
+
|
|
39
|
+
Prepared execution reuses a named statement per SQL shape per connection, which
|
|
40
|
+
removes one PostgreSQL protocol round trip from every repeated parameterized
|
|
41
|
+
query. On a remote database that is roughly half of each statement.
|
|
42
|
+
|
|
43
|
+
`resolvePrepare(opts, env)` returns `{ prepare, source }` for anything that
|
|
44
|
+
needs to report the decision.
|
|
45
|
+
|
|
46
|
+
postgres.js does not honour a pool-level `prepare` for the queries Drizzle
|
|
47
|
+
issues, so `prepare-policy.ts` threads the option through by hand. Read that
|
|
48
|
+
file before changing anything here; it also carries the removal condition for
|
|
49
|
+
when Drizzle fixes this upstream.
|
|
50
|
+
|
|
51
|
+
Set `TANGEN_DB_PREPARE=false` for a pooler or proxy that cannot carry named
|
|
52
|
+
statements - PgBouncer in transaction mode with `max_prepared_statements = 0`,
|
|
53
|
+
or anything whose support you have not verified. Direct PostgreSQL, session
|
|
54
|
+
pooling, and PgBouncer 1.21+ transaction pooling with prepared statements
|
|
55
|
+
enabled all want the default. `false` is permanent: nothing overrides it.
|
|
56
|
+
|
|
57
|
+
One thing to know if you write SQL against this client: a named statement is
|
|
58
|
+
cached per distinct SQL string, per connection, until that connection closes.
|
|
59
|
+
Code that inlines a value into the SQL text instead of binding it as a
|
|
60
|
+
parameter mints a new prepared statement on every call. Tangen's own write
|
|
61
|
+
paths bind their values; extension or application code that concatenates would
|
|
62
|
+
accumulate.
|
|
63
|
+
|
|
64
|
+
## What's included
|
|
65
|
+
|
|
66
|
+
- `createDb(url, poolOptions?)` — Drizzle client with an `end()` helper
|
|
67
|
+
- All Tangen system table definitions (drizzle `pgTable` objects)
|
|
68
|
+
|
|
69
|
+
Project-specific generated content tables are **not** part of this package —
|
|
70
|
+
they are generated into your project by `tangen push`.
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
MIT
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "workspaces" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"name" text NOT NULL,
|
|
4
|
+
"slug" text NOT NULL,
|
|
5
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
6
|
+
"deleted_at" timestamp with time zone,
|
|
7
|
+
CONSTRAINT "workspaces_slug_unique" UNIQUE("slug")
|
|
8
|
+
);
|
|
9
|
+
--> statement-breakpoint
|
|
10
|
+
CREATE TABLE IF NOT EXISTS "environments" (
|
|
11
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
12
|
+
"workspace_id" text NOT NULL,
|
|
13
|
+
"slug" text NOT NULL,
|
|
14
|
+
"label" text NOT NULL,
|
|
15
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
16
|
+
CONSTRAINT "environments_workspace_id_slug_unique" UNIQUE("workspace_id","slug")
|
|
17
|
+
);
|
|
18
|
+
--> statement-breakpoint
|
|
19
|
+
CREATE TABLE IF NOT EXISTS "object_types" (
|
|
20
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
21
|
+
"env_id" text NOT NULL,
|
|
22
|
+
"name" text NOT NULL,
|
|
23
|
+
"label" text NOT NULL,
|
|
24
|
+
"singleton" boolean DEFAULT false NOT NULL,
|
|
25
|
+
"previous_names" text[] DEFAULT '{}' NOT NULL,
|
|
26
|
+
"admin_config" jsonb DEFAULT '{}' NOT NULL,
|
|
27
|
+
"schema_version" integer DEFAULT 1 NOT NULL,
|
|
28
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
29
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
30
|
+
CONSTRAINT "object_types_env_id_name_unique" UNIQUE("env_id","name")
|
|
31
|
+
);
|
|
32
|
+
--> statement-breakpoint
|
|
33
|
+
CREATE TABLE IF NOT EXISTS "fields" (
|
|
34
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
35
|
+
"env_id" text NOT NULL,
|
|
36
|
+
"type_id" text NOT NULL,
|
|
37
|
+
"name" text NOT NULL,
|
|
38
|
+
"label" text NOT NULL,
|
|
39
|
+
"field_type" text NOT NULL,
|
|
40
|
+
"config" jsonb DEFAULT '{}' NOT NULL,
|
|
41
|
+
"previous_names" text[] DEFAULT '{}' NOT NULL,
|
|
42
|
+
"position" integer DEFAULT 0 NOT NULL,
|
|
43
|
+
"required" boolean DEFAULT false NOT NULL,
|
|
44
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
45
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
46
|
+
CONSTRAINT "fields_type_id_name_unique" UNIQUE("type_id","name")
|
|
47
|
+
);
|
|
48
|
+
--> statement-breakpoint
|
|
49
|
+
CREATE TABLE IF NOT EXISTS "_revisions" (
|
|
50
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
51
|
+
"workspace_id" text NOT NULL,
|
|
52
|
+
"env_id" text NOT NULL,
|
|
53
|
+
"type_id" text NOT NULL,
|
|
54
|
+
"type_name" text NOT NULL,
|
|
55
|
+
"object_id" text NOT NULL,
|
|
56
|
+
"version" integer DEFAULT 1 NOT NULL,
|
|
57
|
+
"version_type" text,
|
|
58
|
+
"snapshot" jsonb NOT NULL,
|
|
59
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
60
|
+
"created_by" text
|
|
61
|
+
);
|
|
62
|
+
--> statement-breakpoint
|
|
63
|
+
CREATE TABLE IF NOT EXISTS "members" (
|
|
64
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
65
|
+
"workspace_id" text NOT NULL,
|
|
66
|
+
"email" text NOT NULL,
|
|
67
|
+
"name" text,
|
|
68
|
+
"password_hash" text NOT NULL,
|
|
69
|
+
"role" text DEFAULT 'editor' NOT NULL,
|
|
70
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
71
|
+
"last_login_at" timestamp with time zone,
|
|
72
|
+
CONSTRAINT "members_workspace_id_email_unique" UNIQUE("workspace_id","email")
|
|
73
|
+
);
|
|
74
|
+
--> statement-breakpoint
|
|
75
|
+
CREATE TABLE IF NOT EXISTS "refresh_tokens" (
|
|
76
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
77
|
+
"member_id" text NOT NULL,
|
|
78
|
+
"token_hash" text NOT NULL,
|
|
79
|
+
"expires_at" timestamp with time zone NOT NULL,
|
|
80
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
81
|
+
"revoked_at" timestamp with time zone
|
|
82
|
+
);
|
|
83
|
+
--> statement-breakpoint
|
|
84
|
+
CREATE TABLE IF NOT EXISTS "api_keys" (
|
|
85
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
86
|
+
"workspace_id" text NOT NULL,
|
|
87
|
+
"env_id" text NOT NULL,
|
|
88
|
+
"label" text NOT NULL,
|
|
89
|
+
"key_prefix" text NOT NULL,
|
|
90
|
+
"key_hash" text NOT NULL,
|
|
91
|
+
"permissions" jsonb DEFAULT '{}' NOT NULL,
|
|
92
|
+
"expires_at" timestamp with time zone,
|
|
93
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
94
|
+
"created_by" text,
|
|
95
|
+
"last_used_at" timestamp with time zone,
|
|
96
|
+
"revoked_at" timestamp with time zone
|
|
97
|
+
);
|
|
98
|
+
--> statement-breakpoint
|
|
99
|
+
CREATE TABLE IF NOT EXISTS "media_assets" (
|
|
100
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
101
|
+
"workspace_id" text NOT NULL,
|
|
102
|
+
"env_id" text NOT NULL,
|
|
103
|
+
"folder_id" text,
|
|
104
|
+
"filename" text NOT NULL,
|
|
105
|
+
"original_name" text NOT NULL,
|
|
106
|
+
"mime_type" text NOT NULL,
|
|
107
|
+
"size_bytes" bigint NOT NULL,
|
|
108
|
+
"width" integer,
|
|
109
|
+
"height" integer,
|
|
110
|
+
"alt_text" text,
|
|
111
|
+
"tags" text[] DEFAULT '{}' NOT NULL,
|
|
112
|
+
"metadata" jsonb DEFAULT '{}' NOT NULL,
|
|
113
|
+
"storage_path" text NOT NULL,
|
|
114
|
+
"thumbnail_path" text,
|
|
115
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
116
|
+
"created_by" text
|
|
117
|
+
);
|
|
118
|
+
--> statement-breakpoint
|
|
119
|
+
CREATE TABLE IF NOT EXISTS "media_folders" (
|
|
120
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
121
|
+
"workspace_id" text NOT NULL,
|
|
122
|
+
"env_id" text NOT NULL,
|
|
123
|
+
"name" text NOT NULL,
|
|
124
|
+
"parent_id" text,
|
|
125
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
126
|
+
);
|
|
127
|
+
--> statement-breakpoint
|
|
128
|
+
CREATE TABLE IF NOT EXISTS "webhooks" (
|
|
129
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
130
|
+
"workspace_id" text NOT NULL,
|
|
131
|
+
"env_id" text NOT NULL,
|
|
132
|
+
"label" text NOT NULL,
|
|
133
|
+
"url" text NOT NULL,
|
|
134
|
+
"secret" text,
|
|
135
|
+
"events" text[] DEFAULT '{}' NOT NULL,
|
|
136
|
+
"type_names" text[] DEFAULT '{}' NOT NULL,
|
|
137
|
+
"enabled" boolean DEFAULT true NOT NULL,
|
|
138
|
+
"headers" jsonb DEFAULT '{}' NOT NULL,
|
|
139
|
+
"consecutive_failures" integer DEFAULT 0 NOT NULL,
|
|
140
|
+
"paused_reason" text,
|
|
141
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
142
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
143
|
+
);
|
|
144
|
+
--> statement-breakpoint
|
|
145
|
+
CREATE TABLE IF NOT EXISTS "webhook_deliveries" (
|
|
146
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
147
|
+
"webhook_id" text NOT NULL,
|
|
148
|
+
"workspace_id" text NOT NULL,
|
|
149
|
+
"env_id" text NOT NULL,
|
|
150
|
+
"event" text NOT NULL,
|
|
151
|
+
"type_name" text NOT NULL,
|
|
152
|
+
"object_id" text NOT NULL,
|
|
153
|
+
"payload" jsonb NOT NULL,
|
|
154
|
+
"status_code" integer,
|
|
155
|
+
"response_body" text,
|
|
156
|
+
"duration_ms" integer,
|
|
157
|
+
"success" boolean NOT NULL,
|
|
158
|
+
"error" text,
|
|
159
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
160
|
+
);
|
|
161
|
+
--> statement-breakpoint
|
|
162
|
+
CREATE TABLE IF NOT EXISTS "audit_log" (
|
|
163
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
164
|
+
"workspace_id" text NOT NULL,
|
|
165
|
+
"env_id" text,
|
|
166
|
+
"event_type" text NOT NULL,
|
|
167
|
+
"event_id" text NOT NULL,
|
|
168
|
+
"actor_id" text,
|
|
169
|
+
"actor_type" text,
|
|
170
|
+
"payload" jsonb NOT NULL,
|
|
171
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
172
|
+
);
|
|
173
|
+
--> statement-breakpoint
|
|
174
|
+
CREATE TABLE IF NOT EXISTS "saved_views" (
|
|
175
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
176
|
+
"workspace_id" text NOT NULL,
|
|
177
|
+
"env_id" text NOT NULL,
|
|
178
|
+
"type_name" text NOT NULL,
|
|
179
|
+
"name" text NOT NULL,
|
|
180
|
+
"filters" jsonb DEFAULT '{}' NOT NULL,
|
|
181
|
+
"visibility" text DEFAULT 'private' NOT NULL,
|
|
182
|
+
"created_by" text NOT NULL,
|
|
183
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
184
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
185
|
+
);
|
|
186
|
+
--> statement-breakpoint
|
|
187
|
+
CREATE TABLE IF NOT EXISTS "member_env_roles" (
|
|
188
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
189
|
+
"member_id" text NOT NULL,
|
|
190
|
+
"env_id" text NOT NULL,
|
|
191
|
+
"workspace_id" text NOT NULL,
|
|
192
|
+
"role" text NOT NULL,
|
|
193
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
194
|
+
CONSTRAINT "member_env_roles_member_id_env_id_unique" UNIQUE("member_id","env_id")
|
|
195
|
+
);
|
|
196
|
+
--> statement-breakpoint
|
|
197
|
+
CREATE TABLE IF NOT EXISTS "workspace_domains" (
|
|
198
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
199
|
+
"workspace_id" text NOT NULL,
|
|
200
|
+
"domain" text NOT NULL,
|
|
201
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
202
|
+
CONSTRAINT "workspace_domains_domain_unique" UNIQUE("domain")
|
|
203
|
+
);
|
|
204
|
+
--> statement-breakpoint
|
|
205
|
+
CREATE TABLE IF NOT EXISTS "release_items" (
|
|
206
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
207
|
+
"release_id" text NOT NULL,
|
|
208
|
+
"object_id" text NOT NULL,
|
|
209
|
+
"type_name" text NOT NULL,
|
|
210
|
+
"field_snapshot" jsonb NOT NULL
|
|
211
|
+
);
|
|
212
|
+
--> statement-breakpoint
|
|
213
|
+
CREATE TABLE IF NOT EXISTS "releases" (
|
|
214
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
215
|
+
"name" text NOT NULL,
|
|
216
|
+
"workspace_id" text NOT NULL,
|
|
217
|
+
"source_env_id" text NOT NULL,
|
|
218
|
+
"target_env_id" text NOT NULL,
|
|
219
|
+
"status" text DEFAULT 'pending' NOT NULL,
|
|
220
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
221
|
+
"applied_at" timestamp with time zone
|
|
222
|
+
);
|
|
223
|
+
--> statement-breakpoint
|
|
224
|
+
CREATE TABLE IF NOT EXISTS "form_submissions" (
|
|
225
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
226
|
+
"form_id" text NOT NULL,
|
|
227
|
+
"workspace_id" text NOT NULL,
|
|
228
|
+
"env_id" text NOT NULL,
|
|
229
|
+
"data" jsonb NOT NULL,
|
|
230
|
+
"ip_address" text,
|
|
231
|
+
"metadata" jsonb,
|
|
232
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
233
|
+
);
|
|
234
|
+
--> statement-breakpoint
|
|
235
|
+
CREATE TABLE IF NOT EXISTS "form_app_settings" (
|
|
236
|
+
"workspace_id" text NOT NULL,
|
|
237
|
+
"env_id" text NOT NULL,
|
|
238
|
+
"settings" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
239
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
240
|
+
CONSTRAINT "form_app_settings_workspace_id_env_id_pk" PRIMARY KEY("workspace_id","env_id")
|
|
241
|
+
);
|
|
242
|
+
--> statement-breakpoint
|
|
243
|
+
CREATE TABLE IF NOT EXISTS "forms" (
|
|
244
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
245
|
+
"workspace_id" text NOT NULL,
|
|
246
|
+
"env_id" text NOT NULL,
|
|
247
|
+
"name" text NOT NULL,
|
|
248
|
+
"slug" text NOT NULL,
|
|
249
|
+
"is_live" boolean DEFAULT false NOT NULL,
|
|
250
|
+
"fields" jsonb DEFAULT '[]'::jsonb NOT NULL,
|
|
251
|
+
"settings" jsonb DEFAULT '{}'::jsonb NOT NULL,
|
|
252
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
253
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
254
|
+
);
|
|
255
|
+
--> statement-breakpoint
|
|
256
|
+
CREATE TABLE IF NOT EXISTS "workspace_installed_apps" (
|
|
257
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
258
|
+
"workspace_id" text NOT NULL,
|
|
259
|
+
"app_id" text NOT NULL,
|
|
260
|
+
"options" jsonb DEFAULT '{}' NOT NULL,
|
|
261
|
+
"installed_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
262
|
+
"installed_by" text
|
|
263
|
+
);
|
|
264
|
+
--> statement-breakpoint
|
|
265
|
+
DO $$ BEGIN
|
|
266
|
+
ALTER TABLE "environments" ADD CONSTRAINT "environments_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE no action ON UPDATE no action;
|
|
267
|
+
EXCEPTION
|
|
268
|
+
WHEN duplicate_object THEN null;
|
|
269
|
+
END $$;
|
|
270
|
+
--> statement-breakpoint
|
|
271
|
+
DO $$ BEGIN
|
|
272
|
+
ALTER TABLE "fields" ADD CONSTRAINT "fields_type_id_object_types_id_fk" FOREIGN KEY ("type_id") REFERENCES "public"."object_types"("id") ON DELETE no action ON UPDATE no action;
|
|
273
|
+
EXCEPTION
|
|
274
|
+
WHEN duplicate_object THEN null;
|
|
275
|
+
END $$;
|
|
276
|
+
--> statement-breakpoint
|
|
277
|
+
DO $$ BEGIN
|
|
278
|
+
ALTER TABLE "refresh_tokens" ADD CONSTRAINT "refresh_tokens_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE no action ON UPDATE no action;
|
|
279
|
+
EXCEPTION
|
|
280
|
+
WHEN duplicate_object THEN null;
|
|
281
|
+
END $$;
|
|
282
|
+
--> statement-breakpoint
|
|
283
|
+
DO $$ BEGIN
|
|
284
|
+
ALTER TABLE "media_assets" ADD CONSTRAINT "media_assets_folder_id_media_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "public"."media_folders"("id") ON DELETE no action ON UPDATE no action;
|
|
285
|
+
EXCEPTION
|
|
286
|
+
WHEN duplicate_object THEN null;
|
|
287
|
+
END $$;
|
|
288
|
+
--> statement-breakpoint
|
|
289
|
+
DO $$ BEGIN
|
|
290
|
+
ALTER TABLE "member_env_roles" ADD CONSTRAINT "member_env_roles_member_id_members_id_fk" FOREIGN KEY ("member_id") REFERENCES "public"."members"("id") ON DELETE cascade ON UPDATE no action;
|
|
291
|
+
EXCEPTION
|
|
292
|
+
WHEN duplicate_object THEN null;
|
|
293
|
+
END $$;
|
|
294
|
+
--> statement-breakpoint
|
|
295
|
+
DO $$ BEGIN
|
|
296
|
+
ALTER TABLE "member_env_roles" ADD CONSTRAINT "member_env_roles_env_id_environments_id_fk" FOREIGN KEY ("env_id") REFERENCES "public"."environments"("id") ON DELETE cascade ON UPDATE no action;
|
|
297
|
+
EXCEPTION
|
|
298
|
+
WHEN duplicate_object THEN null;
|
|
299
|
+
END $$;
|
|
300
|
+
--> statement-breakpoint
|
|
301
|
+
DO $$ BEGIN
|
|
302
|
+
ALTER TABLE "workspace_domains" ADD CONSTRAINT "workspace_domains_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;
|
|
303
|
+
EXCEPTION
|
|
304
|
+
WHEN duplicate_object THEN null;
|
|
305
|
+
END $$;
|
|
306
|
+
--> statement-breakpoint
|
|
307
|
+
DO $$ BEGIN
|
|
308
|
+
ALTER TABLE "release_items" ADD CONSTRAINT "release_items_release_id_releases_id_fk" FOREIGN KEY ("release_id") REFERENCES "public"."releases"("id") ON DELETE cascade ON UPDATE no action;
|
|
309
|
+
EXCEPTION
|
|
310
|
+
WHEN duplicate_object THEN null;
|
|
311
|
+
END $$;
|
|
312
|
+
--> statement-breakpoint
|
|
313
|
+
DO $$ BEGIN
|
|
314
|
+
ALTER TABLE "releases" ADD CONSTRAINT "releases_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;
|
|
315
|
+
EXCEPTION
|
|
316
|
+
WHEN duplicate_object THEN null;
|
|
317
|
+
END $$;
|
|
318
|
+
--> statement-breakpoint
|
|
319
|
+
DO $$ BEGIN
|
|
320
|
+
ALTER TABLE "form_submissions" ADD CONSTRAINT "form_submissions_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;
|
|
321
|
+
EXCEPTION
|
|
322
|
+
WHEN duplicate_object THEN null;
|
|
323
|
+
END $$;
|
|
324
|
+
--> statement-breakpoint
|
|
325
|
+
DO $$ BEGIN
|
|
326
|
+
ALTER TABLE "form_app_settings" ADD CONSTRAINT "form_app_settings_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;
|
|
327
|
+
EXCEPTION
|
|
328
|
+
WHEN duplicate_object THEN null;
|
|
329
|
+
END $$;
|
|
330
|
+
--> statement-breakpoint
|
|
331
|
+
DO $$ BEGIN
|
|
332
|
+
ALTER TABLE "forms" ADD CONSTRAINT "forms_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;
|
|
333
|
+
EXCEPTION
|
|
334
|
+
WHEN duplicate_object THEN null;
|
|
335
|
+
END $$;
|
|
336
|
+
--> statement-breakpoint
|
|
337
|
+
CREATE INDEX IF NOT EXISTS "revisions_object" ON "_revisions" ("workspace_id","env_id","object_id","created_at");--> statement-breakpoint
|
|
338
|
+
CREATE INDEX IF NOT EXISTS "audit_log_workspace" ON "audit_log" ("workspace_id","created_at");--> statement-breakpoint
|
|
339
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "workspace_installed_apps_ws_app_unique" ON "workspace_installed_apps" ("workspace_id","app_id");
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "app_data" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"app_id" text NOT NULL,
|
|
4
|
+
"object_id" text NOT NULL,
|
|
5
|
+
"workspace_id" text NOT NULL,
|
|
6
|
+
"env_id" text NOT NULL,
|
|
7
|
+
"data" jsonb DEFAULT '{}' NOT NULL,
|
|
8
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
9
|
+
);
|
|
10
|
+
--> statement-breakpoint
|
|
11
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "app_data_app_object_unique" ON "app_data" ("app_id","object_id","workspace_id","env_id");
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "app_data" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"app_id" text NOT NULL,
|
|
4
|
+
"object_id" text NOT NULL,
|
|
5
|
+
"workspace_id" text NOT NULL,
|
|
6
|
+
"env_id" text NOT NULL,
|
|
7
|
+
"data" jsonb DEFAULT '{}' NOT NULL,
|
|
8
|
+
"published_data" jsonb,
|
|
9
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
10
|
+
);
|
|
11
|
+
--> statement-breakpoint
|
|
12
|
+
CREATE TABLE IF NOT EXISTS "globals" (
|
|
13
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
14
|
+
"name" text NOT NULL,
|
|
15
|
+
"workspace_id" text NOT NULL,
|
|
16
|
+
"env_id" text NOT NULL,
|
|
17
|
+
"data" jsonb DEFAULT '{}' NOT NULL,
|
|
18
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
19
|
+
);
|
|
20
|
+
--> statement-breakpoint
|
|
21
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "app_data_app_object_unique" ON "app_data" ("app_id","object_id","workspace_id","env_id");--> statement-breakpoint
|
|
22
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "globals_workspace_env_name_unique" ON "globals" ("workspace_id","env_id","name");--> statement-breakpoint
|
|
23
|
+
CREATE INDEX IF NOT EXISTS "refresh_tokens_member_idx" ON "refresh_tokens" ("member_id");--> statement-breakpoint
|
|
24
|
+
CREATE INDEX IF NOT EXISTS "api_keys_key_hash_idx" ON "api_keys" ("key_hash");--> statement-breakpoint
|
|
25
|
+
CREATE INDEX IF NOT EXISTS "media_assets_workspace_env_idx" ON "media_assets" ("workspace_id","env_id");--> statement-breakpoint
|
|
26
|
+
CREATE INDEX IF NOT EXISTS "media_assets_folder_idx" ON "media_assets" ("folder_id");--> statement-breakpoint
|
|
27
|
+
CREATE INDEX IF NOT EXISTS "webhooks_workspace_env_idx" ON "webhooks" ("workspace_id","env_id");--> statement-breakpoint
|
|
28
|
+
CREATE INDEX IF NOT EXISTS "webhook_deliveries_workspace_env_idx" ON "webhook_deliveries" ("workspace_id","env_id");--> statement-breakpoint
|
|
29
|
+
CREATE INDEX IF NOT EXISTS "webhook_deliveries_webhook_idx" ON "webhook_deliveries" ("webhook_id");--> statement-breakpoint
|
|
30
|
+
CREATE INDEX IF NOT EXISTS "saved_views_workspace_env_idx" ON "saved_views" ("workspace_id","env_id");--> statement-breakpoint
|
|
31
|
+
CREATE INDEX IF NOT EXISTS "release_items_release_idx" ON "release_items" ("release_id");--> statement-breakpoint
|
|
32
|
+
CREATE INDEX IF NOT EXISTS "release_items_object_idx" ON "release_items" ("object_id");--> statement-breakpoint
|
|
33
|
+
CREATE INDEX IF NOT EXISTS "releases_workspace_idx" ON "releases" ("workspace_id");--> statement-breakpoint
|
|
34
|
+
CREATE INDEX IF NOT EXISTS "form_submissions_workspace_env_idx" ON "form_submissions" ("workspace_id","env_id");--> statement-breakpoint
|
|
35
|
+
CREATE INDEX IF NOT EXISTS "form_submissions_form_idx" ON "form_submissions" ("form_id");--> statement-breakpoint
|
|
36
|
+
CREATE INDEX IF NOT EXISTS "forms_workspace_env_idx" ON "forms" ("workspace_id","env_id");
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- Adds published_data column to extension_data (idempotent: previously in 0002_app_data_published.sql but not journalled)
|
|
2
|
+
ALTER TABLE "extension_data" ADD COLUMN IF NOT EXISTS "published_data" jsonb;--> statement-breakpoint
|
|
3
|
+
-- Creates globals table (idempotent: previously in 0003_globals.sql but not journalled)
|
|
4
|
+
CREATE TABLE IF NOT EXISTS "globals" (
|
|
5
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
6
|
+
"name" text NOT NULL,
|
|
7
|
+
"workspace_id" text NOT NULL,
|
|
8
|
+
"env_id" text NOT NULL,
|
|
9
|
+
"data" jsonb DEFAULT '{}' NOT NULL,
|
|
10
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
11
|
+
);--> statement-breakpoint
|
|
12
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "globals_workspace_env_name_unique" ON "globals" ("workspace_id","env_id","name");
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "auth_users" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"workspace_id" text NOT NULL,
|
|
4
|
+
"env_id" text NOT NULL,
|
|
5
|
+
"email" text,
|
|
6
|
+
"name" text,
|
|
7
|
+
"password_hash" text,
|
|
8
|
+
"role" text DEFAULT 'user' NOT NULL,
|
|
9
|
+
"status" text DEFAULT 'active' NOT NULL,
|
|
10
|
+
"is_anonymous" boolean DEFAULT false NOT NULL,
|
|
11
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
12
|
+
"last_login_at" timestamp with time zone
|
|
13
|
+
);
|
|
14
|
+
--> statement-breakpoint
|
|
15
|
+
CREATE TABLE IF NOT EXISTS "auth_sessions" (
|
|
16
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
17
|
+
"user_id" text NOT NULL,
|
|
18
|
+
"workspace_id" text NOT NULL,
|
|
19
|
+
"env_id" text NOT NULL,
|
|
20
|
+
"refresh_token_hash" text NOT NULL,
|
|
21
|
+
"expires_at" timestamp with time zone NOT NULL,
|
|
22
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
23
|
+
"revoked_at" timestamp with time zone,
|
|
24
|
+
CONSTRAINT "auth_sessions_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth_users"("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
|
25
|
+
);
|
|
26
|
+
--> statement-breakpoint
|
|
27
|
+
CREATE TABLE IF NOT EXISTS "auth_sso_accounts" (
|
|
28
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
29
|
+
"user_id" text NOT NULL,
|
|
30
|
+
"workspace_id" text NOT NULL,
|
|
31
|
+
"env_id" text NOT NULL,
|
|
32
|
+
"provider" text NOT NULL,
|
|
33
|
+
"provider_user_id" text NOT NULL,
|
|
34
|
+
"access_token" text,
|
|
35
|
+
"refresh_token" text,
|
|
36
|
+
"expires_at" timestamp with time zone,
|
|
37
|
+
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
|
|
38
|
+
CONSTRAINT "auth_sso_accounts_user_id_auth_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "auth_users"("id") ON DELETE CASCADE ON UPDATE NO ACTION
|
|
39
|
+
);
|
|
40
|
+
--> statement-breakpoint
|
|
41
|
+
CREATE INDEX IF NOT EXISTS "auth_users_workspace_env_idx" ON "auth_users" ("workspace_id","env_id");
|
|
42
|
+
--> statement-breakpoint
|
|
43
|
+
CREATE INDEX IF NOT EXISTS "auth_users_anon_idx" ON "auth_users" ("workspace_id","env_id","is_anonymous");
|
|
44
|
+
--> statement-breakpoint
|
|
45
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "auth_users_workspace_env_email_idx" ON "auth_users" ("workspace_id","env_id","email") WHERE email IS NOT NULL;
|
|
46
|
+
--> statement-breakpoint
|
|
47
|
+
CREATE INDEX IF NOT EXISTS "auth_sessions_user_idx" ON "auth_sessions" ("user_id");
|
|
48
|
+
--> statement-breakpoint
|
|
49
|
+
CREATE INDEX IF NOT EXISTS "auth_sessions_workspace_env_idx" ON "auth_sessions" ("workspace_id","env_id");
|
|
50
|
+
--> statement-breakpoint
|
|
51
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "auth_sso_accounts_provider_user_idx" ON "auth_sso_accounts" ("workspace_id","env_id","provider","provider_user_id");
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "auth_identities" (
|
|
2
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"user_id" text NOT NULL REFERENCES "auth_users"("id") ON DELETE CASCADE,
|
|
4
|
+
"workspace_id" text NOT NULL,
|
|
5
|
+
"env_id" text NOT NULL,
|
|
6
|
+
"provider" text NOT NULL,
|
|
7
|
+
"provider_user_id" text NOT NULL,
|
|
8
|
+
"created_at" timestamptz NOT NULL DEFAULT now()
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "auth_identities_user_provider_idx" ON "auth_identities"("user_id","provider");
|
|
12
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "auth_identities_provider_uid_idx" ON "auth_identities"("workspace_id","env_id","provider","provider_user_id");
|
|
13
|
+
CREATE INDEX IF NOT EXISTS "auth_identities_user_idx" ON "auth_identities"("user_id");
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
CREATE TABLE IF NOT EXISTS "ai_settings" (
|
|
2
|
+
"workspace_id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"data" jsonb DEFAULT '{}' NOT NULL,
|
|
4
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
5
|
+
);
|
|
6
|
+
--> statement-breakpoint
|
|
7
|
+
CREATE TABLE IF NOT EXISTS "ai_chats" (
|
|
8
|
+
"id" text PRIMARY KEY NOT NULL,
|
|
9
|
+
"workspace_id" text NOT NULL,
|
|
10
|
+
"env_id" text NOT NULL,
|
|
11
|
+
"object_type" text NOT NULL,
|
|
12
|
+
"object_id" text NOT NULL,
|
|
13
|
+
"member_id" text NOT NULL,
|
|
14
|
+
"messages" jsonb DEFAULT '[]' NOT NULL,
|
|
15
|
+
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
|
16
|
+
);
|
|
17
|
+
--> statement-breakpoint
|
|
18
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "ai_chats_scope_unique" ON "ai_chats" ("workspace_id","env_id","object_type","object_id","member_id");
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
ALTER TABLE "ai_chats" ADD COLUMN IF NOT EXISTS "title" text;
|
|
2
|
+
--> statement-breakpoint
|
|
3
|
+
DROP INDEX IF EXISTS "ai_chats_scope_unique";
|
|
4
|
+
--> statement-breakpoint
|
|
5
|
+
CREATE INDEX IF NOT EXISTS "ai_chats_scope_idx" ON "ai_chats" ("workspace_id","env_id","object_type","object_id","member_id");
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
-- Forms v2: code-first forms need an origin marker and a slug identity.
|
|
2
|
+
-- Pre-dedupe: suffix any duplicate slugs before the unique index lands.
|
|
3
|
+
WITH d AS (
|
|
4
|
+
SELECT id, ROW_NUMBER() OVER (PARTITION BY workspace_id, env_id, slug ORDER BY created_at) AS rn
|
|
5
|
+
FROM forms
|
|
6
|
+
)
|
|
7
|
+
UPDATE forms f SET slug = f.slug || '-' || d.rn
|
|
8
|
+
FROM d WHERE f.id = d.id AND d.rn > 1;
|
|
9
|
+
|
|
10
|
+
ALTER TABLE forms ADD COLUMN IF NOT EXISTS source text NOT NULL DEFAULT 'admin';
|
|
11
|
+
CREATE UNIQUE INDEX IF NOT EXISTS forms_workspace_env_slug_unique ON forms (workspace_id, env_id, slug);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- RBAC: workspace-defined custom roles + member suspension status.
|
|
2
|
+
CREATE TABLE IF NOT EXISTS roles (
|
|
3
|
+
id text PRIMARY KEY,
|
|
4
|
+
workspace_id text NOT NULL,
|
|
5
|
+
slug text NOT NULL,
|
|
6
|
+
name text NOT NULL,
|
|
7
|
+
description text,
|
|
8
|
+
permissions jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
9
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
10
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
11
|
+
CONSTRAINT roles_workspace_slug_unique UNIQUE (workspace_id, slug)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
ALTER TABLE members ADD COLUMN IF NOT EXISTS status text NOT NULL DEFAULT 'active';
|