@freema/drobek-modules 0.3.3

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.
Files changed (33) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +53 -0
  3. package/dist/chunk-WHKPW5MP.js +6611 -0
  4. package/dist/index.d.ts +1604 -0
  5. package/dist/index.js +373 -0
  6. package/dist/mail-guard.d-BKJhsVA2.d.ts +2097 -0
  7. package/dist/migrations/core/0000_dusty_scarecrow.sql +84 -0
  8. package/dist/migrations/core/0001_busy_orphan.sql +69 -0
  9. package/dist/migrations/core/0002_odd_alex_power.sql +17 -0
  10. package/dist/migrations/core/0003_nosy_shatterstar.sql +26 -0
  11. package/dist/migrations/core/0004_nice_ben_parker.sql +27 -0
  12. package/dist/migrations/core/0005_panoramic_bill_hollister.sql +4 -0
  13. package/dist/migrations/core/0006_outgoing_ricochet.sql +29 -0
  14. package/dist/migrations/core/0007_app_versions.sql +95 -0
  15. package/dist/migrations/core/0008_user_bound_tokens.sql +42 -0
  16. package/dist/migrations/core/0009_app_name.sql +1 -0
  17. package/dist/migrations/core/0010_apps_origin.sql +12 -0
  18. package/dist/migrations/core/0011_modules.sql +32 -0
  19. package/dist/migrations/core/0012_data_module_tables.sql +9 -0
  20. package/dist/migrations/core/0014_get_logs.sql +26 -0
  21. package/dist/migrations/core/0016_apps_slug_release.sql +8 -0
  22. package/dist/migrations/core/0018_custom_domains.sql +22 -0
  23. package/dist/migrations/core/0021_abuse_reports.sql +23 -0
  24. package/dist/migrations/core/0022_gallery.sql +20 -0
  25. package/dist/migrations/core/0023_workspace_modules.sql +13 -0
  26. package/dist/migrations/core/0024_app_assets.sql +18 -0
  27. package/dist/migrations/core/0025_app_asset_snapshots.sql +28 -0
  28. package/dist/migrations/core/0026_publish_approval.sql +13 -0
  29. package/dist/migrations/core/0027_publish_block.sql +6 -0
  30. package/dist/migrations/core/meta/_journal.json +167 -0
  31. package/dist/testing.d.ts +269 -0
  32. package/dist/testing.js +1569 -0
  33. package/package.json +63 -0
@@ -0,0 +1,84 @@
1
+ CREATE TYPE "public"."app_status" AS ENUM('live', 'hibernated');--> statement-breakpoint
2
+ CREATE TYPE "public"."app_visibility" AS ENUM('public', 'team', 'password');--> statement-breakpoint
3
+ CREATE TYPE "public"."membership_role" AS ENUM('workspace-admin', 'editor', 'viewer');--> statement-breakpoint
4
+ CREATE TYPE "public"."routing_mode" AS ENUM('spa', 'exact');--> statement-breakpoint
5
+ CREATE TYPE "public"."workspace_kind" AS ENUM('personal', 'team');--> statement-breakpoint
6
+ CREATE TABLE "apps" (
7
+ "id" text PRIMARY KEY NOT NULL,
8
+ "workspace_id" text NOT NULL,
9
+ "slug" text NOT NULL,
10
+ "active_deploy_id" text,
11
+ "routing_mode" "routing_mode" DEFAULT 'spa' NOT NULL,
12
+ "visibility" "app_visibility" DEFAULT 'public' NOT NULL,
13
+ "password_hash" text,
14
+ "status" "app_status" DEFAULT 'live' NOT NULL,
15
+ "uses_end_user_auth" boolean DEFAULT false NOT NULL,
16
+ "created_at" timestamp DEFAULT now() NOT NULL,
17
+ "deleted_at" timestamp
18
+ );
19
+ --> statement-breakpoint
20
+ CREATE TABLE "blob_refs" (
21
+ "sha256" text NOT NULL,
22
+ "deploy_id" text NOT NULL,
23
+ CONSTRAINT "blob_refs_sha256_deploy_id_pk" PRIMARY KEY("sha256","deploy_id")
24
+ );
25
+ --> statement-breakpoint
26
+ CREATE TABLE "blobs" (
27
+ "sha256" text PRIMARY KEY NOT NULL,
28
+ "content_type" text NOT NULL,
29
+ "size" bigint NOT NULL,
30
+ "path" text NOT NULL
31
+ );
32
+ --> statement-breakpoint
33
+ CREATE TABLE "deploy_files" (
34
+ "deploy_id" text NOT NULL,
35
+ "path" text NOT NULL,
36
+ "sha256" text NOT NULL,
37
+ CONSTRAINT "deploy_files_deploy_id_path_pk" PRIMARY KEY("deploy_id","path")
38
+ );
39
+ --> statement-breakpoint
40
+ CREATE TABLE "deploys" (
41
+ "id" text PRIMARY KEY NOT NULL,
42
+ "app_id" text NOT NULL,
43
+ "manifest" jsonb NOT NULL,
44
+ "lint_report" jsonb,
45
+ "created_at" timestamp DEFAULT now() NOT NULL,
46
+ "deleted_at" timestamp
47
+ );
48
+ --> statement-breakpoint
49
+ CREATE TABLE "memberships" (
50
+ "user_id" text NOT NULL,
51
+ "workspace_id" text NOT NULL,
52
+ "role" "membership_role" NOT NULL,
53
+ "created_at" timestamp DEFAULT now() NOT NULL,
54
+ CONSTRAINT "memberships_user_id_workspace_id_pk" PRIMARY KEY("user_id","workspace_id")
55
+ );
56
+ --> statement-breakpoint
57
+ CREATE TABLE "users" (
58
+ "id" text PRIMARY KEY NOT NULL,
59
+ "email" text NOT NULL,
60
+ "google_sub" text,
61
+ "created_at" timestamp DEFAULT now() NOT NULL,
62
+ CONSTRAINT "users_email_unique" UNIQUE("email"),
63
+ CONSTRAINT "users_google_sub_unique" UNIQUE("google_sub")
64
+ );
65
+ --> statement-breakpoint
66
+ CREATE TABLE "workspaces" (
67
+ "id" text PRIMARY KEY NOT NULL,
68
+ "kind" "workspace_kind" NOT NULL,
69
+ "slug" text NOT NULL,
70
+ "name" text NOT NULL,
71
+ "created_at" timestamp DEFAULT now() NOT NULL,
72
+ CONSTRAINT "workspaces_slug_unique" UNIQUE("slug")
73
+ );
74
+ --> statement-breakpoint
75
+ ALTER TABLE "apps" ADD CONSTRAINT "apps_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
76
+ ALTER TABLE "apps" ADD CONSTRAINT "apps_active_deploy_id_deploys_id_fk" FOREIGN KEY ("active_deploy_id") REFERENCES "public"."deploys"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
77
+ ALTER TABLE "blob_refs" ADD CONSTRAINT "blob_refs_sha256_blobs_sha256_fk" FOREIGN KEY ("sha256") REFERENCES "public"."blobs"("sha256") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
78
+ ALTER TABLE "blob_refs" ADD CONSTRAINT "blob_refs_deploy_id_deploys_id_fk" FOREIGN KEY ("deploy_id") REFERENCES "public"."deploys"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
79
+ ALTER TABLE "deploy_files" ADD CONSTRAINT "deploy_files_deploy_id_deploys_id_fk" FOREIGN KEY ("deploy_id") REFERENCES "public"."deploys"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
80
+ ALTER TABLE "deploy_files" ADD CONSTRAINT "deploy_files_sha256_blobs_sha256_fk" FOREIGN KEY ("sha256") REFERENCES "public"."blobs"("sha256") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
81
+ ALTER TABLE "deploys" ADD CONSTRAINT "deploys_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
82
+ ALTER TABLE "memberships" ADD CONSTRAINT "memberships_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
83
+ ALTER TABLE "memberships" ADD CONSTRAINT "memberships_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
84
+ CREATE UNIQUE INDEX "apps_workspace_slug_uq" ON "apps" USING btree ("workspace_id","slug");
@@ -0,0 +1,69 @@
1
+ CREATE TABLE "oauth_access_tokens" (
2
+ "id" text PRIMARY KEY NOT NULL,
3
+ "token_hash" text NOT NULL,
4
+ "user_id" text NOT NULL,
5
+ "workspace_id" text NOT NULL,
6
+ "role" "membership_role" NOT NULL,
7
+ "oauth_client_id" text,
8
+ "scope" text NOT NULL,
9
+ "audience" text NOT NULL,
10
+ "expires_at" timestamp NOT NULL,
11
+ "created_at" timestamp DEFAULT now() NOT NULL,
12
+ "revoked_at" timestamp,
13
+ CONSTRAINT "oauth_access_tokens_token_hash_unique" UNIQUE("token_hash")
14
+ );
15
+ --> statement-breakpoint
16
+ CREATE TABLE "oauth_authorization_codes" (
17
+ "id" text PRIMARY KEY NOT NULL,
18
+ "code_hash" text NOT NULL,
19
+ "client_id" text NOT NULL,
20
+ "user_id" text NOT NULL,
21
+ "workspace_id" text NOT NULL,
22
+ "role" "membership_role" NOT NULL,
23
+ "redirect_uri" text NOT NULL,
24
+ "code_challenge" text NOT NULL,
25
+ "code_challenge_method" text NOT NULL,
26
+ "scope" text NOT NULL,
27
+ "resource" text NOT NULL,
28
+ "used" boolean DEFAULT false NOT NULL,
29
+ "expires_at" timestamp NOT NULL,
30
+ "created_at" timestamp DEFAULT now() NOT NULL,
31
+ CONSTRAINT "oauth_authorization_codes_code_hash_unique" UNIQUE("code_hash")
32
+ );
33
+ --> statement-breakpoint
34
+ CREATE TABLE "oauth_clients" (
35
+ "id" text PRIMARY KEY NOT NULL,
36
+ "client_id" text NOT NULL,
37
+ "client_name" text NOT NULL,
38
+ "redirect_uris" text[] NOT NULL,
39
+ "token_endpoint_auth_method" text DEFAULT 'none' NOT NULL,
40
+ "created_at" timestamp DEFAULT now() NOT NULL,
41
+ CONSTRAINT "oauth_clients_client_id_unique" UNIQUE("client_id")
42
+ );
43
+ --> statement-breakpoint
44
+ CREATE TABLE "oauth_refresh_tokens" (
45
+ "id" text PRIMARY KEY NOT NULL,
46
+ "token_hash" text NOT NULL,
47
+ "user_id" text NOT NULL,
48
+ "workspace_id" text NOT NULL,
49
+ "role" "membership_role" NOT NULL,
50
+ "oauth_client_id" text,
51
+ "scope" text NOT NULL,
52
+ "audience" text NOT NULL,
53
+ "rotated_to" text,
54
+ "used_at" timestamp,
55
+ "expires_at" timestamp NOT NULL,
56
+ "created_at" timestamp DEFAULT now() NOT NULL,
57
+ CONSTRAINT "oauth_refresh_tokens_token_hash_unique" UNIQUE("token_hash")
58
+ );
59
+ --> statement-breakpoint
60
+ ALTER TABLE "oauth_access_tokens" ADD CONSTRAINT "oauth_access_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
61
+ ALTER TABLE "oauth_access_tokens" ADD CONSTRAINT "oauth_access_tokens_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
62
+ ALTER TABLE "oauth_access_tokens" ADD CONSTRAINT "oauth_access_tokens_oauth_client_id_oauth_clients_id_fk" FOREIGN KEY ("oauth_client_id") REFERENCES "public"."oauth_clients"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
63
+ ALTER TABLE "oauth_authorization_codes" ADD CONSTRAINT "oauth_authorization_codes_client_id_oauth_clients_client_id_fk" FOREIGN KEY ("client_id") REFERENCES "public"."oauth_clients"("client_id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
64
+ ALTER TABLE "oauth_authorization_codes" ADD CONSTRAINT "oauth_authorization_codes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
65
+ ALTER TABLE "oauth_authorization_codes" ADD CONSTRAINT "oauth_authorization_codes_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
66
+ ALTER TABLE "oauth_refresh_tokens" ADD CONSTRAINT "oauth_refresh_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
67
+ ALTER TABLE "oauth_refresh_tokens" ADD CONSTRAINT "oauth_refresh_tokens_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
68
+ ALTER TABLE "oauth_refresh_tokens" ADD CONSTRAINT "oauth_refresh_tokens_oauth_client_id_oauth_clients_id_fk" FOREIGN KEY ("oauth_client_id") REFERENCES "public"."oauth_clients"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
69
+ ALTER TABLE "oauth_refresh_tokens" ADD CONSTRAINT "oauth_refresh_tokens_rotated_to_oauth_refresh_tokens_id_fk" FOREIGN KEY ("rotated_to") REFERENCES "public"."oauth_refresh_tokens"("id") ON DELETE no action ON UPDATE no action;
@@ -0,0 +1,17 @@
1
+ CREATE TYPE "public"."deploy_state" AS ENUM('awaiting_upload', 'queued', 'linting', 'storing', 'activating', 'ready', 'failed');--> statement-breakpoint
2
+ CREATE TABLE "audit_log" (
3
+ "id" text PRIMARY KEY NOT NULL,
4
+ "workspace_id" text NOT NULL,
5
+ "actor_user_id" text,
6
+ "action" text NOT NULL,
7
+ "target" text,
8
+ "meta" jsonb,
9
+ "created_at" timestamp DEFAULT now() NOT NULL
10
+ );
11
+ --> statement-breakpoint
12
+ ALTER TABLE "deploys" ADD COLUMN "state" "deploy_state" DEFAULT 'awaiting_upload' NOT NULL;--> statement-breakpoint
13
+ ALTER TABLE "deploys" ADD COLUMN "error" text;--> statement-breakpoint
14
+ ALTER TABLE "deploys" ADD COLUMN "total_bytes" bigint;--> statement-breakpoint
15
+ ALTER TABLE "deploys" ADD COLUMN "activated_at" timestamp;--> statement-breakpoint
16
+ ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
17
+ ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_actor_user_id_users_id_fk" FOREIGN KEY ("actor_user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
@@ -0,0 +1,26 @@
1
+ CREATE TYPE "public"."collection_access_mode" AS ENUM('public-read', 'public-write', 'locked', 'owner-only');--> statement-breakpoint
2
+ CREATE TABLE "app_documents" (
3
+ "id" text PRIMARY KEY NOT NULL,
4
+ "app_id" text NOT NULL,
5
+ "collection" text NOT NULL,
6
+ "owner_end_user_id" text,
7
+ "doc" jsonb NOT NULL,
8
+ "created_at" timestamp DEFAULT now() NOT NULL,
9
+ "updated_at" timestamp DEFAULT now() NOT NULL,
10
+ "deleted_at" timestamp
11
+ );
12
+ --> statement-breakpoint
13
+ CREATE TABLE "collections" (
14
+ "id" text PRIMARY KEY NOT NULL,
15
+ "app_id" text NOT NULL,
16
+ "name" text NOT NULL,
17
+ "json_schema" jsonb NOT NULL,
18
+ "access_mode" "collection_access_mode" DEFAULT 'locked' NOT NULL,
19
+ "created_at" timestamp DEFAULT now() NOT NULL,
20
+ "updated_at" timestamp DEFAULT now() NOT NULL
21
+ );
22
+ --> statement-breakpoint
23
+ ALTER TABLE "app_documents" ADD CONSTRAINT "app_documents_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
24
+ ALTER TABLE "collections" ADD CONSTRAINT "collections_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
25
+ CREATE INDEX "app_documents_app_collection_idx" ON "app_documents" USING btree ("app_id","collection");--> statement-breakpoint
26
+ CREATE UNIQUE INDEX "collections_app_name_uq" ON "collections" USING btree ("app_id","name");
@@ -0,0 +1,27 @@
1
+ CREATE TYPE "public"."app_error_type" AS ENUM('error', 'unhandledrejection');--> statement-breakpoint
2
+ CREATE TABLE "app_daily_stats" (
3
+ "app_id" text NOT NULL,
4
+ "day" text NOT NULL,
5
+ "path_404_counts" jsonb DEFAULT '{}'::jsonb NOT NULL,
6
+ "count_5xx" integer DEFAULT 0 NOT NULL,
7
+ "request_count" integer DEFAULT 0 NOT NULL,
8
+ "updated_at" timestamp DEFAULT now() NOT NULL,
9
+ CONSTRAINT "app_daily_stats_app_id_day_pk" PRIMARY KEY("app_id","day")
10
+ );
11
+ --> statement-breakpoint
12
+ CREATE TABLE "app_errors" (
13
+ "id" text PRIMARY KEY NOT NULL,
14
+ "app_id" text NOT NULL,
15
+ "type" "app_error_type" NOT NULL,
16
+ "message" text NOT NULL,
17
+ "stack" text,
18
+ "url" text NOT NULL,
19
+ "ua" text,
20
+ "ts" timestamp,
21
+ "dedup_key" text NOT NULL,
22
+ "created_at" timestamp DEFAULT now() NOT NULL
23
+ );
24
+ --> statement-breakpoint
25
+ ALTER TABLE "app_daily_stats" ADD CONSTRAINT "app_daily_stats_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
26
+ ALTER TABLE "app_errors" ADD CONSTRAINT "app_errors_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
27
+ CREATE INDEX "app_errors_app_created_idx" ON "app_errors" USING btree ("app_id","created_at");
@@ -0,0 +1,4 @@
1
+ CREATE TYPE "public"."audit_actor_kind" AS ENUM('user', 'agent');--> statement-breakpoint
2
+ ALTER TABLE "audit_log" ADD COLUMN "actor_kind" "audit_actor_kind" DEFAULT 'user' NOT NULL;--> statement-breakpoint
3
+ ALTER TABLE "audit_log" ADD COLUMN "subject_type" text;--> statement-breakpoint
4
+ CREATE INDEX "audit_log_workspace_created_idx" ON "audit_log" USING btree ("workspace_id","created_at");
@@ -0,0 +1,29 @@
1
+ CREATE TYPE "public"."upstream_auth_type" AS ENUM('none', 'bearer', 'header');--> statement-breakpoint
2
+ CREATE TABLE "upstream_secrets" (
3
+ "upstream_id" text PRIMARY KEY NOT NULL,
4
+ "ciphertext" text NOT NULL,
5
+ "iv" text NOT NULL,
6
+ "auth_tag" text NOT NULL,
7
+ "wrapped_dek" text NOT NULL,
8
+ "kek_id" text NOT NULL,
9
+ "created_at" timestamp DEFAULT now() NOT NULL
10
+ );
11
+ --> statement-breakpoint
12
+ CREATE TABLE "upstreams" (
13
+ "id" text PRIMARY KEY NOT NULL,
14
+ "workspace_id" text NOT NULL,
15
+ "name" text NOT NULL,
16
+ "base_url" text NOT NULL,
17
+ "allowed_methods" text[] NOT NULL,
18
+ "allowed_path_prefixes" text[] NOT NULL,
19
+ "auth_type" "upstream_auth_type" DEFAULT 'none' NOT NULL,
20
+ "auth_header_name" text,
21
+ "allowed_app_ids" text[] DEFAULT '{}' NOT NULL,
22
+ "created_by" text,
23
+ "created_at" timestamp DEFAULT now() NOT NULL
24
+ );
25
+ --> statement-breakpoint
26
+ ALTER TABLE "upstream_secrets" ADD CONSTRAINT "upstream_secrets_upstream_id_upstreams_id_fk" FOREIGN KEY ("upstream_id") REFERENCES "public"."upstreams"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
27
+ ALTER TABLE "upstreams" ADD CONSTRAINT "upstreams_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
28
+ ALTER TABLE "upstreams" ADD CONSTRAINT "upstreams_created_by_users_id_fk" FOREIGN KEY ("created_by") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
29
+ CREATE UNIQUE INDEX "upstreams_workspace_name_uq" ON "upstreams" USING btree ("workspace_id","name");
@@ -0,0 +1,95 @@
1
+ -- M0-02 (NSO-281): immutable app versions replace the upload/deploy pipeline.
2
+ --
3
+ -- EXPLICIT, DESTRUCTIVE: the deploy pipeline tables are dropped and their
4
+ -- history is NOT migrated (decided in the M0 plan). Apps, collections,
5
+ -- documents, errors, stats, upstreams and the audit log are kept. Blob bytes
6
+ -- move from the disk volume into Postgres (`blobs.bytes`); the old on-disk
7
+ -- BLOB_DIR contents are no longer read by anything. See CHANGELOG.md.
8
+ ALTER TABLE "apps" DROP CONSTRAINT "apps_active_deploy_id_deploys_id_fk";--> statement-breakpoint
9
+ ALTER TABLE "apps" DROP COLUMN "active_deploy_id";--> statement-breakpoint
10
+ ALTER TABLE "apps" DROP COLUMN "routing_mode";--> statement-breakpoint
11
+ ALTER TABLE "apps" DROP COLUMN "uses_end_user_auth";--> statement-breakpoint
12
+ DROP TABLE "blob_refs";--> statement-breakpoint
13
+ DROP TABLE "deploy_files";--> statement-breakpoint
14
+ DROP TABLE "deploys";--> statement-breakpoint
15
+ DROP TABLE "blobs";--> statement-breakpoint
16
+ DROP TYPE "public"."deploy_state";--> statement-breakpoint
17
+ DROP TYPE "public"."routing_mode";--> statement-breakpoint
18
+ DROP INDEX "apps_workspace_slug_uq";--> statement-breakpoint
19
+ -- App slugs become GLOBALLY unique host labels (`<slug>.<APPS_DOMAIN>`):
20
+ -- 3–40 chars of ^[a-z0-9]+(-[a-z0-9]+)*$, no reserved word. Existing slugs
21
+ -- that break the grammar, are reserved, or collide with an OLDER app's slug
22
+ -- in another workspace are renamed to `<base>-<4hex>` (deterministic from the
23
+ -- app id; the oldest app keeps a contested slug).
24
+ DO $$
25
+ DECLARE
26
+ r record;
27
+ base text;
28
+ candidate text;
29
+ attempt int;
30
+ BEGIN
31
+ FOR r IN
32
+ SELECT id, slug FROM (
33
+ SELECT id, slug,
34
+ row_number() OVER (PARTITION BY slug ORDER BY created_at, id) AS rank
35
+ FROM apps
36
+ ) ranked
37
+ WHERE rank > 1
38
+ OR slug !~ '^[a-z0-9]+(-[a-z0-9]+)*$'
39
+ OR char_length(slug) NOT BETWEEN 3 AND 40
40
+ OR slug IN ('www', 'api', 'mcp', 'preview', 'admin', 'mail', 'static', 'app', 'auth', 'oauth')
41
+ ORDER BY id
42
+ LOOP
43
+ base := trim(both '-' from regexp_replace(lower(r.slug), '[^a-z0-9]+', '-', 'g'));
44
+ base := trim(trailing '-' from left(base, 35));
45
+ IF base = '' THEN base := 'app'; END IF;
46
+ attempt := 0;
47
+ LOOP
48
+ candidate := base || '-' || substr(md5(r.id || attempt::text), 1, 4);
49
+ EXIT WHEN NOT EXISTS (SELECT 1 FROM apps WHERE slug = candidate);
50
+ attempt := attempt + 1;
51
+ END LOOP;
52
+ UPDATE apps SET slug = candidate WHERE id = r.id;
53
+ END LOOP;
54
+ END $$;--> statement-breakpoint
55
+ CREATE TYPE "public"."compile_status" AS ENUM('pending', 'ok', 'error');--> statement-breakpoint
56
+ CREATE TYPE "public"."version_file_kind" AS ENUM('source', 'built');--> statement-breakpoint
57
+ CREATE TABLE "blobs" (
58
+ "sha256" text PRIMARY KEY NOT NULL,
59
+ "bytes" "bytea" NOT NULL,
60
+ "size" integer NOT NULL,
61
+ "created_at" timestamp DEFAULT now() NOT NULL
62
+ );
63
+ --> statement-breakpoint
64
+ CREATE TABLE "app_versions" (
65
+ "id" text PRIMARY KEY NOT NULL,
66
+ "app_id" text NOT NULL,
67
+ "number" integer NOT NULL,
68
+ "created_by_user_id" text,
69
+ "actor_kind" "audit_actor_kind" NOT NULL,
70
+ "reasoning" text,
71
+ "compile_status" "compile_status" DEFAULT 'pending' NOT NULL,
72
+ "compile_errors" jsonb,
73
+ "created_at" timestamp DEFAULT now() NOT NULL
74
+ );
75
+ --> statement-breakpoint
76
+ CREATE TABLE "version_files" (
77
+ "version_id" text NOT NULL,
78
+ "path" text NOT NULL,
79
+ "sha256" text NOT NULL,
80
+ "size" integer NOT NULL,
81
+ "kind" "version_file_kind" NOT NULL,
82
+ CONSTRAINT "version_files_version_id_kind_path_pk" PRIMARY KEY("version_id","kind","path")
83
+ );
84
+ --> statement-breakpoint
85
+ ALTER TABLE "apps" ADD COLUMN "published_version_id" text;--> statement-breakpoint
86
+ ALTER TABLE "app_versions" ADD CONSTRAINT "app_versions_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
87
+ ALTER TABLE "app_versions" ADD CONSTRAINT "app_versions_created_by_user_id_users_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
88
+ ALTER TABLE "apps" ADD CONSTRAINT "apps_published_version_id_app_versions_id_fk" FOREIGN KEY ("published_version_id") REFERENCES "public"."app_versions"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
89
+ ALTER TABLE "version_files" ADD CONSTRAINT "version_files_version_id_app_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."app_versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
90
+ ALTER TABLE "version_files" ADD CONSTRAINT "version_files_sha256_blobs_sha256_fk" FOREIGN KEY ("sha256") REFERENCES "public"."blobs"("sha256") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
91
+ CREATE UNIQUE INDEX "app_versions_app_number_uq" ON "app_versions" USING btree ("app_id","number");--> statement-breakpoint
92
+ CREATE UNIQUE INDEX "apps_slug_uq" ON "apps" USING btree ("slug");--> statement-breakpoint
93
+ CREATE INDEX "apps_workspace_idx" ON "apps" USING btree ("workspace_id");--> statement-breakpoint
94
+ CREATE INDEX "version_files_sha256_idx" ON "version_files" USING btree ("sha256");--> statement-breakpoint
95
+ ALTER TABLE "apps" ADD CONSTRAINT "apps_slug_format" CHECK ("apps"."slug" ~ '^[a-z0-9]+(-[a-z0-9]+)*$' AND char_length("apps"."slug") BETWEEN 3 AND 40);
@@ -0,0 +1,42 @@
1
+ -- M0-04 (NSO-282): OAuth tokens become USER-bound; API keys; CIMD/DCR client
2
+ -- bookkeeping.
3
+ --
4
+ -- EXPLICIT, DESTRUCTIVE: every authorization code, access token and refresh
5
+ -- token is deleted — they were bound to one workspace + role and used the old
6
+ -- scope vocabulary (mcp:whoami, apps:read, deploy:write, data:read,
7
+ -- data:write), which nothing accepts any more. Connected agents simply
8
+ -- reconnect (new consent → read / write / publish). Registered clients are
9
+ -- kept; the ones that ever held a grant are stamped as used so they do not
10
+ -- count toward the unused-DCR-client cap. See CHANGELOG.md.
11
+ ALTER TABLE "oauth_clients" ADD COLUMN "source" text DEFAULT 'dcr' NOT NULL;--> statement-breakpoint
12
+ ALTER TABLE "oauth_clients" ADD COLUMN "last_used_at" timestamp;--> statement-breakpoint
13
+ UPDATE "oauth_clients" c SET "last_used_at" = c."created_at"
14
+ WHERE EXISTS (SELECT 1 FROM "oauth_authorization_codes" a WHERE a."client_id" = c."client_id")
15
+ OR EXISTS (SELECT 1 FROM "oauth_access_tokens" t WHERE t."oauth_client_id" = c."id")
16
+ OR EXISTS (SELECT 1 FROM "oauth_refresh_tokens" r WHERE r."oauth_client_id" = c."id");--> statement-breakpoint
17
+ TRUNCATE TABLE "oauth_authorization_codes", "oauth_access_tokens", "oauth_refresh_tokens";--> statement-breakpoint
18
+ CREATE TABLE "api_keys" (
19
+ "id" text PRIMARY KEY NOT NULL,
20
+ "user_id" text NOT NULL,
21
+ "name" text NOT NULL,
22
+ "key_hash" text NOT NULL,
23
+ "scopes" text NOT NULL,
24
+ "last_used_at" timestamp,
25
+ "revoked_at" timestamp,
26
+ "created_at" timestamp DEFAULT now() NOT NULL,
27
+ CONSTRAINT "api_keys_key_hash_unique" UNIQUE("key_hash")
28
+ );
29
+ --> statement-breakpoint
30
+ ALTER TABLE "oauth_access_tokens" DROP CONSTRAINT "oauth_access_tokens_workspace_id_workspaces_id_fk";
31
+ --> statement-breakpoint
32
+ ALTER TABLE "oauth_authorization_codes" DROP CONSTRAINT "oauth_authorization_codes_workspace_id_workspaces_id_fk";
33
+ --> statement-breakpoint
34
+ ALTER TABLE "oauth_refresh_tokens" DROP CONSTRAINT "oauth_refresh_tokens_workspace_id_workspaces_id_fk";
35
+ --> statement-breakpoint
36
+ ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
37
+ ALTER TABLE "oauth_access_tokens" DROP COLUMN "workspace_id";--> statement-breakpoint
38
+ ALTER TABLE "oauth_access_tokens" DROP COLUMN "role";--> statement-breakpoint
39
+ ALTER TABLE "oauth_authorization_codes" DROP COLUMN "workspace_id";--> statement-breakpoint
40
+ ALTER TABLE "oauth_authorization_codes" DROP COLUMN "role";--> statement-breakpoint
41
+ ALTER TABLE "oauth_refresh_tokens" DROP COLUMN "workspace_id";--> statement-breakpoint
42
+ ALTER TABLE "oauth_refresh_tokens" DROP COLUMN "role";
@@ -0,0 +1 @@
1
+ ALTER TABLE "apps" ADD COLUMN "name" text;
@@ -0,0 +1,12 @@
1
+ -- M0-06 (NSO-285): apps origin. Generated by drizzle-kit, then hand-edited:
2
+ -- the `team` visibility cannot be enforced on an app host (app hosts never read
3
+ -- the dashboard session), so existing `team` apps become `password` apps —
4
+ -- fail closed: with no password set nobody passes the gate until the owner sets one.
5
+ ALTER TABLE "apps" ALTER COLUMN "visibility" SET DATA TYPE text;--> statement-breakpoint
6
+ UPDATE "apps" SET "visibility" = 'password' WHERE "visibility" = 'team';--> statement-breakpoint
7
+ ALTER TABLE "apps" ALTER COLUMN "visibility" SET DEFAULT 'public'::text;--> statement-breakpoint
8
+ DROP TYPE "public"."app_visibility";--> statement-breakpoint
9
+ CREATE TYPE "public"."app_visibility" AS ENUM('public', 'password');--> statement-breakpoint
10
+ ALTER TABLE "apps" ALTER COLUMN "visibility" SET DEFAULT 'public'::"public"."app_visibility";--> statement-breakpoint
11
+ ALTER TABLE "apps" ALTER COLUMN "visibility" SET DATA TYPE "public"."app_visibility" USING "visibility"::"public"."app_visibility";--> statement-breakpoint
12
+ ALTER TABLE "apps" ADD COLUMN "frame_ancestors" text;
@@ -0,0 +1,32 @@
1
+ -- M1-01 (NSO-287): platform modules. Generated by drizzle-kit. module_configs =
2
+ -- per-app module configuration (+ one pending change awaiting the owner's
3
+ -- confirmation); module_secrets = envelope-encrypted per-app module secrets
4
+ -- (dashboard-only writes, never returned by any API). audit_actor_kind gains
5
+ -- 'end_user' for module actions of an app's signed-in end users (ADD VALUE is
6
+ -- fine inside the migration transaction on PG 12+: nothing uses it in-txn).
7
+ ALTER TYPE "public"."audit_actor_kind" ADD VALUE 'end_user';--> statement-breakpoint
8
+ CREATE TABLE "module_configs" (
9
+ "app_id" text NOT NULL,
10
+ "module" text NOT NULL,
11
+ "config" jsonb DEFAULT '{}'::jsonb NOT NULL,
12
+ "pending" jsonb,
13
+ "updated_at" timestamp DEFAULT now() NOT NULL,
14
+ CONSTRAINT "module_configs_app_id_module_pk" PRIMARY KEY("app_id","module")
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE TABLE "module_secrets" (
18
+ "app_id" text NOT NULL,
19
+ "module" text NOT NULL,
20
+ "name" text NOT NULL,
21
+ "ciphertext" text NOT NULL,
22
+ "iv" text NOT NULL,
23
+ "auth_tag" text NOT NULL,
24
+ "wrapped_dek" text NOT NULL,
25
+ "kek_id" text NOT NULL,
26
+ "created_at" timestamp DEFAULT now() NOT NULL,
27
+ "updated_at" timestamp DEFAULT now() NOT NULL,
28
+ CONSTRAINT "module_secrets_app_id_module_name_pk" PRIMARY KEY("app_id","module","name")
29
+ );
30
+ --> statement-breakpoint
31
+ ALTER TABLE "module_configs" ADD CONSTRAINT "module_configs_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
32
+ ALTER TABLE "module_secrets" ADD CONSTRAINT "module_secrets_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;
@@ -0,0 +1,9 @@
1
+ -- NSO-300 (M1-03): the pre-module Data API tables `collections`,
2
+ -- `app_documents` and the enum `collection_access_mode` now belong to the
3
+ -- platform module `data` (modules/data, drobek-module-data). Its first
4
+ -- migration (journal __drizzle_migrations_mod_data) imports every collection
5
+ -- into the app's `module_configs` row (access_mode → per-operation rules) and
6
+ -- every live document into `mod_data_documents`, then drops the old tables.
7
+ -- Core only forgets them here, so a server without the data module never
8
+ -- loses the stored records.
9
+ SELECT 1;
@@ -0,0 +1,26 @@
1
+ -- NSO-290 (M1-07): get_logs — the compile history (app_compiles) and the
2
+ -- per-module request counters (module_request_stats). Additive only.
3
+ CREATE TABLE "app_compiles" (
4
+ "id" text PRIMARY KEY NOT NULL,
5
+ "app_id" text NOT NULL,
6
+ "version_number" integer,
7
+ "ok" boolean NOT NULL,
8
+ "errors" jsonb DEFAULT '[]'::jsonb NOT NULL,
9
+ "warning_count" integer DEFAULT 0 NOT NULL,
10
+ "duration_ms" integer DEFAULT 0 NOT NULL,
11
+ "trigger" text NOT NULL,
12
+ "created_at" timestamp DEFAULT now() NOT NULL
13
+ );
14
+ --> statement-breakpoint
15
+ CREATE TABLE "module_request_stats" (
16
+ "app_id" text NOT NULL,
17
+ "module" text NOT NULL,
18
+ "status_class" text NOT NULL,
19
+ "day" text NOT NULL,
20
+ "count" integer DEFAULT 0 NOT NULL,
21
+ CONSTRAINT "module_request_stats_app_id_module_status_class_day_pk" PRIMARY KEY("app_id","module","status_class","day")
22
+ );
23
+ --> statement-breakpoint
24
+ ALTER TABLE "app_compiles" ADD CONSTRAINT "app_compiles_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
25
+ ALTER TABLE "module_request_stats" ADD CONSTRAINT "module_request_stats_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
26
+ CREATE INDEX "app_compiles_app_created_idx" ON "app_compiles" USING btree ("app_id","created_at");
@@ -0,0 +1,8 @@
1
+ -- NSO-288 (M2-01): dashboard app delete. A soft-deleted app keeps its slug for
2
+ -- 30 days; then @drobek/apps (releaseDeletedAppSlugs) renames it to the
3
+ -- tombstone `<slug>~deleted-<id>` so a new app can take the slug. The slug
4
+ -- CHECK admits that tombstone on deleted rows only; the partial index serves
5
+ -- the release sweep. Additive for live rows (their grammar is unchanged).
6
+ ALTER TABLE "apps" DROP CONSTRAINT "apps_slug_format";--> statement-breakpoint
7
+ CREATE INDEX "apps_deleted_at_idx" ON "apps" USING btree ("deleted_at") WHERE "apps"."deleted_at" IS NOT NULL;--> statement-breakpoint
8
+ ALTER TABLE "apps" ADD CONSTRAINT "apps_slug_format" CHECK (("apps"."slug" ~ '^[a-z0-9]+(-[a-z0-9]+)*$' AND char_length("apps"."slug") BETWEEN 3 AND 40) OR ("apps"."deleted_at" IS NOT NULL AND "apps"."slug" ~ '^[a-z0-9]+(-[a-z0-9]+)*~deleted-[a-z0-9]+$'));
@@ -0,0 +1,22 @@
1
+ -- NSO-292 (M3-01): custom domains. Generated by drizzle-kit. Additive only.
2
+ -- Partial unique indexes: one VERIFIED row per hostname instance-wide (an
3
+ -- unverified claim never blocks the domain's real owner) and at most one
4
+ -- primary (redirect target) domain per app.
5
+ CREATE TABLE "domains" (
6
+ "id" text PRIMARY KEY NOT NULL,
7
+ "app_id" text NOT NULL,
8
+ "hostname" text NOT NULL,
9
+ "verification_token" text NOT NULL,
10
+ "verified_at" timestamp,
11
+ "last_check_at" timestamp,
12
+ "last_error" text,
13
+ "cert_state" text DEFAULT 'none' NOT NULL,
14
+ "is_primary" boolean DEFAULT false NOT NULL,
15
+ "created_at" timestamp DEFAULT now() NOT NULL
16
+ );
17
+ --> statement-breakpoint
18
+ ALTER TABLE "domains" ADD CONSTRAINT "domains_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
19
+ CREATE UNIQUE INDEX "domains_app_hostname_uq" ON "domains" USING btree ("app_id","hostname");--> statement-breakpoint
20
+ CREATE UNIQUE INDEX "domains_verified_hostname_uq" ON "domains" USING btree ("hostname") WHERE "domains"."verified_at" IS NOT NULL;--> statement-breakpoint
21
+ CREATE UNIQUE INDEX "domains_primary_uq" ON "domains" USING btree ("app_id") WHERE "domains"."is_primary";--> statement-breakpoint
22
+ CREATE INDEX "domains_hostname_idx" ON "domains" USING btree ("hostname");
@@ -0,0 +1,23 @@
1
+ -- NSO-293 (M4-02): abuse and moderation — the abuse_reports queue (public
2
+ -- report form + publish heuristic flags) and apps.locked_reason (super-admin
3
+ -- takedown). Additive only.
4
+ CREATE TYPE "public"."abuse_report_status" AS ENUM('open', 'resolved');--> statement-breakpoint
5
+ CREATE TABLE "abuse_reports" (
6
+ "id" text PRIMARY KEY NOT NULL,
7
+ "app_id" text,
8
+ "host" text NOT NULL,
9
+ "reason" text NOT NULL,
10
+ "details" text DEFAULT '' NOT NULL,
11
+ "reporter_email" text,
12
+ "ip_hash" text,
13
+ "status" "abuse_report_status" DEFAULT 'open' NOT NULL,
14
+ "created_at" timestamp DEFAULT now() NOT NULL,
15
+ "resolved_at" timestamp,
16
+ "resolved_by" text
17
+ );
18
+ --> statement-breakpoint
19
+ ALTER TABLE "apps" ADD COLUMN "locked_reason" text;--> statement-breakpoint
20
+ ALTER TABLE "abuse_reports" ADD CONSTRAINT "abuse_reports_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
21
+ ALTER TABLE "abuse_reports" ADD CONSTRAINT "abuse_reports_resolved_by_users_id_fk" FOREIGN KEY ("resolved_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
22
+ CREATE INDEX "abuse_reports_status_created_idx" ON "abuse_reports" USING btree ("status","created_at");--> statement-breakpoint
23
+ CREATE INDEX "abuse_reports_app_idx" ON "abuse_reports" USING btree ("app_id");
@@ -0,0 +1,20 @@
1
+ -- NSO-340: the public gallery. Generated by drizzle-kit, then the backfill
2
+ -- was added by hand. Additive only.
3
+ -- apps.published_at = when the production host last started serving a
4
+ -- version (publish sets it, unpublish / takedown clear it); backfilled from
5
+ -- the newest `app.publish` audit row, else the published version's creation.
6
+ -- Millisecond precision (the gallery cursor round-trips it through JS Date).
7
+ -- gallery_listed / gallery_description = the owner's opt-in (dashboard);
8
+ -- gallery_hidden_at = a super-admin hid the entry.
9
+ ALTER TABLE "apps" ADD COLUMN "published_at" timestamp;--> statement-breakpoint
10
+ ALTER TABLE "apps" ADD COLUMN "gallery_listed" boolean DEFAULT false NOT NULL;--> statement-breakpoint
11
+ ALTER TABLE "apps" ADD COLUMN "gallery_description" text;--> statement-breakpoint
12
+ ALTER TABLE "apps" ADD COLUMN "gallery_hidden_at" timestamp;--> statement-breakpoint
13
+ CREATE INDEX "apps_gallery_idx" ON "apps" USING btree ("published_at" DESC NULLS LAST,"slug" DESC NULLS LAST) WHERE "apps"."gallery_listed";--> statement-breakpoint
14
+ UPDATE "apps" AS a SET "published_at" = date_trunc('milliseconds', COALESCE(
15
+ (SELECT max(l."created_at") FROM "audit_log" l
16
+ WHERE l."workspace_id" = a."workspace_id" AND l."subject_type" = 'app'
17
+ AND l."target" = a."slug" AND l."action" = 'app.publish'),
18
+ (SELECT v."created_at" FROM "app_versions" v WHERE v."id" = a."published_version_id"),
19
+ now()
20
+ )) WHERE a."published_version_id" IS NOT NULL;
@@ -0,0 +1,13 @@
1
+ -- NSO-346: opt-in platform modules a super-admin enabled per workspace
2
+ -- (dashboard). Generated by drizzle-kit. Additive only. A default module never
3
+ -- has a row; the limits provider's MODULE_ENABLED_<NAME> overrides a row.
4
+ CREATE TABLE "workspace_modules" (
5
+ "workspace_id" text NOT NULL,
6
+ "module" text NOT NULL,
7
+ "enabled_by" text,
8
+ "enabled_at" timestamp with time zone DEFAULT now() NOT NULL,
9
+ CONSTRAINT "workspace_modules_workspace_id_module_pk" PRIMARY KEY("workspace_id","module")
10
+ );
11
+ --> statement-breakpoint
12
+ ALTER TABLE "workspace_modules" ADD CONSTRAINT "workspace_modules_workspace_id_workspaces_id_fk" FOREIGN KEY ("workspace_id") REFERENCES "public"."workspaces"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
13
+ ALTER TABLE "workspace_modules" ADD CONSTRAINT "workspace_modules_enabled_by_users_id_fk" FOREIGN KEY ("enabled_by") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;
@@ -0,0 +1,18 @@
1
+ -- NSO-358: app assets — binary files (images, video, audio, fonts) an app
2
+ -- serves at /assets/<name>; the bytes live on disk under ASSETS_DIR, the rows
3
+ -- here. Additive only.
4
+ CREATE TABLE "app_assets" (
5
+ "app_id" text NOT NULL,
6
+ "name" text NOT NULL,
7
+ "content_type" text NOT NULL,
8
+ "size" bigint NOT NULL,
9
+ "sha256" text NOT NULL,
10
+ "storage_key" text NOT NULL,
11
+ "created_by_user_id" text,
12
+ "created_at" timestamp DEFAULT now() NOT NULL,
13
+ "updated_at" timestamp DEFAULT now() NOT NULL,
14
+ CONSTRAINT "app_assets_app_id_name_pk" PRIMARY KEY("app_id","name")
15
+ );
16
+ --> statement-breakpoint
17
+ ALTER TABLE "app_assets" ADD CONSTRAINT "app_assets_app_id_apps_id_fk" FOREIGN KEY ("app_id") REFERENCES "public"."apps"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
18
+ ALTER TABLE "app_assets" ADD CONSTRAINT "app_assets_created_by_user_id_users_id_fk" FOREIGN KEY ("created_by_user_id") REFERENCES "public"."users"("id") ON DELETE set null ON UPDATE no action;