From 302a461f68fdfe6368f48acd89788870e73b8653 Mon Sep 17 00:00:00 2001
From: Travis Rich
Date: Wed, 27 May 2026 22:45:23 -0400
Subject: [PATCH 1/6] attempt 2 at betterauth functional
---
docker-compose.local.yml | 4 +-
package.json | 4 +-
pnpm-lock.yaml | 321 +++-
server.ts | 71 +-
src/api/accounts.ts | 1100 ++---------
src/api/ark.ts | 126 +-
src/api/auth.server.ts | 104 +-
src/api/collections.ts | 260 ++-
src/api/files.ts | 16 +-
src/api/kf-auth.ts | 191 --
src/api/kf-summary.ts | 57 +-
src/api/query.ts | 36 +-
src/api/schemas.ts | 12 +-
src/api/uploads.ts | 32 +-
src/api/versions.ts | 38 +-
src/components/BaseLayout.tsx | 4 +-
src/db/migrations/0000_harsh_white_tiger.sql | 295 +++
.../migrations/0000_military_machine_man.sql | 134 --
src/db/migrations/0001_daffy_agent_zero.sql | 35 -
src/db/migrations/0002_harsh_madrox.sql | 29 -
src/db/migrations/0003_cool_leper_queen.sql | 3 -
src/db/migrations/0004_powerful_marvex.sql | 14 -
src/db/migrations/0005_smiling_baron_zemo.sql | 1 -
src/db/migrations/0006_ark_identifiers.sql | 29 -
.../migrations/0007_kf-auth-integration.sql | 47 -
src/db/migrations/0008_sudden_cyclops.sql | 6 -
src/db/migrations/0009_salty_colossus.sql | 1 -
src/db/migrations/0010_keen_meltdown.sql | 5 -
src/db/migrations/0011_messy_molten_man.sql | 1 -
src/db/migrations/meta/0000_snapshot.json | 1666 ++++++++++++++---
src/db/migrations/meta/0001_snapshot.json | 1065 -----------
src/db/migrations/meta/0002_snapshot.json | 1244 ------------
src/db/migrations/meta/0003_snapshot.json | 1298 -------------
src/db/migrations/meta/0004_snapshot.json | 1391 --------------
src/db/migrations/meta/0005_snapshot.json | 1398 --------------
src/db/migrations/meta/0007_snapshot.json | 1582 ----------------
src/db/migrations/meta/0008_snapshot.json | 1519 ---------------
src/db/migrations/meta/0009_snapshot.json | 1525 ---------------
src/db/migrations/meta/0010_snapshot.json | 1497 ---------------
src/db/migrations/meta/0011_snapshot.json | 1497 ---------------
src/db/migrations/meta/_journal.json | 81 +-
src/db/schema.ts | 253 ++-
src/db/seed.ts | 54 +-
src/lib/ark.ts | 6 +-
src/lib/auth-client.ts | 9 +
src/lib/auth-internal.server.ts | 5 +-
src/lib/auth.server.ts | 98 +-
src/lib/auth.ts | 105 ++
src/lib/kf-auth.server.ts | 17 -
src/lib/kf-orgs.server.ts | 6 -
src/lib/kf-profile-cache.server.ts | 9 -
src/lib/mirror-sync.ts | 39 +-
src/lib/oidc.server.ts | 228 ---
src/routes/docs/integration.tsx | 3 +-
src/routes/forgot-password.tsx | 2 +-
src/routes/index.tsx | 4 +-
src/routes/invitations/accept.tsx | 4 +-
src/routes/login.tsx | 4 +-
src/routes/logout.tsx | 5 +-
src/routes/query.tsx | 2 +-
src/routes/reset-password.tsx | 2 +-
src/routes/signup.tsx | 2 +-
tools/seedMirror.ts | 18 +-
63 files changed, 2851 insertions(+), 16763 deletions(-)
delete mode 100644 src/api/kf-auth.ts
create mode 100644 src/db/migrations/0000_harsh_white_tiger.sql
delete mode 100644 src/db/migrations/0000_military_machine_man.sql
delete mode 100644 src/db/migrations/0001_daffy_agent_zero.sql
delete mode 100644 src/db/migrations/0002_harsh_madrox.sql
delete mode 100644 src/db/migrations/0003_cool_leper_queen.sql
delete mode 100644 src/db/migrations/0004_powerful_marvex.sql
delete mode 100644 src/db/migrations/0005_smiling_baron_zemo.sql
delete mode 100644 src/db/migrations/0006_ark_identifiers.sql
delete mode 100644 src/db/migrations/0007_kf-auth-integration.sql
delete mode 100644 src/db/migrations/0008_sudden_cyclops.sql
delete mode 100644 src/db/migrations/0009_salty_colossus.sql
delete mode 100644 src/db/migrations/0010_keen_meltdown.sql
delete mode 100644 src/db/migrations/0011_messy_molten_man.sql
delete mode 100644 src/db/migrations/meta/0001_snapshot.json
delete mode 100644 src/db/migrations/meta/0002_snapshot.json
delete mode 100644 src/db/migrations/meta/0003_snapshot.json
delete mode 100644 src/db/migrations/meta/0004_snapshot.json
delete mode 100644 src/db/migrations/meta/0005_snapshot.json
delete mode 100644 src/db/migrations/meta/0007_snapshot.json
delete mode 100644 src/db/migrations/meta/0008_snapshot.json
delete mode 100644 src/db/migrations/meta/0009_snapshot.json
delete mode 100644 src/db/migrations/meta/0010_snapshot.json
delete mode 100644 src/db/migrations/meta/0011_snapshot.json
create mode 100644 src/lib/auth-client.ts
create mode 100644 src/lib/auth.ts
delete mode 100644 src/lib/kf-auth.server.ts
delete mode 100644 src/lib/kf-orgs.server.ts
delete mode 100644 src/lib/kf-profile-cache.server.ts
delete mode 100644 src/lib/oidc.server.ts
diff --git a/docker-compose.local.yml b/docker-compose.local.yml
index f39c40a..295c77b 100644
--- a/docker-compose.local.yml
+++ b/docker-compose.local.yml
@@ -21,7 +21,7 @@ services:
ports:
- '5433:5432'
volumes:
- - pgdata-dev:/var/lib/postgresql/data
+ - pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -U underlay']
interval: 5s
@@ -49,5 +49,5 @@ services:
condition: service_healthy
volumes:
- pgdata-dev:
+ pgdata:
app_node_modules:
diff --git a/package.json b/package.json
index 62f6a65..65904da 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"type": "module",
"scripts": {
"dev": "./dev.sh",
- "dev:app": "tsx --env-file=.env.local server.ts",
+ "dev:app": "tsx watch --clear-screen=false --env-file=.env.local server.ts",
"build": "vite build --outDir dist/client && vite build --ssr src/entry-server.tsx --outDir dist/server",
"start": "NODE_ENV=production node --import tsx/esm server.ts",
"typecheck": "tsc --noEmit",
@@ -30,6 +30,7 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.750.0",
+ "@better-auth/api-key": "^1.6.11",
"@codemirror/autocomplete": "^6.20.1",
"@codemirror/commands": "^6.10.3",
"@codemirror/lang-sql": "^6.10.0",
@@ -41,6 +42,7 @@
"ajv": "^8.17.0",
"ajv-formats": "^3.0.0",
"bcrypt": "^6.0.0",
+ "better-auth": "^1.6.11",
"better-sqlite3": "^12.9.0",
"codemirror": "^6.0.2",
"drizzle-orm": "^0.45.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cea68fe..22e5084 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,6 +11,9 @@ importers:
'@aws-sdk/client-s3':
specifier: ^3.750.0
version: 3.1045.0
+ '@better-auth/api-key':
+ specifier: ^1.6.11
+ version: 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(better-auth@1.6.11(better-sqlite3@12.9.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0))))
'@codemirror/autocomplete':
specifier: ^6.20.1
version: 6.20.2
@@ -44,6 +47,9 @@ importers:
bcrypt:
specifier: ^6.0.0
version: 6.0.0
+ better-auth:
+ specifier: ^1.6.11
+ version: 1.6.11(better-sqlite3@12.9.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0)))
better-sqlite3:
specifier: ^12.9.0
version: 12.9.0
@@ -52,7 +58,7 @@ importers:
version: 6.0.2
drizzle-orm:
specifier: ^0.45.0
- version: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(postgres@3.4.9)(sql.js@1.14.1)
+ version: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1)
hono:
specifier: ^4
version: 4.12.18
@@ -405,6 +411,92 @@ packages:
resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==}
engines: {node: '>=6.9.0'}
+ '@better-auth/api-key@1.6.11':
+ resolution: {integrity: sha512-717Bmbs1Y2h0KrPIwrutxI/HwdqKlga1a1OHlL5TrRbN35IxHeY3GilqvyI/92n7RxFGa/AQhIajX0OL+FJkvw==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ better-auth: ^1.6.11
+
+ '@better-auth/core@1.6.11':
+ resolution: {integrity: sha512-LrwidLCV8azdMGjvtwp30nj9tIv1BwI3VhtC0UaGSjQkAVWw4bN42I8qwbxRziPeSQoj+zUVkOpxZzAWBDARtQ==}
+ peerDependencies:
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+ '@cloudflare/workers-types': '>=4'
+ '@opentelemetry/api': ^1.9.0
+ better-call: 1.3.5
+ jose: ^6.1.0
+ kysely: ^0.28.5
+ nanostores: ^1.0.1
+ peerDependenciesMeta:
+ '@cloudflare/workers-types':
+ optional: true
+ '@opentelemetry/api':
+ optional: true
+
+ '@better-auth/drizzle-adapter@1.6.11':
+ resolution: {integrity: sha512-4jpkETIGZOHCf7BK4jnu22fdN6jjomH0/HhEzkaWy3+Eppi5PYlHTF/460jrTmA3Xc+Vqwp9t282ymHiEPypGw==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ drizzle-orm: ^0.45.2
+ peerDependenciesMeta:
+ drizzle-orm:
+ optional: true
+
+ '@better-auth/kysely-adapter@1.6.11':
+ resolution: {integrity: sha512-/g8M9RfIjdcZDnbstSUvQiINkvdNlCeZr248zwqx2/PVksQI1MhQofbzUn3RnQnbPKp0EPwpX/dR3oudRFenUg==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ kysely: ^0.28.17
+ peerDependenciesMeta:
+ kysely:
+ optional: true
+
+ '@better-auth/memory-adapter@1.6.11':
+ resolution: {integrity: sha512-hpdfw0BBf8MuzLkIdmbcUZICbY9r/bhLO2RxSnkzT5+/O+0I0u2I8+m0YUP7vNllP/ZCKASHOYgXPLO75Z0f9Q==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+
+ '@better-auth/mongo-adapter@1.6.11':
+ resolution: {integrity: sha512-3Tor8rSv8vSEIMEaV2PFpPEuVhqc1gNoZ6eGvoh3LwExXXuj8madew6ob+H1pH7Aphn3Ar5PQ08AguT8TbwFAA==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ mongodb: ^6.0.0 || ^7.0.0
+ peerDependenciesMeta:
+ mongodb:
+ optional: true
+
+ '@better-auth/prisma-adapter@1.6.11':
+ resolution: {integrity: sha512-Pw+7q7zTp+VSci1V+CYMvuxIbAeVMZLe4lRo46LJoAKMHfjFl5T/ycsyFvWs/DkWC7n9gZZzRDEbHp0I5FiKKw==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0
+ prisma: ^5.0.0 || ^6.0.0 || ^7.0.0
+ peerDependenciesMeta:
+ '@prisma/client':
+ optional: true
+ prisma:
+ optional: true
+
+ '@better-auth/telemetry@1.6.11':
+ resolution: {integrity: sha512-hsjDHc8MZbm6/AHeNdtywrWedXevnBjmdvnHTcZub+rTVjOv+Td0roI8USKuC6uUibmrl//2rJfVCsGbopihNA==}
+ peerDependencies:
+ '@better-auth/core': ^1.6.11
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+
+ '@better-auth/utils@0.4.0':
+ resolution: {integrity: sha512-RpMtLUIQAEWMgdPLNVbIF5ON2mm+CH0U3rCdUCU1VyeAUui4m38DyK7/aXMLZov2YDjG684pS1D0MBllrmgjQA==}
+
+ '@better-fetch/fetch@1.1.21':
+ resolution: {integrity: sha512-/ImESw0sskqlVR94jB+5+Pxjf+xBwDZF/N5+y2/q4EqD7IARUTSpPfIo8uf39SYpCxyOCtbyYpUrZ3F/k0zT4A==}
+
'@codemirror/autocomplete@6.20.2':
resolution: {integrity: sha512-G5FPkgIiLjOgZMjqVjvuKQ1rGPtHogLldJr33eFJdVLtmwY+giGrlv/ewljLz6b9BSQLkjxuwBc6g6omDM+YxQ==}
@@ -918,9 +1010,21 @@ packages:
'@marijn/find-cluster-break@1.0.2':
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
+ '@noble/ciphers@2.2.0':
+ resolution: {integrity: sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA==}
+ engines: {node: '>= 20.19.0'}
+
+ '@noble/hashes@2.2.0':
+ resolution: {integrity: sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==}
+ engines: {node: '>= 20.19.0'}
+
'@nodable/entities@2.1.0':
resolution: {integrity: sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==}
+ '@opentelemetry/semantic-conventions@1.41.1':
+ resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==}
+ engines: {node: '>=14'}
+
'@oxfmt/binding-android-arm-eabi@0.50.0':
resolution: {integrity: sha512-ICXQVKrDvsWUtfx6EiVJxfWrajKTwTfRV8vz2XiMkxZeuCKJLgD4YAj6dE3BWvpqDlkVkie4VSTAtMUWO9LDXg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -1824,6 +1928,76 @@ packages:
resolution: {integrity: sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==}
engines: {node: '>= 18'}
+ better-auth@1.6.11:
+ resolution: {integrity: sha512-Wwt6+q07dwIhsp6XiM7L1qSXVUWBEtNl+eZvwM778CguFqDZFBN9Pt6LtFaHl55t8Z+Zc//5kxcbgDY8/79vFQ==}
+ peerDependencies:
+ '@lynx-js/react': '*'
+ '@prisma/client': ^5.0.0 || ^6.0.0 || ^7.0.0
+ '@sveltejs/kit': ^2.0.0
+ '@tanstack/react-start': ^1.0.0
+ '@tanstack/solid-start': ^1.0.0
+ better-sqlite3: ^12.0.0
+ drizzle-kit: '>=0.31.4'
+ drizzle-orm: ^0.45.2
+ mongodb: ^6.0.0 || ^7.0.0
+ mysql2: ^3.0.0
+ next: ^14.0.0 || ^15.0.0 || ^16.0.0
+ pg: ^8.0.0
+ prisma: ^5.0.0 || ^6.0.0 || ^7.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ solid-js: ^1.0.0
+ svelte: ^4.0.0 || ^5.0.0
+ vitest: ^2.0.0 || ^3.0.0 || ^4.0.0
+ vue: ^3.0.0
+ peerDependenciesMeta:
+ '@lynx-js/react':
+ optional: true
+ '@prisma/client':
+ optional: true
+ '@sveltejs/kit':
+ optional: true
+ '@tanstack/react-start':
+ optional: true
+ '@tanstack/solid-start':
+ optional: true
+ better-sqlite3:
+ optional: true
+ drizzle-kit:
+ optional: true
+ drizzle-orm:
+ optional: true
+ mongodb:
+ optional: true
+ mysql2:
+ optional: true
+ next:
+ optional: true
+ pg:
+ optional: true
+ prisma:
+ optional: true
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ solid-js:
+ optional: true
+ svelte:
+ optional: true
+ vitest:
+ optional: true
+ vue:
+ optional: true
+
+ better-call@1.3.5:
+ resolution: {integrity: sha512-kOFJkBP7utAQLEYrobZm3vkTH8mXq5GNgvjc5/XEST1ilVHaxXUXfeDeFlqoETMtyqS4+3/h4ONX2i++ebZrvA==}
+ peerDependencies:
+ zod: ^4.0.0
+ peerDependenciesMeta:
+ zod:
+ optional: true
+
better-sqlite3@12.9.0:
resolution: {integrity: sha512-wqUv4Gm3toFpHDQmaKD4QhZm3g1DjUBI0yzS4UBl6lElUmXFYdTQmmEDpAFa5o8FiFiymURypEnfVHzILKaxqQ==}
engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x}
@@ -1899,6 +2073,9 @@ packages:
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
engines: {node: '>=4.0.0'}
+ defu@6.1.7:
+ resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==}
+
dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -2144,6 +2321,9 @@ packages:
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
hasBin: true
+ jose@6.2.3:
+ resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==}
+
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
@@ -2160,6 +2340,10 @@ packages:
engines: {node: '>=6'}
hasBin: true
+ kysely@0.28.17:
+ resolution: {integrity: sha512-nbD8lB9EB3wNdMhOCdx5Li8DxnLbvKByylRLcJ1h+4SkrowVeECAyZlyiKMThF7xFdRz0jSQ2MoJr+wXux2y0Q==}
+ engines: {node: '>=20.0.0'}
+
lightningcss-android-arm64@1.32.0:
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
engines: {node: '>= 12.0.0'}
@@ -2289,6 +2473,10 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ nanostores@1.3.0:
+ resolution: {integrity: sha512-XPUa/jz+P1oJvN9VBxw4L9MtdFfaH3DAryqPssqhb2kXjmb9npz0dly6rCsgFWOPr4Yg9mTfM3MDZgZZ+7A3lA==}
+ engines: {node: ^20.0.0 || >=22.0.0}
+
napi-build-utils@2.0.0:
resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==}
@@ -2433,6 +2621,9 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ rou3@0.7.12:
+ resolution: {integrity: sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg==}
+
safe-buffer@5.2.1:
resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
@@ -2451,6 +2642,9 @@ packages:
set-cookie-parser@2.7.2:
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+ set-cookie-parser@3.1.0:
+ resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==}
+
siginfo@2.0.0:
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
@@ -2733,6 +2927,9 @@ packages:
zod@3.25.76:
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
+ zod@4.4.3:
+ resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+
snapshots:
'@aws-crypto/crc32@5.2.0':
@@ -3296,6 +3493,66 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5
+ '@better-auth/api-key@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(better-auth@1.6.11(better-sqlite3@12.9.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0))))':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+ better-auth: 1.6.11(better-sqlite3@12.9.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0)))
+ zod: 4.4.3
+
+ '@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)':
+ dependencies:
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+ '@opentelemetry/semantic-conventions': 1.41.1
+ '@standard-schema/spec': 1.1.0
+ better-call: 1.3.5(zod@3.25.76)
+ jose: 6.2.3
+ kysely: 0.28.17
+ nanostores: 1.3.0
+ zod: 4.4.3
+
+ '@better-auth/drizzle-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+ optionalDependencies:
+ drizzle-orm: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1)
+
+ '@better-auth/kysely-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(kysely@0.28.17)':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+ optionalDependencies:
+ kysely: 0.28.17
+
+ '@better-auth/memory-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+
+ '@better-auth/mongo-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+
+ '@better-auth/prisma-adapter@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+
+ '@better-auth/telemetry@1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)':
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+
+ '@better-auth/utils@0.4.0':
+ dependencies:
+ '@noble/hashes': 2.2.0
+
+ '@better-fetch/fetch@1.1.21': {}
+
'@codemirror/autocomplete@6.20.2':
dependencies:
'@codemirror/language': 6.12.3
@@ -3620,8 +3877,14 @@ snapshots:
'@marijn/find-cluster-break@1.0.2': {}
+ '@noble/ciphers@2.2.0': {}
+
+ '@noble/hashes@2.2.0': {}
+
'@nodable/entities@2.1.0': {}
+ '@opentelemetry/semantic-conventions@1.41.1': {}
+
'@oxfmt/binding-android-arm-eabi@0.50.0':
optional: true
@@ -4430,6 +4693,45 @@ snapshots:
node-addon-api: 8.7.0
node-gyp-build: 4.8.4
+ better-auth@1.6.11(better-sqlite3@12.9.0)(drizzle-kit@0.31.10)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(vitest@4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0))):
+ dependencies:
+ '@better-auth/core': 1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0)
+ '@better-auth/drizzle-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1))
+ '@better-auth/kysely-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(kysely@0.28.17)
+ '@better-auth/memory-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)
+ '@better-auth/mongo-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)
+ '@better-auth/prisma-adapter': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)
+ '@better-auth/telemetry': 1.6.11(@better-auth/core@1.6.11(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)(better-call@1.3.5(zod@4.4.3))(jose@6.2.3)(kysely@0.28.17)(nanostores@1.3.0))(@better-auth/utils@0.4.0)(@better-fetch/fetch@1.1.21)
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+ '@noble/ciphers': 2.2.0
+ '@noble/hashes': 2.2.0
+ better-call: 1.3.5(zod@3.25.76)
+ defu: 6.1.7
+ jose: 6.2.3
+ kysely: 0.28.17
+ nanostores: 1.3.0
+ zod: 4.4.3
+ optionalDependencies:
+ better-sqlite3: 12.9.0
+ drizzle-kit: 0.31.10
+ drizzle-orm: 0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1)
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ vitest: 4.1.6(@types/node@25.6.2)(happy-dom@20.9.0)(vite@6.4.2(@types/node@25.6.2)(jiti@2.7.0)(lightningcss@1.32.0)(tsx@4.21.0)(yaml@2.9.0))
+ transitivePeerDependencies:
+ - '@cloudflare/workers-types'
+ - '@opentelemetry/api'
+
+ better-call@1.3.5(zod@3.25.76):
+ dependencies:
+ '@better-auth/utils': 0.4.0
+ '@better-fetch/fetch': 1.1.21
+ rou3: 0.7.12
+ set-cookie-parser: 3.1.0
+ optionalDependencies:
+ zod: 3.25.76
+
better-sqlite3@12.9.0:
dependencies:
bindings: 1.5.0
@@ -4505,6 +4807,8 @@ snapshots:
deep-extend@0.6.0: {}
+ defu@6.1.7: {}
+
dequal@2.0.3: {}
detect-libc@2.1.2: {}
@@ -4518,10 +4822,11 @@ snapshots:
esbuild: 0.25.12
tsx: 4.21.0
- drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(postgres@3.4.9)(sql.js@1.14.1):
+ drizzle-orm@0.45.2(@types/better-sqlite3@7.6.13)(better-sqlite3@12.9.0)(kysely@0.28.17)(postgres@3.4.9)(sql.js@1.14.1):
optionalDependencies:
'@types/better-sqlite3': 7.6.13
better-sqlite3: 12.9.0
+ kysely: 0.28.17
postgres: 3.4.9
sql.js: 1.14.1
@@ -4712,6 +5017,8 @@ snapshots:
jiti@2.7.0: {}
+ jose@6.2.3: {}
+
js-tokens@4.0.0: {}
jsesc@3.1.0: {}
@@ -4720,6 +5027,8 @@ snapshots:
json5@2.2.3: {}
+ kysely@0.28.17: {}
+
lightningcss-android-arm64@1.32.0:
optional: true
@@ -4822,6 +5131,8 @@ snapshots:
nanoid@3.3.12: {}
+ nanostores@1.3.0: {}
+
napi-build-utils@2.0.0: {}
node-abi@3.92.0:
@@ -5010,6 +5321,8 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.60.3
fsevents: 2.3.3
+ rou3@0.7.12: {}
+
safe-buffer@5.2.1: {}
scheduler@0.27.0: {}
@@ -5020,6 +5333,8 @@ snapshots:
set-cookie-parser@2.7.2: {}
+ set-cookie-parser@3.1.0: {}
+
siginfo@2.0.0: {}
signal-exit@4.1.0: {}
@@ -5255,3 +5570,5 @@ snapshots:
optional: true
zod@3.25.76: {}
+
+ zod@4.4.3: {}
diff --git a/server.ts b/server.ts
index c8ef98b..8f34bf0 100644
--- a/server.ts
+++ b/server.ts
@@ -17,14 +17,13 @@ import { authMiddleware, requireAuth } from '~/api/auth.server'
import * as _collections from '~/api/collections'
import * as _files from '~/api/files'
import * as _health from '~/api/health'
-import * as _kfAuth from '~/api/kf-auth'
import * as _kfSummary from '~/api/kf-summary'
import * as _query from '~/api/query'
import * as _schemas from '~/api/schemas'
import * as _uploads from '~/api/uploads'
import * as _versions from '~/api/versions'
+import { auth } from '~/lib/auth'
import { getMirrorConfig } from '~/lib/mirror-config'
-import { initOidc } from '~/lib/oidc.server'
const isProd = process.env.NODE_ENV === 'production'
let vite: ViteDevServer | undefined
@@ -49,7 +48,6 @@ const ark = hot(_ark, '/src/api/ark.ts')
const collections = hot(_collections, '/src/api/collections.ts')
const files = hot(_files, '/src/api/files.ts')
const health = hot(_health, '/src/api/health.ts')
-const kfAuth = hot(_kfAuth, '/src/api/kf-auth.ts')
const kfSummary = hot(_kfSummary, '/src/api/kf-summary.ts')
const query = hot(_query, '/src/api/query.ts')
const schemas = hot(_schemas, '/src/api/schemas.ts')
@@ -76,23 +74,38 @@ app.use('/api/admin/*', async (c, next) => {
// --- ARK resolution middleware ---
app.use('/ark\\:*', arkMiddleware)
-// --- KF Auth (OIDC login) ---
+// --- Better-auth handler (OIDC login, sessions, API keys) ---
+app.on(['GET', 'POST'], '/api/auth/*', async (c) => {
+ return auth.handler(c.req.raw)
+})
+
+// /login redirect — fall through to React route only when there's an error to display
app.get('/login', async (c, next) => {
- // Server-side redirect to avoid client-side "Redirecting..." flash.
- // Fall through to the React route only when there's an error to display.
const url = new URL(c.req.url)
if (!url.searchParams.has('error')) {
- const returnTo = url.searchParams.get('return_to') ?? ''
- const target = returnTo
- ? `/auth/login?return_to=${encodeURIComponent(returnTo)}`
- : '/auth/login'
- return c.redirect(target)
+ const signInUrl = new URL('/api/auth/sign-in/oauth2', url.origin)
+ const authRes = await auth.handler(
+ new Request(signInUrl, {
+ method: 'POST',
+ headers: new Headers({
+ 'Content-Type': 'application/json',
+ Cookie: c.req.header('cookie') ?? '',
+ Origin: url.origin,
+ }),
+ body: JSON.stringify({ providerId: 'kf-auth', callbackURL: '/dashboard' }),
+ }),
+ )
+ const body = await authRes.json()
+ if (body.url) {
+ const redirect = new Response(null, { status: 302, headers: { Location: body.url } })
+ for (const [key, value] of authRes.headers.entries()) {
+ if (key.toLowerCase() === 'set-cookie') redirect.headers.append(key, value)
+ }
+ return redirect
+ }
}
await next()
})
-app.get('/auth/login', kfAuth.login)
-app.get('/auth/callback', kfAuth.callback)
-app.post('/auth/logout', kfAuth.logout)
// --- API routes ---
app.get('/api/health', health.check)
@@ -191,32 +204,13 @@ app.get('/api/collections/:owner/:slug/versions/:n/manifest', versions.manifest)
app.post('/api/collections/:owner/:slug/versions', requireAuth('write'), versions.push)
app.get('/api/collections/:owner/:slug/versions/:n/diff', versions.diff)
-// Accounts
+// Accounts (custom routes — org CRUD, members, invitations, API keys handled by /api/auth/*)
app.get('/api/accounts/me', requireAuth(), accounts.getMe)
app.get('/api/accounts/available-kf-orgs', requireAuth(), accounts.availableKfOrgs)
app.get('/api/accounts/:slug', accounts.getBySlug)
app.patch('/api/accounts/me', requireAuth(), accounts.updateMe)
-app.get('/api/accounts/me/sessions', requireAuth(), accounts.listSessions)
-app.delete('/api/accounts/me/sessions/:sessionId', requireAuth(), accounts.deleteSession)
-app.delete('/api/accounts/me', requireAuth(), accounts.deleteMe)
-app.post('/api/accounts/keys', requireAuth(), accounts.createKey)
-app.get('/api/accounts/keys', requireAuth(), accounts.listKeys)
-app.delete('/api/accounts/keys/:id', requireAuth(), accounts.deleteKey)
-app.post('/api/accounts/:slug/keys', requireAuth(), accounts.createOrgKey)
-app.get('/api/accounts/:slug/keys', requireAuth(), accounts.listOrgKeys)
-app.delete('/api/accounts/:slug/keys/:id', requireAuth(), accounts.deleteOrgKey)
-app.post('/api/accounts/orgs', requireAuth(), accounts.createOrg)
-app.get('/api/accounts/:slug/members', requireAuth(), accounts.listMembers)
-app.post('/api/accounts/:slug/members', requireAuth(), accounts.addMember)
-app.patch('/api/accounts/:slug/members/:userId', requireAuth(), accounts.updateMember)
-app.delete('/api/accounts/:slug/members/:userId', requireAuth(), accounts.removeMember)
-app.patch('/api/accounts/:slug', requireAuth(), accounts.updateOrg)
app.post('/api/accounts/:slug/avatar', requireAuth(), accounts.uploadOrgAvatar)
-app.post('/api/accounts/:slug/invitations', requireAuth(), accounts.createInvitation)
-app.get('/api/accounts/:slug/invitations', requireAuth(), accounts.listInvitations)
-app.delete('/api/accounts/:slug/invitations/:id', requireAuth(), accounts.deleteInvitation)
-app.post('/api/accounts/invitations/accept', requireAuth(), accounts.acceptInvitation)
-app.delete('/api/accounts/:slug', requireAuth(), accounts.deleteOrg)
+app.delete('/api/accounts/me', requireAuth(), accounts.deleteMe)
// --- Blog content API (serves rendered markdown) ---
app.get('/api/blog/:slug', (c) => {
@@ -336,12 +330,5 @@ if (isProd) {
const port = Number(process.env.PORT) || 3000
-// Validate OIDC provider is reachable before accepting requests
-await initOidc().catch((err) => {
- console.error('FATAL: OIDC discovery failed — cannot start without a valid OIDC provider.')
- console.error(err.message)
- process.exit(1)
-})
-
console.log(`Server running at http://localhost:${port}`)
serve({ fetch: app.fetch, port })
diff --git a/src/api/accounts.ts b/src/api/accounts.ts
index 589f59a..b388b76 100644
--- a/src/api/accounts.ts
+++ b/src/api/accounts.ts
@@ -1,15 +1,10 @@
-import bcrypt from 'bcrypt'
-import { and, count, eq } from 'drizzle-orm'
+import { and, eq } from 'drizzle-orm'
import type { Context } from 'hono'
-import { getCookie } from 'hono/cookie'
-import { v4 as uuidv4 } from 'uuid'
import { db, schema } from '../db/client.server.js'
-import { sendEmail } from '../lib/email.js'
import { deleteS3Objects, listS3Objects, uploadToS3 } from '../lib/s3.js'
-import { type AuthEnv, clearSessionCookie } from './auth.server.js'
+import type { AuthEnv } from './auth.server.js'
-/** Base URL for public assets (avatars, etc.) */
const ASSETS_BASE_URL = process.env.ASSETS_BASE_URL ?? 'https://assets.underlay.org'
const RESERVED_SLUGS = new Set([
@@ -37,7 +32,6 @@ const RESERVED_SLUGS = new Set([
const SLUG_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
-/** Validate a slug and return an error message or null if valid. */
function validateSlug(slug: string): string | null {
if (!slug || typeof slug !== 'string') return 'Slug is required'
if (slug.length < 2) return 'Slug must be at least 2 characters'
@@ -49,811 +43,148 @@ function validateSlug(slug: string): string | null {
return null
}
-// Get current user
-export async function getMe(c: Context) {
- const [account] = await db
- .select({
- id: schema.accounts.id,
- slug: schema.accounts.slug,
- type: schema.accounts.type,
- displayName: schema.accounts.displayName,
- bio: schema.accounts.bio,
- website: schema.accounts.website,
- location: schema.accounts.location,
- avatarUrl: schema.accounts.avatarUrl,
- notificationPrefs: schema.accounts.notificationPrefs,
- createdAt: schema.accounts.createdAt,
- })
- .from(schema.accounts)
- .where(eq(schema.accounts.id, c.get('accountId')!))
- .limit(1)
-
- if (!account) {
- return c.json({ error: 'Account not found', statusCode: 404 }, 404)
- }
-
- // Fetch org memberships
- const memberships = await db
- .select({
- orgId: schema.orgMemberships.orgId,
- role: schema.orgMemberships.role,
- slug: schema.accounts.slug,
- displayName: schema.accounts.displayName,
- })
- .from(schema.orgMemberships)
- .innerJoin(schema.accounts, eq(schema.orgMemberships.orgId, schema.accounts.id))
- .where(eq(schema.orgMemberships.userId, account.id))
-
- return c.json({ ...account, orgs: memberships })
-}
-
-// Get account by slug (public)
-export async function getBySlug(c: Context) {
- const slug = c.req.param('slug')!
- const [account] = await db
- .select({
- id: schema.accounts.id,
- slug: schema.accounts.slug,
- type: schema.accounts.type,
- displayName: schema.accounts.displayName,
- bio: schema.accounts.bio,
- website: schema.accounts.website,
- location: schema.accounts.location,
- avatarUrl: schema.accounts.avatarUrl,
- arkNaan: schema.accounts.arkNaan,
- kfOrgId: schema.accounts.kfOrgId,
- createdAt: schema.accounts.createdAt,
- })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, slug))
- .limit(1)
-
- if (!account) {
- return c.json({ error: 'Account not found', statusCode: 404 }, 404)
- }
-
- // Include ARK shoulder if minted
- const [shoulderRow] = await db
- .select({ shoulder: schema.arkShoulders.shoulder })
- .from(schema.arkShoulders)
- .where(eq(schema.arkShoulders.accountId, account.id))
- .limit(1)
-
- return c.json({ ...account, arkShoulder: shoulderRow?.shoulder ?? null })
-}
-
-// Update own profile
-export async function updateMe(c: Context) {
- // Name, email, and avatar are managed by KF Auth — only Underlay-specific fields are writable here.
- const { slug, bio, website, location, notificationPrefs } = await c.req.json()
-
- const accountId = c.get('accountId')!
-
- if (slug !== undefined) {
- const slugErr = validateSlug(slug)
- if (slugErr) return c.json({ error: slugErr, statusCode: 422 }, 422)
-
- const [existing] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, slug))
- .limit(1)
-
- if (existing && existing.id !== accountId) {
- return c.json({ error: 'That slug is already taken', statusCode: 409 }, 409)
- }
- }
-
- const updates: Record = {}
- if (slug !== undefined) updates.slug = slug
- if (bio !== undefined) updates.bio = bio
- if (website !== undefined) updates.website = website
- if (location !== undefined) updates.location = location
- if (notificationPrefs !== undefined) updates.notificationPrefs = notificationPrefs
-
- if (Object.keys(updates).length > 0) {
- await db.update(schema.accounts).set(updates).where(eq(schema.accounts.id, accountId))
- }
-
- return c.json({ ok: true, slug: slug ?? undefined })
-}
-
-// Upload avatar
-export async function uploadAvatar(c: Context) {
- const body = await c.req.parseBody()
- const file = Object.values(body).find((v): v is File => v instanceof File)
- if (!file) {
- return c.json({ error: 'No file uploaded', statusCode: 400 }, 400)
- }
-
- const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
- if (!allowedTypes.includes(file.type)) {
- return c.json(
- { error: 'Only JPEG, PNG, GIF, and WebP images are allowed', statusCode: 422 },
- 422,
- )
- }
-
- const buffer = Buffer.from(await file.arrayBuffer())
- if (buffer.length > 5 * 1024 * 1024) {
- return c.json({ error: 'Image must be less than 5MB', statusCode: 422 }, 422)
- }
-
- const ext = file.type.split('/')[1] === 'jpeg' ? 'jpg' : file.type.split('/')[1]
- const accountId = c.get('accountId')!
- const key = `avatars/${accountId}/${Date.now()}.${ext}`
-
- await uploadToS3(key, buffer, file.type)
-
- await db
- .update(schema.accounts)
- .set({ avatarUrl: `${ASSETS_BASE_URL}/${key}` })
- .where(eq(schema.accounts.id, accountId))
-
- return c.json({ ok: true, avatarUrl: `${ASSETS_BASE_URL}/${key}` })
-}
-
-// List sessions
-export async function listSessions(c: Context) {
- const sessions = await db
- .select({
- id: schema.sessions.id,
- userAgent: schema.sessions.userAgent,
- ipAddress: schema.sessions.ipAddress,
- createdAt: schema.sessions.createdAt,
- expiresAt: schema.sessions.expiresAt,
- })
- .from(schema.sessions)
- .where(eq(schema.sessions.userId, c.get('accountId')!))
-
- // Get current session ID to mark it
- const currentSessionId = getCookie(c, 'session')
- return c.json(
- sessions.map((s) => ({
- ...s,
- current: s.id === currentSessionId,
- })),
- )
-}
-
-// Revoke a session
-export async function deleteSession(c: Context) {
- const sessionId = c.req.param('sessionId')!
-
- const [session] = await db
- .select()
- .from(schema.sessions)
- .where(and(eq(schema.sessions.id, sessionId), eq(schema.sessions.userId, c.get('accountId')!)))
- .limit(1)
-
- if (!session) {
- return c.json({ error: 'Session not found', statusCode: 404 }, 404)
- }
-
- await db.delete(schema.sessions).where(eq(schema.sessions.id, sessionId))
- return c.json({ ok: true })
-}
-
-// Delete own account
-export async function deleteMe(c: Context) {
- const { confirmSlug } = await c.req.json()
-
- const [account] = await db
- .select()
- .from(schema.accounts)
- .where(eq(schema.accounts.id, c.get('accountId')!))
- .limit(1)
-
- if (!account) {
- return c.json({ error: 'Account not found', statusCode: 404 }, 404)
- }
-
- if (confirmSlug !== account.slug) {
- return c.json({ error: 'Username confirmation does not match', statusCode: 422 }, 422)
- }
-
- // Check for owned collections
- const [collCount] = await db
- .select({ count: count() })
- .from(schema.collections)
- .where(eq(schema.collections.accountId, account.id))
-
- if (collCount && collCount.count > 0) {
- return c.json(
- {
- error: `You still own ${collCount.count} collection(s). Transfer or delete them before deleting your account.`,
- statusCode: 422,
- },
- 422,
- )
- }
-
- // Clean up S3 avatars
- try {
- const avatarKeys = await listS3Objects(`avatars/${account.id}/`)
- if (avatarKeys.length > 0) {
- await deleteS3Objects(avatarKeys)
- }
- } catch {
- // Non-fatal: avatar cleanup failed
- }
-
- // Cascade will handle sessions, memberships, api keys
- await db.delete(schema.accounts).where(eq(schema.accounts.id, account.id))
- clearSessionCookie(c)
- return c.json({ ok: true })
-}
-
-// Create API key
-export async function createKey(c: Context) {
- const { label, scope, collectionId, expiresIn } = await c.req.json()
-
- const rawKey = `ul_${uuidv4().replace(/-/g, '')}`
- const keyHash = await bcrypt.hash(rawKey, 10)
- const keyPrefix = rawKey.slice(0, 12)
-
- const expiresAt = expiresIn ? new Date(Date.now() + expiresIn * 24 * 60 * 60 * 1000) : null
-
- const [key] = await db
- .insert(schema.apiKeys)
- .values({
- accountId: c.get('accountId')!,
- scope,
- keyHash,
- keyPrefix,
- label,
- collectionId: collectionId ?? null,
- expiresAt,
- })
- .returning()
-
- return c.json(
- {
- id: key!.id,
- key: rawKey, // shown once
- label,
- scope,
- keyPrefix,
- collectionId: collectionId ?? null,
- expiresAt,
- },
- 201,
- )
-}
-
-// List API keys
-export async function listKeys(c: Context) {
- const keys = await db
- .select({
- id: schema.apiKeys.id,
- label: schema.apiKeys.label,
- scope: schema.apiKeys.scope,
- keyPrefix: schema.apiKeys.keyPrefix,
- collectionId: schema.apiKeys.collectionId,
- expiresAt: schema.apiKeys.expiresAt,
- createdAt: schema.apiKeys.createdAt,
- lastUsedAt: schema.apiKeys.lastUsedAt,
- })
- .from(schema.apiKeys)
- .where(eq(schema.apiKeys.accountId, c.get('accountId')!))
- return c.json(keys)
-}
-
-// Delete API key
-export async function deleteKey(c: Context) {
- const id = c.req.param('id')!
- const [key] = await db.select().from(schema.apiKeys).where(eq(schema.apiKeys.id, id)).limit(1)
-
- if (!key || key.accountId !== c.get('accountId')) {
- return c.json({ error: 'Key not found', statusCode: 404 }, 404)
- }
-
- await db.delete(schema.apiKeys).where(eq(schema.apiKeys.id, id))
- return c.json({ ok: true })
-}
-
-// --- Org-scoped API Keys ---
-
-// Create API key for an org
-export async function createOrgKey(c: Context) {
- const slug = c.req.param('slug')!
- const { label, scope, collectionId, expiresIn } = await c.req.json()
+// --- helpers ---
+async function findOrgBySlug(slug: string) {
const [org] = await db
.select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- // Must be owner or admin
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, slug))
.limit(1)
-
- if (!membership || membership.role === 'member') {
- return c.json(
- { error: 'Must be an owner or admin to manage org API keys', statusCode: 403 },
- 403,
- )
- }
-
- const rawKey = `ul_${uuidv4().replace(/-/g, '')}`
- const keyHash = await bcrypt.hash(rawKey, 10)
- const keyPrefix = rawKey.slice(0, 12)
-
- const expiresAt = expiresIn ? new Date(Date.now() + expiresIn * 24 * 60 * 60 * 1000) : null
-
- const [key] = await db
- .insert(schema.apiKeys)
- .values({
- accountId: org.id,
- scope,
- keyHash,
- keyPrefix,
- label,
- collectionId: collectionId ?? null,
- expiresAt,
- })
- .returning()
-
- return c.json(
- {
- id: key!.id,
- key: rawKey,
- label,
- scope,
- keyPrefix,
- collectionId: collectionId ?? null,
- expiresAt,
- },
- 201,
- )
+ return org ?? null
}
-// List org API keys
-export async function listOrgKeys(c: Context) {
- const slug = c.req.param('slug')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- // Must be a member
+async function requireOrgMembership(
+ organizationId: string,
+ userId: string,
+ minRole?: 'owner' | 'admin',
+) {
const [membership] = await db
.select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, organizationId), eq(schema.member.userId, userId)))
.limit(1)
-
- if (!membership) return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
-
- const keys = await db
- .select({
- id: schema.apiKeys.id,
- label: schema.apiKeys.label,
- scope: schema.apiKeys.scope,
- keyPrefix: schema.apiKeys.keyPrefix,
- collectionId: schema.apiKeys.collectionId,
- expiresAt: schema.apiKeys.expiresAt,
- createdAt: schema.apiKeys.createdAt,
- lastUsedAt: schema.apiKeys.lastUsedAt,
- })
- .from(schema.apiKeys)
- .where(eq(schema.apiKeys.accountId, org.id))
-
- return c.json(keys)
-}
-
-// Delete org API key
-export async function deleteOrgKey(c: Context) {
- const slug = c.req.param('slug')!
- const id = c.req.param('id')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!membership || membership.role === 'member') {
- return c.json(
- { error: 'Must be an owner or admin to manage org API keys', statusCode: 403 },
- 403,
- )
- }
-
- const [key] = await db
- .select()
- .from(schema.apiKeys)
- .where(and(eq(schema.apiKeys.id, id), eq(schema.apiKeys.accountId, org.id)))
- .limit(1)
-
- if (!key) return c.json({ error: 'Key not found', statusCode: 404 }, 404)
-
- await db.delete(schema.apiKeys).where(eq(schema.apiKeys.id, id))
- return c.json({ ok: true })
+ if (!membership) return null
+ if (minRole === 'owner' && membership.role !== 'owner') return null
+ if (minRole === 'admin' && membership.role === 'member') return null
+ return membership
}
-// --- Org Management ---
-
-// Create organization
-export async function createOrg(c: Context) {
- const { slug, displayName, kfOrgId } = await c.req.json()
-
- if (!kfOrgId || typeof kfOrgId !== 'string') {
- return c.json(
- {
- error: 'kfOrgId is required — every Underlay org must be linked to a KF org',
- statusCode: 422,
- },
- 422,
- )
- }
-
- if (RESERVED_SLUGS.has(slug.toLowerCase())) {
- return c.json({ error: 'That name is reserved', statusCode: 422 }, 422)
- }
-
- if (!/^[a-z0-9][a-z0-9\-]*[a-z0-9]$/.test(slug) || slug.length < 2) {
- return c.json(
- {
- error: 'Slug must be lowercase alphanumeric with hyphens, at least 2 characters',
- statusCode: 422,
- },
- 422,
- )
- }
-
- const existing = await db
- .select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, slug))
- .limit(1)
-
- if (existing.length > 0) {
- return c.json({ error: 'Name already taken', statusCode: 409 }, 409)
- }
-
- const id = uuidv4()
- await db.insert(schema.accounts).values({
- id,
- slug,
- type: 'org',
- displayName,
- kfOrgId,
- })
-
- // Add the creating user as owner
- await db.insert(schema.orgMemberships).values({
- orgId: id,
- userId: c.get('accountId')!,
- role: 'owner',
- })
-
- return c.json({ id, slug, displayName, type: 'org', kfOrgId }, 201)
-}
+// --- Current user ---
-/**
- * GET /api/accounts/available-kf-orgs
- * Returns all KF orgs the current user belongs to.
- */
-export async function availableKfOrgs(c: Context) {
- const accountId = c.get('accountId')!
-
- // Fetch user's KF orgs on demand from KF Auth internal API
- const { fetchKfOrgs } = await import('../lib/kf-orgs.server.js')
- return c.json(await fetchKfOrgs(accountId))
-}
-
-// List org members
-export async function listMembers(c: Context) {
- const slug = c.req.param('slug')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
+export async function getMe(c: Context) {
+ const userId = c.get('userId')!
- // Must be a member to view
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
+ const [u] = await db.select().from(schema.user).where(eq(schema.user.id, userId)).limit(1)
- if (!membership) return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
+ if (!u) return c.json({ error: 'User not found', statusCode: 404 }, 404)
- const members = await db
+ const memberships = await db
.select({
- userId: schema.orgMemberships.userId,
- role: schema.orgMemberships.role,
- slug: schema.accounts.slug,
- displayName: schema.accounts.displayName,
+ organizationId: schema.member.organizationId,
+ role: schema.member.role,
+ slug: schema.organization.slug,
+ name: schema.organization.name,
+ isDefault: schema.organization.isDefault,
})
- .from(schema.orgMemberships)
- .innerJoin(schema.accounts, eq(schema.orgMemberships.userId, schema.accounts.id))
- .where(eq(schema.orgMemberships.orgId, org.id))
-
- return c.json(members)
-}
-
-// Add org member
-export async function addMember(c: Context) {
- const slug = c.req.param('slug')!
- const { username, role } = await c.req.json()
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- // Must be owner or admin
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!callerMembership || callerMembership.role === 'member') {
- return c.json({ error: 'Must be an owner or admin to add members', statusCode: 403 }, 403)
- }
-
- // Find user to add
- const [user] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, username), eq(schema.accounts.type, 'user')))
- .limit(1)
-
- if (!user) return c.json({ error: 'User not found', statusCode: 404 }, 404)
-
- // Check not already a member
- const [existing] = await db
- .select()
- .from(schema.orgMemberships)
- .where(and(eq(schema.orgMemberships.orgId, org.id), eq(schema.orgMemberships.userId, user.id)))
- .limit(1)
-
- if (existing) return c.json({ error: 'Already a member', statusCode: 409 }, 409)
-
- await db.insert(schema.orgMemberships).values({
- orgId: org.id,
- userId: user.id,
- role: role ?? 'member',
+ .from(schema.member)
+ .innerJoin(schema.organization, eq(schema.member.organizationId, schema.organization.id))
+ .where(eq(schema.member.userId, u.id))
+
+ const defaultOrg = memberships.find((m) => m.isDefault)
+
+ return c.json({
+ id: u.id,
+ name: u.name,
+ email: u.email,
+ image: u.image,
+ slug: defaultOrg?.slug ?? null,
+ displayName: defaultOrg?.name ?? u.name,
+ createdAt: u.createdAt,
+ orgs: memberships,
})
-
- return c.json({ ok: true, username, role }, 201)
}
-// Update member role
-export async function updateMember(c: Context) {
+// Get org by slug (public)
+export async function getBySlug(c: Context) {
const slug = c.req.param('slug')!
- const userId = c.req.param('userId')!
- const { role } = await c.req.json()
+ const org = await findOrgBySlug(slug)
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
+ if (!org) return c.json({ error: 'Not found', statusCode: 404 }, 404)
- // Must be owner
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ const [shoulderRow] = await db
+ .select({ shoulder: schema.arkShoulders.shoulder })
+ .from(schema.arkShoulders)
+ .where(eq(schema.arkShoulders.organizationId, org.id))
.limit(1)
- if (!callerMembership || callerMembership.role !== 'owner') {
- return c.json({ error: 'Must be an owner to change roles', statusCode: 403 }, 403)
- }
-
- await db
- .update(schema.orgMemberships)
- .set({ role })
- .where(and(eq(schema.orgMemberships.orgId, org.id), eq(schema.orgMemberships.userId, userId)))
-
- return c.json({ ok: true })
+ return c.json({ ...org, arkShoulder: shoulderRow?.shoulder ?? null })
}
-// Remove member
-export async function removeMember(c: Context) {
- const slug = c.req.param('slug')!
- const userId = c.req.param('userId')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
+// Update own profile (updates the user's default org)
+export async function updateMe(c: Context) {
+ const { slug, displayName, bio, website } = await c.req.json()
+ const userId = c.get('userId')!
- // Must be owner or admin (or removing yourself)
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ const [defaultMembership] = await db
+ .select({ organizationId: schema.member.organizationId })
+ .from(schema.member)
+ .innerJoin(schema.organization, eq(schema.member.organizationId, schema.organization.id))
+ .where(and(eq(schema.member.userId, userId), eq(schema.organization.isDefault, true)))
.limit(1)
- const isSelf = c.get('accountId') === userId
- if (!callerMembership || (callerMembership.role === 'member' && !isSelf)) {
- return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
+ if (!defaultMembership) {
+ return c.json({ error: 'Default org not found', statusCode: 404 }, 404)
}
- await db
- .delete(schema.orgMemberships)
- .where(and(eq(schema.orgMemberships.orgId, org.id), eq(schema.orgMemberships.userId, userId)))
-
- return c.json({ ok: true })
-}
-
-// Update org profile
-export async function updateOrg(c: Context) {
- const slug = c.req.param('slug')!
- const { slug: newSlug, displayName, bio, website, location, kfOrgId } = await c.req.json()
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- // Must be owner
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
+ const organizationId = defaultMembership.organizationId
- if (!callerMembership || callerMembership.role !== 'owner') {
- return c.json({ error: 'Must be an owner to update the organization', statusCode: 403 }, 403)
- }
-
- // Validate slug change if provided
- if (newSlug !== undefined) {
- const slugErr = validateSlug(newSlug)
+ if (slug !== undefined) {
+ const slugErr = validateSlug(slug)
if (slugErr) return c.json({ error: slugErr, statusCode: 422 }, 422)
const [existing] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, newSlug))
+ .select({ id: schema.organization.id })
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, slug))
.limit(1)
- if (existing && existing.id !== org.id) {
+ if (existing && existing.id !== organizationId) {
return c.json({ error: 'That slug is already taken', statusCode: 409 }, 409)
}
}
- // Validate kfOrgId change if provided
- if (kfOrgId !== undefined) {
- if (!kfOrgId || typeof kfOrgId !== 'string') {
- return c.json({ error: 'kfOrgId must be a non-empty string', statusCode: 422 }, 422)
- }
- // Check it's not already linked to another UL org
- const [alreadyLinked] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(and(eq(schema.accounts.kfOrgId, kfOrgId), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (alreadyLinked && alreadyLinked.id !== org.id) {
- return c.json(
- {
- error: 'This KF organization is already linked to another Underlay org',
- statusCode: 409,
- },
- 409,
- )
- }
- }
-
const updates: Record = {}
- if (newSlug !== undefined) updates.slug = newSlug
- if (displayName !== undefined) updates.displayName = displayName
+ if (slug !== undefined) updates.slug = slug
+ if (displayName !== undefined) updates.name = displayName
if (bio !== undefined) updates.bio = bio
if (website !== undefined) updates.website = website
- if (location !== undefined) updates.location = location
- if (kfOrgId !== undefined) updates.kfOrgId = kfOrgId
if (Object.keys(updates).length > 0) {
- await db.update(schema.accounts).set(updates).where(eq(schema.accounts.id, org.id))
+ await db
+ .update(schema.organization)
+ .set(updates)
+ .where(eq(schema.organization.id, organizationId))
}
- return c.json({ ok: true, slug: newSlug ?? slug })
+ return c.json({ ok: true, slug: slug ?? undefined })
+}
+
+export async function availableKfOrgs(c: Context) {
+ const userId = c.get('userId')!
+ const { fetchAuthOrgs } = await import('../lib/auth-internal.server.js')
+ return c.json(await fetchAuthOrgs(userId))
}
// Upload org avatar
export async function uploadOrgAvatar(c: Context) {
const slug = c.req.param('slug')!
+ const userId = c.get('userId')!
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
+ const org = await findOrgBySlug(slug)
if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!membership || membership.role !== 'owner') {
+ if (!(await requireOrgMembership(org.id, userId, 'owner'))) {
return c.json(
{ error: 'Must be an owner to update the organization avatar', statusCode: 403 },
403,
@@ -862,9 +193,7 @@ export async function uploadOrgAvatar(c: Context) {
const body = await c.req.parseBody()
const file = Object.values(body).find((v): v is File => v instanceof File)
- if (!file) {
- return c.json({ error: 'No file uploaded', statusCode: 400 }, 400)
- }
+ if (!file) return c.json({ error: 'No file uploaded', statusCode: 400 }, 400)
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
if (!allowedTypes.includes(file.type)) {
@@ -885,250 +214,39 @@ export async function uploadOrgAvatar(c: Context) {
await uploadToS3(key, buffer, file.type)
await db
- .update(schema.accounts)
+ .update(schema.organization)
.set({ avatarUrl: `${ASSETS_BASE_URL}/${key}` })
- .where(eq(schema.accounts.id, org.id))
+ .where(eq(schema.organization.id, org.id))
return c.json({ ok: true, avatarUrl: `${ASSETS_BASE_URL}/${key}` })
}
-// --- Org Invitations ---
-
-// Invite user to org
-export async function createInvitation(c: Context) {
- const slug = c.req.param('slug')!
- const { email, role } = await c.req.json()
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!callerMembership || callerMembership.role === 'member') {
- return c.json({ error: 'Must be an owner or admin to invite members', statusCode: 403 }, 403)
- }
-
- // Check if there's already a pending invitation for this email
- const [existingInvite] = await db
- .select()
- .from(schema.orgInvitations)
- .where(and(eq(schema.orgInvitations.orgId, org.id), eq(schema.orgInvitations.email, email)))
- .limit(1)
-
- if (existingInvite && !existingInvite.acceptedAt) {
- return c.json(
- { error: 'An invitation is already pending for this email', statusCode: 409 },
- 409,
- )
- }
-
- const token = uuidv4()
- const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 days
-
- await db.insert(schema.orgInvitations).values({
- orgId: org.id,
- email,
- role,
- invitedBy: c.get('accountId')!,
- token,
- expiresAt,
- })
-
- // Send invitation email
- const origin = new URL(c.req.url).origin
- const inviteUrl = `${origin}/invitations/accept?token=${token}`
- await sendEmail({
- to: email,
- subject: `You've been invited to join ${org.displayName} on Underlay`,
- text: `You've been invited to join ${org.displayName} as a ${role}.\n\nAccept: ${inviteUrl}\n\nThis invitation expires in 7 days.`,
- html: `You've been invited to join ${org.displayName} as a ${role}.
Accept invitation
This invitation expires in 7 days.
`,
- })
-
- return c.json({ ok: true }, 201)
-}
-
-// List pending invitations for an org
-export async function listInvitations(c: Context) {
- const slug = c.req.param('slug')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!membership) return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
-
- const invitations = await db
- .select({
- id: schema.orgInvitations.id,
- email: schema.orgInvitations.email,
- role: schema.orgInvitations.role,
- expiresAt: schema.orgInvitations.expiresAt,
- acceptedAt: schema.orgInvitations.acceptedAt,
- createdAt: schema.orgInvitations.createdAt,
- })
- .from(schema.orgInvitations)
- .where(eq(schema.orgInvitations.orgId, org.id))
-
- return c.json(invitations)
-}
-
-// Cancel an invitation
-export async function deleteInvitation(c: Context) {
- const slug = c.req.param('slug')!
- const id = c.req.param('id')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!membership || membership.role === 'member') {
- return c.json(
- { error: 'Must be an owner or admin to cancel invitations', statusCode: 403 },
- 403,
- )
- }
-
- await db.delete(schema.orgInvitations).where(eq(schema.orgInvitations.id, id))
- return c.json({ ok: true })
-}
-
-// Accept an invitation (public, token-based)
-export async function acceptInvitation(c: Context) {
- const { token } = await c.req.json()
+// Delete own account
+export async function deleteMe(c: Context) {
+ const { confirmSlug } = await c.req.json()
+ const userId = c.get('userId')!
- const [invitation] = await db
- .select()
- .from(schema.orgInvitations)
- .where(eq(schema.orgInvitations.token, token))
+ const [defaultOrg] = await db
+ .select({ id: schema.organization.id, slug: schema.organization.slug })
+ .from(schema.organization)
+ .innerJoin(schema.member, eq(schema.organization.id, schema.member.organizationId))
+ .where(and(eq(schema.member.userId, userId), eq(schema.organization.isDefault, true)))
.limit(1)
- if (!invitation) {
- return c.json({ error: 'Invitation not found', statusCode: 404 }, 404)
- }
-
- if (invitation.acceptedAt) {
- return c.json({ error: 'Invitation already accepted', statusCode: 409 }, 409)
- }
-
- if (new Date(invitation.expiresAt) < new Date()) {
- return c.json({ error: 'Invitation has expired', statusCode: 410 }, 410)
- }
-
- // Verify the logged-in user's email matches the invitation.
- // Email is fetched from auth internal API since we don't store it locally.
- const { getAuthUserWithEmail } = await import('../lib/auth-internal.server.js')
- const accountId = c.get('accountId')!
-
- // Fetch email from auth internal API
- let userEmail: string | null = null
- const authUser = await getAuthUserWithEmail(accountId)
- if (authUser) {
- userEmail = authUser.email
- }
+ if (!defaultOrg) return c.json({ error: 'Account not found', statusCode: 404 }, 404)
- if (!userEmail || userEmail !== invitation.email) {
- return c.json(
- { error: 'This invitation was sent to a different email address', statusCode: 403 },
- 403,
- )
+ if (confirmSlug !== defaultOrg.slug) {
+ return c.json({ error: 'Username confirmation does not match', statusCode: 422 }, 422)
}
- // Add to org
- await db.insert(schema.orgMemberships).values({
- orgId: invitation.orgId,
- userId: c.get('accountId')!,
- role: invitation.role as 'owner' | 'admin' | 'member',
- })
-
- // Mark invitation as accepted
- await db
- .update(schema.orgInvitations)
- .set({ acceptedAt: new Date() })
- .where(eq(schema.orgInvitations.id, invitation.id))
-
- // Get org slug for redirect
- const [org] = await db
- .select({ slug: schema.accounts.slug })
- .from(schema.accounts)
- .where(eq(schema.accounts.id, invitation.orgId))
- .limit(1)
-
- return c.json({ ok: true, orgSlug: org?.slug ?? '' })
-}
-
-// Delete org
-export async function deleteOrg(c: Context) {
- const slug = c.req.param('slug')!
-
- const [org] = await db
- .select()
- .from(schema.accounts)
- .where(and(eq(schema.accounts.slug, slug), eq(schema.accounts.type, 'org')))
- .limit(1)
-
- if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404)
-
- // Must be owner
- const [callerMembership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, org.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
-
- if (!callerMembership || callerMembership.role !== 'owner') {
- return c.json({ error: 'Must be an owner to delete the organization', statusCode: 403 }, 403)
+ try {
+ const avatarKeys = await listS3Objects(`avatars/${defaultOrg.id}/`)
+ if (avatarKeys.length > 0) await deleteS3Objects(avatarKeys)
+ } catch {
+ // Non-fatal
}
- // Cascade will handle memberships, collections, etc.
- await db.delete(schema.accounts).where(eq(schema.accounts.id, org.id))
+ await db.delete(schema.organization).where(eq(schema.organization.id, defaultOrg.id))
+ await db.delete(schema.user).where(eq(schema.user.id, userId))
return c.json({ ok: true })
}
diff --git a/src/api/ark.ts b/src/api/ark.ts
index 424d0e7..cabc965 100644
--- a/src/api/ark.ts
+++ b/src/api/ark.ts
@@ -38,9 +38,9 @@ export async function resolve(c: Context) {
const { shoulder, collectionArkId, version, recordType, recordId } = components
- // Lookup shoulder → account
+ // Lookup shoulder → org
const [shoulderRow] = await db
- .select({ accountId: schema.arkShoulders.accountId })
+ .select({ organizationId: schema.arkShoulders.organizationId })
.from(schema.arkShoulders)
.where(eq(schema.arkShoulders.shoulder, shoulder))
.limit(1)
@@ -54,21 +54,21 @@ export async function resolve(c: Context) {
customUrl: schema.arkCollections.customUrl,
collectionSlug: schema.collections.slug,
collectionName: schema.collections.name,
- ownerSlug: schema.accounts.slug,
- ownerName: schema.accounts.displayName,
- ownerNaan: schema.accounts.arkNaan,
- collectionAccountId: schema.collections.accountId,
+ ownerSlug: schema.organization.slug,
+ ownerName: schema.organization.name,
+ ownerNaan: schema.organization.arkNaan,
+ collectionOrgId: schema.collections.organizationId,
})
.from(schema.arkCollections)
.innerJoin(schema.collections, eq(schema.arkCollections.collectionId, schema.collections.id))
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
.where(eq(schema.arkCollections.arkId, collectionArkId))
.limit(1)
if (!collRow || !collRow.enabled) return c.json({ type: 'not_found' }, 404)
// Verify the shoulder belongs to the collection's owner
- if (shoulderRow.accountId !== collRow.collectionAccountId) {
+ if (shoulderRow.organizationId !== collRow.collectionOrgId) {
return c.json({ type: 'not_found' }, 404)
}
@@ -289,17 +289,17 @@ export async function getArk(c: Context) {
const [coll] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
- ownerNaan: schema.accounts.arkNaan,
+ organizationId: schema.collections.organizationId,
+ ownerNaan: schema.organization.arkNaan,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!coll) return c.json({ error: 'Collection not found' }, 404)
// Must be owner/member
- const hasAccess = await checkCollectionAccess(coll.accountId, c.get('accountId')!)
+ const hasAccess = await checkCollectionAccess(coll.organizationId, c.get('userId')!)
if (!hasAccess) return c.json({ error: 'Forbidden' }, 403)
const naan = coll.ownerNaan ?? DEFAULT_NAAN
@@ -312,7 +312,7 @@ export async function getArk(c: Context) {
shoulder: schema.arkShoulders.shoulder,
})
.from(schema.arkCollections)
- .innerJoin(schema.arkShoulders, eq(schema.arkShoulders.accountId, coll.accountId))
+ .innerJoin(schema.arkShoulders, eq(schema.arkShoulders.organizationId, coll.organizationId))
.where(eq(schema.arkCollections.collectionId, coll.id))
.limit(1)
@@ -336,14 +336,14 @@ export async function updateArk(c: Context) {
const { enabled, customUrl } = await c.req.json()
const [coll] = await db
- .select({ id: schema.collections.id, accountId: schema.collections.accountId })
+ .select({ id: schema.collections.id, organizationId: schema.collections.organizationId })
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!coll) return c.json({ error: 'Collection not found' }, 404)
- const hasAccess = await checkCollectionAccess(coll.accountId, c.get('accountId')!)
+ const hasAccess = await checkCollectionAccess(coll.organizationId, c.get('userId')!)
if (!hasAccess) return c.json({ error: 'Forbidden' }, 403)
const [existing] = await db
@@ -354,7 +354,7 @@ export async function updateArk(c: Context) {
if (!existing) {
// Collection predates ARK tables — mint now
- await getOrMintShoulder(coll.accountId)
+ await getOrMintShoulder(coll.organizationId)
const arkId = collectionToArkId(coll.id)
await db.insert(schema.arkCollections).values({
collectionId: coll.id,
@@ -384,14 +384,14 @@ export async function getArkRecordTypes(c: Context) {
const slug = c.req.param('slug')!
const [coll] = await db
- .select({ id: schema.collections.id, accountId: schema.collections.accountId })
+ .select({ id: schema.collections.id, organizationId: schema.collections.organizationId })
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!coll) return c.json({ error: 'Collection not found' }, 404)
- const hasAccess = await checkCollectionAccess(coll.accountId, c.get('accountId')!)
+ const hasAccess = await checkCollectionAccess(coll.organizationId, c.get('userId')!)
if (!hasAccess) return c.json({ error: 'Forbidden' }, 403)
const rows = await db
@@ -413,14 +413,14 @@ export async function updateArkRecordTypes(c: Context) {
if (!recordType) return c.json({ error: 'recordType required' }, 400)
const [coll] = await db
- .select({ id: schema.collections.id, accountId: schema.collections.accountId })
+ .select({ id: schema.collections.id, organizationId: schema.collections.organizationId })
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!coll) return c.json({ error: 'Collection not found' }, 404)
- const hasAccess = await checkCollectionAccess(coll.accountId, c.get('accountId')!)
+ const hasAccess = await checkCollectionAccess(coll.organizationId, c.get('userId')!)
if (!hasAccess) return c.json({ error: 'Forbidden' }, 403)
if (redirectUrlField === null) {
@@ -455,61 +455,39 @@ export async function updateAccountArk(c: Context) {
return c.json({ error: 'NAAN must be numeric (up to 16 digits)' }, 400)
}
- const [account] = await db
- .select({ id: schema.accounts.id, type: schema.accounts.type })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, slug))
+ const [org] = await db
+ .select({ id: schema.organization.id })
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, slug))
.limit(1)
- if (!account) return c.json({ error: 'Account not found' }, 404)
-
- // Must be owner/admin of the org (or the user themselves)
- if (account.type === 'org') {
- const [membership] = await db
- .select({ role: schema.orgMemberships.role })
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, account.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
- if (!membership || (membership.role !== 'owner' && membership.role !== 'admin')) {
- return c.json({ error: 'Forbidden' }, 403)
- }
- } else if (account.id !== c.get('accountId')) {
+ if (!org) return c.json({ error: 'Org not found' }, 404)
+
+ // Must be owner/admin of the org
+ const [membership] = await db
+ .select({ role: schema.member.role })
+ .from(schema.member)
+ .where(
+ and(eq(schema.member.organizationId, org.id), eq(schema.member.userId, c.get('userId')!)),
+ )
+ .limit(1)
+ if (!membership || (membership.role !== 'owner' && membership.role !== 'admin')) {
return c.json({ error: 'Forbidden' }, 403)
}
- await db.update(schema.accounts).set({ arkNaan: naan }).where(eq(schema.accounts.id, account.id))
+ await db
+ .update(schema.organization)
+ .set({ arkNaan: naan })
+ .where(eq(schema.organization.id, org.id))
return c.json({ ok: true })
}
// --- Helpers ---
-async function checkCollectionAccess(
- ownerAccountId: string,
- requestAccountId: string,
-): Promise {
- const [account] = await db
- .select({ id: schema.accounts.id, type: schema.accounts.type })
- .from(schema.accounts)
- .where(eq(schema.accounts.id, ownerAccountId))
+async function checkCollectionAccess(orgId: string, userId: string): Promise {
+ const [membership] = await db
+ .select({ role: schema.member.role })
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, orgId), eq(schema.member.userId, userId)))
.limit(1)
- if (!account) return false
- if (account.id === requestAccountId) return true
- if (account.type === 'org') {
- const [membership] = await db
- .select({ role: schema.orgMemberships.role })
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, account.id),
- eq(schema.orgMemberships.userId, requestAccountId),
- ),
- )
- .limit(1)
- return !!membership
- }
- return false
+ return !!membership
}
diff --git a/src/api/auth.server.ts b/src/api/auth.server.ts
index bf25c1c..da3439d 100644
--- a/src/api/auth.server.ts
+++ b/src/api/auth.server.ts
@@ -1,14 +1,11 @@
-import bcrypt from 'bcrypt'
-import { eq } from 'drizzle-orm'
-import type { Context, MiddlewareHandler } from 'hono'
-import { deleteCookie, getCookie, setCookie } from 'hono/cookie'
+import type { MiddlewareHandler } from 'hono'
import { createMiddleware } from 'hono/factory'
-import { db, schema } from '../db/client.server.js'
+import { auth } from '../lib/auth.js'
export type AuthEnv = {
Variables: {
- accountId?: string
+ userId?: string
apiKeyScope?: 'read' | 'write' | 'admin'
apiKeyCollectionId?: string | null
sessionUserId?: string
@@ -19,7 +16,6 @@ const publicPaths = new Set(['/api/health', '/api/query/generate-sql'])
const internalToken = process.env.INTERNAL_API_TOKEN ?? 'internal-dev-token'
const authInternalApiKey = process.env.AUTH_INTERNAL_API_KEY ?? ''
-const sessionSecret = process.env.SESSION_SECRET ?? 'dev-secret-change-me'
export const authMiddleware = createMiddleware(async (c, next) => {
// Internal service calls (legacy header)
@@ -30,69 +26,52 @@ export const authMiddleware = createMiddleware(async (c, next) => {
}
// Auth provider internal API key (used by /api/kf/* endpoints)
- const auth = c.req.header('authorization')
- if (authInternalApiKey && auth === `Bearer ${authInternalApiKey}`) {
+ const authorization = c.req.header('authorization')
+ if (authInternalApiKey && authorization === `Bearer ${authInternalApiKey}`) {
c.set('apiKeyScope', 'admin')
return next()
}
- // API key auth via Bearer token
- if (auth?.startsWith('Bearer ')) {
- const token = auth.slice(7)
- const keys = await db.select().from(schema.apiKeys)
- let matched = false
- for (const key of keys) {
- const match = await bcrypt.compare(token, key.keyHash)
- if (match) {
- c.set('accountId', key.accountId)
- c.set('apiKeyScope', key.scope as 'read' | 'write' | 'admin')
- c.set('apiKeyCollectionId', key.collectionId)
- await db
- .update(schema.apiKeys)
- .set({ lastUsedAt: new Date() })
- .where(eq(schema.apiKeys.id, key.id))
- matched = true
- break
- }
- }
- if (!matched) {
- return c.json({ error: 'Invalid API key', statusCode: 401 }, 401)
- }
- return next()
- }
-
- // Session cookie auth
- const sessionCookie = getCookie(c, 'session')
- if (sessionCookie) {
+ // API key auth via Bearer token (better-auth apiKey plugin)
+ if (authorization?.startsWith('Bearer ')) {
+ const key = authorization.slice(7)
try {
- // Try to parse as signed cookie (value.signature format)
- let sessionId = sessionCookie
- const dotIdx = sessionCookie.lastIndexOf('.')
- if (dotIdx > 0) {
- sessionId = sessionCookie.slice(0, dotIdx)
- }
- if (sessionId) {
- const [session] = await db
- .select()
- .from(schema.sessions)
- .where(eq(schema.sessions.id, sessionId))
- .limit(1)
- if (session && new Date(session.expiresAt) > new Date()) {
- c.set('sessionUserId', session.userId)
- c.set('accountId', session.userId)
+ const result = await auth.api.verifyApiKey({ body: { key } })
+ if (result?.valid && result.key) {
+ c.set('userId', (result.key as any).userId ?? (result.key as any).referenceId)
+ const perms = (result.key.permissions as Record) ?? {}
+ if (perms['collections']?.includes('admin')) {
c.set('apiKeyScope', 'admin')
+ } else if (perms['collections']?.includes('write')) {
+ c.set('apiKeyScope', 'write')
+ } else {
+ c.set('apiKeyScope', 'read')
}
+ return next()
}
} catch {
- // Invalid or expired cookie — ignore silently
+ // Invalid key — fall through
}
+ return c.json({ error: 'Invalid API key', statusCode: 401 }, 401)
+ }
+
+ // Session cookie auth (better-auth managed)
+ try {
+ const session = await auth.api.getSession({ headers: c.req.raw.headers })
+ if (session) {
+ c.set('sessionUserId', session.user.id)
+ c.set('userId', session.user.id)
+ c.set('apiKeyScope', 'admin')
+ }
+ } catch {
+ // Invalid or expired session — ignore
}
// Public GETs are allowed without auth
if (c.req.method === 'GET') return next()
// All writes require auth, except public paths
- if (!c.get('accountId')) {
+ if (!c.get('userId')) {
const path = new URL(c.req.url).pathname
if (publicPaths.has(path)) return next()
return c.json({ error: 'Authentication required', statusCode: 401 }, 401)
@@ -103,7 +82,7 @@ export const authMiddleware = createMiddleware(async (c, next) => {
export function requireAuth(scope?: 'read' | 'write' | 'admin'): MiddlewareHandler {
return async (c, next) => {
- if (!c.get('accountId')) {
+ if (!c.get('userId')) {
return c.json({ error: 'Authentication required', statusCode: 401 }, 401)
}
if (scope === 'admin' && c.get('apiKeyScope') !== 'admin') {
@@ -115,18 +94,3 @@ export function requireAuth(scope?: 'read' | 'write' | 'admin'): MiddlewareHandl
return next()
}
}
-
-// Helper to set signed session cookie
-export function setSessionCookie(c: Context, sessionId: string) {
- setCookie(c, 'session', sessionId, {
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
- sameSite: 'Lax',
- path: '/',
- maxAge: 30 * 24 * 60 * 60, // 30 days
- })
-}
-
-export function clearSessionCookie(c: Context) {
- deleteCookie(c, 'session', { path: '/' })
-}
diff --git a/src/api/collections.ts b/src/api/collections.ts
index 80b6a2e..d6c2c4f 100644
--- a/src/api/collections.ts
+++ b/src/api/collections.ts
@@ -35,13 +35,13 @@ export async function list(c: Context) {
slug: schema.collections.slug,
name: schema.collections.name,
description: schema.collections.description,
- ownerSlug: schema.accounts.slug,
- ownerName: schema.accounts.displayName,
+ ownerSlug: schema.organization.slug,
+ ownerName: schema.organization.name,
createdAt: schema.collections.createdAt,
updatedAt: schema.collections.updatedAt,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
.where(and(...conditions))
.limit(take)
.offset(skip)
@@ -65,42 +65,34 @@ export async function create(c: Context) {
public?: boolean
}>()
- // Resolve owner account
- const [account] = await db
+ // Resolve owner org
+ const [org] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!account) {
- return c.json({ error: 'Account not found', statusCode: 404 }, 404)
+ if (!org) {
+ return c.json({ error: 'Org not found', statusCode: 404 }, 404)
}
- // Check permission: user must own the account or be a member of the org
- if (account.type === 'user' && account.id !== c.get('accountId')) {
+ // Check permission: user must be a member of the org
+ const [membership] = await db
+ .select()
+ .from(schema.member)
+ .where(
+ and(eq(schema.member.organizationId, org.id), eq(schema.member.userId, c.get('userId')!)),
+ )
+ .limit(1)
+ if (!membership) {
return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
}
- if (account.type === 'org') {
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, account.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
- .limit(1)
- if (!membership) {
- return c.json({ error: 'Forbidden', statusCode: 403 }, 403)
- }
- }
// Check for existing collection with same slug under this owner
const [existing] = await db
.select({ id: schema.collections.id })
.from(schema.collections)
- .where(and(eq(schema.collections.accountId, account.id), eq(schema.collections.slug, slug)))
+ .where(and(eq(schema.collections.organizationId, org.id), eq(schema.collections.slug, slug)))
.limit(1)
if (existing) {
@@ -110,7 +102,7 @@ export async function create(c: Context) {
const id = uuidv4()
await db.insert(schema.collections).values({
id,
- accountId: account.id,
+ organizationId: org.id,
slug,
name,
description: description ?? null,
@@ -119,10 +111,10 @@ export async function create(c: Context) {
// Auto-mint ARK for the new collection
try {
- const shoulder = await getOrMintShoulder(account.id)
+ const shoulder = await getOrMintShoulder(org.id)
const arkId = collectionToArkId(id)
await db.insert(schema.arkCollections).values({ collectionId: id, arkId, enabled: true })
- const naan = account.arkNaan ?? DEFAULT_NAAN
+ const naan = org.arkNaan ?? DEFAULT_NAAN
const arkUrl = buildArkUrl(naan, shoulder, arkId)
return c.json({ id, owner, slug, name, ark: arkUrl }, 201)
} catch {
@@ -143,44 +135,39 @@ export async function get(c: Context) {
name: schema.collections.name,
description: schema.collections.description,
public: schema.collections.public,
- ownerSlug: schema.accounts.slug,
- ownerName: schema.accounts.displayName,
- ownerType: schema.accounts.type,
+ ownerSlug: schema.organization.slug,
+ ownerName: schema.organization.name,
createdAt: schema.collections.createdAt,
updatedAt: schema.collections.updatedAt,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!result) {
return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
}
- if (!result.public && c.get('accountId') !== result.id) {
- // Check if user owns or is member of the owning account
- const [account] = await db
- .select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ if (!result.public) {
+ // Check if user is a member of the owning org
+ const [org] = await db
+ .select({ id: schema.organization.id })
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!account) {
+ if (!org) {
return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
}
- let hasAccess = account.id === c.get('accountId')
- if (!hasAccess && account.type === 'org') {
+ const userId = c.get('userId')
+ let hasAccess = false
+ if (userId) {
const [membership] = await db
.select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, account.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, org.id), eq(schema.member.userId, userId)))
.limit(1)
hasAccess = !!membership
}
@@ -230,12 +217,15 @@ export async function get(c: Context) {
arkId: schema.arkCollections.arkId,
enabled: schema.arkCollections.enabled,
shoulder: schema.arkShoulders.shoulder,
- ownerNaan: schema.accounts.arkNaan,
+ ownerNaan: schema.organization.arkNaan,
})
.from(schema.arkCollections)
.innerJoin(schema.collections, eq(schema.arkCollections.collectionId, schema.collections.id))
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .innerJoin(schema.arkShoulders, eq(schema.arkShoulders.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .innerJoin(
+ schema.arkShoulders,
+ eq(schema.arkShoulders.organizationId, schema.organization.id),
+ )
.where(eq(schema.arkCollections.collectionId, result.id))
.limit(1)
if (arkRow?.enabled) {
@@ -264,20 +254,20 @@ export async function update(c: Context) {
public?: boolean
}>()
- const [account] = await db
+ const [org] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!account) {
+ if (!org) {
return c.json({ error: 'Not found', statusCode: 404 }, 404)
}
const [collection] = await db
.select()
.from(schema.collections)
- .where(and(eq(schema.collections.accountId, account.id), eq(schema.collections.slug, slug)))
+ .where(and(eq(schema.collections.organizationId, org.id), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) {
@@ -296,12 +286,12 @@ export async function update(c: Context) {
422,
)
}
- // Check uniqueness within same account
+ // Check uniqueness within same org
const [existing] = await db
.select({ id: schema.collections.id })
.from(schema.collections)
.where(
- and(eq(schema.collections.accountId, account.id), eq(schema.collections.slug, newSlug)),
+ and(eq(schema.collections.organizationId, org.id), eq(schema.collections.slug, newSlug)),
)
.limit(1)
@@ -323,20 +313,20 @@ export async function remove(c: Context) {
const owner = c.req.param('owner')!
const slug = c.req.param('slug')!
- const [account] = await db
+ const [org] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!account) {
+ if (!org) {
return c.json({ error: 'Not found', statusCode: 404 }, 404)
}
const [collection] = await db
.select()
.from(schema.collections)
- .where(and(eq(schema.collections.accountId, account.id), eq(schema.collections.slug, slug)))
+ .where(and(eq(schema.collections.organizationId, org.id), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) {
@@ -347,80 +337,64 @@ export async function remove(c: Context) {
return c.json({ ok: true })
}
-// Transfer collection to another account
+// Transfer collection to another org
export async function transfer(c: Context) {
const owner = c.req.param('owner')!
const slug = c.req.param('slug')!
- const { targetAccountSlug } = await c.req.json()
+ const { targetOrgSlug } = await c.req.json()
- if (!targetAccountSlug || typeof targetAccountSlug !== 'string') {
- return c.json({ error: 'targetAccountSlug is required', statusCode: 422 }, 422)
+ if (!targetOrgSlug || typeof targetOrgSlug !== 'string') {
+ return c.json({ error: 'targetOrgSlug is required', statusCode: 422 }, 422)
}
- const callerId = c.get('accountId')!
+ const callerId = c.get('userId')!
- // Find source account
- const [sourceAccount] = await db
+ // Find source org
+ const [sourceOrg] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!sourceAccount) return c.json({ error: 'Source account not found', statusCode: 404 }, 404)
+ if (!sourceOrg) return c.json({ error: 'Source org not found', statusCode: 404 }, 404)
- // Verify caller has access to source account
- const callerIsSource = sourceAccount.id === callerId
- let callerHasSourceAccess = callerIsSource
- if (!callerIsSource && sourceAccount.type === 'org') {
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, sourceAccount.id),
- eq(schema.orgMemberships.userId, callerId),
- ),
- )
- .limit(1)
- callerHasSourceAccess =
- !!membership && (membership.role === 'owner' || membership.role === 'admin')
- }
- if (!callerHasSourceAccess) {
+ // Verify caller has admin/owner access to source org
+ const [sourceMembership] = await db
+ .select()
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, sourceOrg.id), eq(schema.member.userId, callerId)))
+ .limit(1)
+ if (
+ !sourceMembership ||
+ (sourceMembership.role !== 'owner' && sourceMembership.role !== 'admin')
+ ) {
return c.json(
- { error: 'You must be an owner or admin of the source account', statusCode: 403 },
+ { error: 'You must be an owner or admin of the source org', statusCode: 403 },
403,
)
}
- // Find target account
- const [targetAccount] = await db
+ // Find target org
+ const [targetOrg] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, targetAccountSlug))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, targetOrgSlug))
.limit(1)
- if (!targetAccount) return c.json({ error: 'Target account not found', statusCode: 404 }, 404)
+ if (!targetOrg) return c.json({ error: 'Target org not found', statusCode: 404 }, 404)
- // Verify caller has access to target account
- const callerIsTarget = targetAccount.id === callerId
- let callerHasTargetAccess = callerIsTarget
- if (!callerIsTarget && targetAccount.type === 'org') {
- const [membership] = await db
- .select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, targetAccount.id),
- eq(schema.orgMemberships.userId, callerId),
- ),
- )
- .limit(1)
- callerHasTargetAccess =
- !!membership && (membership.role === 'owner' || membership.role === 'admin')
- }
- if (!callerHasTargetAccess) {
+ // Verify caller has admin/owner access to target org
+ const [targetMembership] = await db
+ .select()
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, targetOrg.id), eq(schema.member.userId, callerId)))
+ .limit(1)
+ if (
+ !targetMembership ||
+ (targetMembership.role !== 'owner' && targetMembership.role !== 'admin')
+ ) {
return c.json(
- { error: 'You must be an owner or admin of the target account', statusCode: 403 },
+ { error: 'You must be an owner or admin of the target org', statusCode: 403 },
403,
)
}
@@ -430,24 +404,24 @@ export async function transfer(c: Context) {
.select()
.from(schema.collections)
.where(
- and(eq(schema.collections.accountId, sourceAccount.id), eq(schema.collections.slug, slug)),
+ and(eq(schema.collections.organizationId, sourceOrg.id), eq(schema.collections.slug, slug)),
)
.limit(1)
if (!collection) return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
- // Check slug uniqueness in target account
+ // Check slug uniqueness in target org
const [existing] = await db
.select({ id: schema.collections.id })
.from(schema.collections)
.where(
- and(eq(schema.collections.accountId, targetAccount.id), eq(schema.collections.slug, slug)),
+ and(eq(schema.collections.organizationId, targetOrg.id), eq(schema.collections.slug, slug)),
)
.limit(1)
if (existing) {
return c.json(
- { error: `Target account already has a collection with slug "${slug}"`, statusCode: 409 },
+ { error: `Target org already has a collection with slug "${slug}"`, statusCode: 409 },
409,
)
}
@@ -455,41 +429,37 @@ export async function transfer(c: Context) {
// Transfer
await db
.update(schema.collections)
- .set({ accountId: targetAccount.id, updatedAt: new Date() })
+ .set({ organizationId: targetOrg.id, updatedAt: new Date() })
.where(eq(schema.collections.id, collection.id))
- return c.json({ ok: true, newOwner: targetAccountSlug })
+ return c.json({ ok: true, newOwner: targetOrgSlug })
}
-// List collections for an account
+// List collections for an org
export async function listByOwner(c: Context) {
const owner = c.req.param('owner')!
- const [account] = await db
+ const [org] = await db
.select()
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, owner))
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, owner))
.limit(1)
- if (!account) return c.json([])
+ if (!org) return c.json([])
- // Check if the requester owns this account or is an org member
- let hasFullAccess = c.get('accountId') === account.id
- if (!hasFullAccess && account.type === 'org' && c.get('accountId')) {
+ // Check if the requester is an org member
+ let hasFullAccess = false
+ const userId = c.get('userId')
+ if (userId) {
const [membership] = await db
.select()
- .from(schema.orgMemberships)
- .where(
- and(
- eq(schema.orgMemberships.orgId, account.id),
- eq(schema.orgMemberships.userId, c.get('accountId')!),
- ),
- )
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, org.id), eq(schema.member.userId, userId)))
.limit(1)
hasFullAccess = !!membership
}
- const conditions = [eq(schema.collections.accountId, account.id)]
+ const conditions = [eq(schema.collections.organizationId, org.id)]
if (!hasFullAccess) {
conditions.push(eq(schema.collections.public, true))
}
@@ -525,18 +495,18 @@ export async function exportArchive(c: Context) {
name: schema.collections.name,
description: schema.collections.description,
public: schema.collections.public,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) {
return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
}
- if (!collection.public && c.get('accountId') !== collection.accountId) {
+ if (!collection.public) {
return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
}
diff --git a/src/api/files.ts b/src/api/files.ts
index ea3aac6..30231c6 100644
--- a/src/api/files.ts
+++ b/src/api/files.ts
@@ -15,23 +15,23 @@ async function isFilePubliclyAccessible(
owner: string,
slug: string,
fileHash: string,
- accountId: string | undefined,
+ userId: string | undefined,
): Promise {
// Resolve collection
const [collection] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) return false
- // Owner always has access
- if (accountId != null && accountId === collection.accountId) {
+ // Org member always has access
+ if (userId != null && userId === collection.organizationId) {
return true
}
@@ -138,7 +138,7 @@ export async function headFile(c: Context) {
}
// Check visibility
- const accessible = await isFilePubliclyAccessible(owner, slug, cleanHash, c.get('accountId'))
+ const accessible = await isFilePubliclyAccessible(owner, slug, cleanHash, c.get('userId'))
if (!accessible) {
return c.body(null, 404)
}
@@ -166,7 +166,7 @@ export async function getFile(c: Context) {
}
// Check visibility
- const accessible = await isFilePubliclyAccessible(owner, slug, cleanHash, c.get('accountId'))
+ const accessible = await isFilePubliclyAccessible(owner, slug, cleanHash, c.get('userId'))
if (!accessible) {
return c.json({ error: 'File not found', statusCode: 404 }, 404)
}
diff --git a/src/api/kf-auth.ts b/src/api/kf-auth.ts
deleted file mode 100644
index 8dd071e..0000000
--- a/src/api/kf-auth.ts
+++ /dev/null
@@ -1,191 +0,0 @@
-import crypto from 'node:crypto'
-
-import { eq } from 'drizzle-orm'
-import type { Context } from 'hono'
-import { deleteCookie, getCookie, setCookie } from 'hono/cookie'
-import { v4 as uuidv4 } from 'uuid'
-
-import { db, schema } from '../db/client.server.js'
-import {
- buildAuthorizeUrl,
- exchangeCode,
- extractOrgs,
- fetchUserInfo,
- type OIDCOrg,
-} from '../lib/oidc.server.js'
-import { type AuthEnv, setSessionCookie } from './auth.server.js'
-
-const STATE_COOKIE = 'kf_oauth_state'
-const RETURN_COOKIE = 'kf_oauth_return'
-const VERIFIER_COOKIE = 'kf_oauth_verifier'
-
-/**
- * GET /auth/login — redirect to KF Auth with CSRF state + PKCE.
- * Optional ?return_to= query param preserved for post-login redirect.
- */
-export async function login(c: Context) {
- const state = crypto.randomBytes(24).toString('hex')
- const returnTo = c.req.query('return_to') ?? '/dashboard'
-
- const cookieOpts = {
- httpOnly: true,
- secure: process.env.NODE_ENV === 'production',
- sameSite: 'Lax' as const,
- path: '/',
- maxAge: 600, // 10 minutes
- }
-
- setCookie(c, STATE_COOKIE, state, cookieOpts)
- setCookie(c, RETURN_COOKIE, returnTo, cookieOpts)
-
- const { url, codeVerifier } = await buildAuthorizeUrl(state)
- setCookie(c, VERIFIER_COOKIE, codeVerifier, cookieOpts)
-
- return c.redirect(url)
-}
-
-/**
- * GET /auth/callback — handle the OIDC callback.
- * Exchanges the code for tokens, fetches user info, upserts the local
- * account, creates a session, and redirects to the return URL.
- */
-export async function callback(c: Context) {
- const code = c.req.query('code')
- const state = c.req.query('state')
- const error = c.req.query('error')
-
- if (error) {
- console.error('KF Auth returned error:', error, c.req.query('error_description'))
- return c.redirect('/login?error=auth_failed')
- }
-
- if (!code || !state) {
- return c.redirect('/login?error=missing_params')
- }
-
- // Validate CSRF state
- const savedState = getCookie(c, STATE_COOKIE)
- const codeVerifier = getCookie(c, VERIFIER_COOKIE)
- deleteCookie(c, STATE_COOKIE, { path: '/' })
- deleteCookie(c, VERIFIER_COOKIE, { path: '/' })
-
- if (!savedState || savedState !== state) {
- return c.redirect('/login?error=invalid_state')
- }
-
- if (!codeVerifier) {
- return c.redirect('/login?error=missing_verifier')
- }
-
- // Exchange code for tokens
- let accessToken: string
- try {
- const tokens = await exchangeCode(code, codeVerifier)
- accessToken = tokens.access_token
- } catch (err) {
- console.error('Token exchange failed:', err)
- return c.redirect('/login?error=token_exchange')
- }
-
- // Fetch user info
- let userInfo: Awaited>
- try {
- userInfo = await fetchUserInfo(accessToken)
- } catch (err) {
- console.error('UserInfo fetch failed:', err)
- return c.redirect('/login?error=userinfo')
- }
-
- // Find or create local account.
- // User account id IS the KF Auth user id (userInfo.sub).
- // No profile data stored locally — fetched from KF Auth on demand.
- const kfUserId = userInfo.sub
- const kfOrgs: OIDCOrg[] = extractOrgs(userInfo)
- const kfPersonalOrg = kfOrgs.find((o) => o.type === 'personal')
- const accountId = kfUserId
-
- const [existing] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(eq(schema.accounts.id, kfUserId))
- .limit(1)
-
- if (existing) {
- // Update personal org link if it changed
- if (kfPersonalOrg) {
- await db
- .update(schema.accounts)
- .set({ kfOrgId: kfPersonalOrg.id })
- .where(eq(schema.accounts.id, accountId))
- }
- } else {
- // Create new account — generate a slug from email or name
- const baseSlug = (userInfo.email?.split('@')[0] ?? userInfo.name ?? 'user')
- .toLowerCase()
- .replace(/[^a-z0-9-]/g, '-')
- .replace(/-+/g, '-')
- .slice(0, 30)
-
- // Ensure slug is unique
- let slug = baseSlug
- let attempt = 0
- while (true) {
- const [conflict] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, slug))
- .limit(1)
-
- if (!conflict) break
- attempt++
- slug = `${baseSlug}-${attempt}`
- }
-
- await db.insert(schema.accounts).values({
- id: kfUserId,
- slug,
- type: 'user',
- kfOrgId: kfPersonalOrg?.id ?? null,
- })
- }
-
- // Create session
- const sessionId = uuidv4()
- const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days
- await db.insert(schema.sessions).values({
- id: sessionId,
- userId: accountId,
- expiresAt,
- userAgent: c.req.header('user-agent') ?? null,
- ipAddress: c.req.header('x-forwarded-for') || 'unknown',
- })
-
- setSessionCookie(c, sessionId)
-
- // Redirect to saved return URL
- const returnTo = getCookie(c, RETURN_COOKIE) ?? '/dashboard'
- deleteCookie(c, RETURN_COOKIE, { path: '/' })
-
- return c.redirect(returnTo)
-}
-
-/**
- * POST /auth/logout — clear local session, return JSON.
- * The client is responsible for redirecting to KF Auth's signout endpoint.
- */
-export async function logout(c: Context) {
- const sessionCookie = getCookie(c, 'session')
- if (sessionCookie) {
- let sessionId = sessionCookie
- const dotIdx = sessionCookie.lastIndexOf('.')
- if (dotIdx > 0) sessionId = sessionCookie.slice(0, dotIdx)
-
- await db
- .delete(schema.sessions)
- .where(eq(schema.sessions.id, sessionId))
- .catch(() => {})
- }
-
- deleteCookie(c, 'session', { path: '/' })
- return c.json({ ok: true })
-}
diff --git a/src/api/kf-summary.ts b/src/api/kf-summary.ts
index ec49290..85ebd04 100644
--- a/src/api/kf-summary.ts
+++ b/src/api/kf-summary.ts
@@ -6,8 +6,7 @@ import { db, schema } from '../db/client.server.js'
/**
* GET /api/kf/summary?kf_org_id=xxx
*
- * Returns Underlay accounts and their collections linked to a KF org.
- * For user-type accounts it also includes UL orgs the user belongs to.
+ * Returns Underlay orgs and their collections linked to a KF org.
*
* Auth: requires AUTH_INTERNAL_API_KEY (service-to-service).
*/
@@ -26,37 +25,36 @@ export async function summary(c: Context) {
const APP_URL = process.env.APP_URL ?? 'http://localhost:4100'
- // Find local accounts linked to this KF org via kf_org_id.
- const directAccounts = await db
+ // Find local orgs linked to this KF org via kf_org_id.
+ const directOrgs = await db
.select({
- id: schema.accounts.id,
- slug: schema.accounts.slug,
- type: schema.accounts.type,
- displayName: schema.accounts.displayName,
+ id: schema.organization.id,
+ slug: schema.organization.slug,
+ displayName: schema.organization.name,
})
- .from(schema.accounts)
- .where(eq(schema.accounts.kfOrgId, kfOrgId))
+ .from(schema.organization)
+ .where(eq(schema.organization.kfOrgId, kfOrgId))
- if (directAccounts.length === 0) {
- return c.json({ accounts: [] })
+ if (directOrgs.length === 0) {
+ return c.json({ orgs: [] })
}
- const allAccountIds = directAccounts.map((a) => a.id)
+ const allOrgIds = directOrgs.map((a) => a.id)
- // Get collections for all accounts
+ // Get collections for all orgs
const collections = await db
.select({
id: schema.collections.id,
slug: schema.collections.slug,
name: schema.collections.name,
- accountId: schema.collections.accountId,
- ownerSlug: schema.accounts.slug,
+ organizationId: schema.collections.organizationId,
+ ownerSlug: schema.organization.slug,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
.where(
- sql`${schema.collections.accountId} IN (${sql.join(
- allAccountIds.map((id) => sql`${id}`),
+ sql`${schema.collections.organizationId} IN (${sql.join(
+ allOrgIds.map((id) => sql`${id}`),
sql`, `,
)})`,
)
@@ -95,22 +93,21 @@ export async function summary(c: Context) {
}
}
- // Group collections by account
- const collectionsByAccount = new Map()
+ // Group collections by org
+ const collectionsByOrg = new Map()
for (const col of collections) {
- const list = collectionsByAccount.get(col.accountId) ?? []
+ const list = collectionsByOrg.get(col.organizationId) ?? []
list.push(col)
- collectionsByAccount.set(col.accountId, list)
+ collectionsByOrg.set(col.organizationId, list)
}
return c.json({
- accounts: directAccounts.map((acct) => ({
- id: acct.id,
- slug: acct.slug,
- type: acct.type,
- name: acct.displayName ?? acct.slug,
- url: `${APP_URL}/${acct.slug}`,
- collections: (collectionsByAccount.get(acct.id) ?? []).map((col) => {
+ orgs: directOrgs.map((org) => ({
+ id: org.id,
+ slug: org.slug,
+ name: org.displayName ?? org.slug,
+ url: `${APP_URL}/${org.slug}`,
+ collections: (collectionsByOrg.get(org.id) ?? []).map((col) => {
const stats = statsMap.get(col.id)
return {
id: col.id,
diff --git a/src/api/query.ts b/src/api/query.ts
index 5ecb407..4bb131e 100644
--- a/src/api/query.ts
+++ b/src/api/query.ts
@@ -43,12 +43,12 @@ async function getOrBuildSqlite(owner: string, slug: string, versionNumber: numb
const [collection] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
public: schema.collections.public,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.accounts.id, schema.collections.accountId))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.organization.id, schema.collections.organizationId))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) return null
@@ -336,21 +336,21 @@ export async function searchCollections(c: Context) {
if (!q || q.trim().length < 2) return c.json([])
const term = `%${q.trim()}%`
- const userId = c.get('accountId')
+ const userId = c.get('userId')
- // Build accessible account IDs (user's own + orgs they belong to)
+ // Build accessible org IDs (user's own + orgs they belong to)
let accessibleAccountIds: string[] = []
if (userId) {
const memberships = await db
- .select({ orgId: schema.orgMemberships.orgId })
- .from(schema.orgMemberships)
- .where(eq(schema.orgMemberships.userId, userId))
- accessibleAccountIds = [userId, ...memberships.map((m) => m.orgId)]
+ .select({ organizationId: schema.member.organizationId })
+ .from(schema.member)
+ .where(eq(schema.member.userId, userId))
+ accessibleAccountIds = [userId, ...memberships.map((m) => m.organizationId)]
}
- // Query: public collections OR private collections owned by accessible accounts
+ // Query: public collections OR private collections owned by accessible orgs
const searchCondition = or(
- ilike(schema.accounts.slug, term),
+ ilike(schema.organization.slug, term),
ilike(schema.collections.slug, term),
ilike(schema.collections.name, term),
)
@@ -361,7 +361,7 @@ export async function searchCollections(c: Context) {
searchCondition,
or(
eq(schema.collections.public, true),
- inArray(schema.collections.accountId, accessibleAccountIds),
+ inArray(schema.collections.organizationId, accessibleAccountIds),
),
)
} else {
@@ -370,14 +370,14 @@ export async function searchCollections(c: Context) {
const collections = await db
.select({
- ownerSlug: schema.accounts.slug,
+ ownerSlug: schema.organization.slug,
slug: schema.collections.slug,
name: schema.collections.name,
description: schema.collections.description,
public: schema.collections.public,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.accounts.id, schema.collections.accountId))
+ .innerJoin(schema.organization, eq(schema.organization.id, schema.collections.organizationId))
.where(whereCondition)
.limit(20)
@@ -392,8 +392,8 @@ export async function searchCollections(c: Context) {
})
.from(schema.versions)
.innerJoin(schema.collections, eq(schema.collections.id, schema.versions.collectionId))
- .innerJoin(schema.accounts, eq(schema.accounts.id, schema.collections.accountId))
- .where(and(eq(schema.accounts.slug, c2.ownerSlug), eq(schema.collections.slug, c2.slug)))
+ .innerJoin(schema.organization, eq(schema.organization.id, schema.collections.organizationId))
+ .where(and(eq(schema.organization.slug, c2.ownerSlug), eq(schema.collections.slug, c2.slug)))
.orderBy(desc(schema.versions.number))
.limit(1)
@@ -427,10 +427,10 @@ export async function collectionVersions(c: Context) {
})
.from(schema.versions)
.innerJoin(schema.collections, eq(schema.collections.id, schema.versions.collectionId))
- .innerJoin(schema.accounts, eq(schema.accounts.id, schema.collections.accountId))
+ .innerJoin(schema.organization, eq(schema.organization.id, schema.collections.organizationId))
.where(
and(
- eq(schema.accounts.slug, owner),
+ eq(schema.organization.slug, owner),
eq(schema.collections.slug, slug),
eq(schema.collections.public, true),
),
diff --git a/src/api/schemas.ts b/src/api/schemas.ts
index 246b4bd..3796484 100644
--- a/src/api/schemas.ts
+++ b/src/api/schemas.ts
@@ -202,13 +202,13 @@ export async function getSchema(c: Context) {
semver: schema.versions.semver,
versionNumber: schema.versions.number,
collectionSlug: schema.collections.slug,
- owner: schema.accounts.slug,
+ owner: schema.organization.slug,
isPublic: schema.collections.public,
})
.from(schema.versionSchemas)
.innerJoin(schema.versions, eq(schema.versionSchemas.versionId, schema.versions.id))
.innerJoin(schema.collections, eq(schema.versions.collectionId, schema.collections.id))
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
.where(and(eq(schema.versionSchemas.schemaId, id), eq(schema.collections.public, true)))
.orderBy(sql`${schema.versions.createdAt} desc`)
.limit(50)
@@ -237,18 +237,18 @@ export async function collectionSchemas(c: Context) {
const [collection] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
public: schema.collections.public,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
if (!collection) return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
// Visibility check
- if (!collection.public && c.get('accountId') !== collection.accountId) {
+ if (!collection.public && c.get('userId') !== collection.organizationId) {
return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
}
diff --git a/src/api/uploads.ts b/src/api/uploads.ts
index 8fef07f..ec560f1 100644
--- a/src/api/uploads.ts
+++ b/src/api/uploads.ts
@@ -28,12 +28,12 @@ async function resolveCollection(owner: string, slug: string) {
const [result] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
slug: schema.collections.slug,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
return result ?? null
}
@@ -54,8 +54,18 @@ export async function startSession(c: Context) {
const collection = await resolveCollection(owner, slug)
if (!collection) return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
- // Verify the caller owns this collection
- if (c.get('accountId') !== collection.accountId) {
+ // Verify the caller is a member of this collection's org
+ const [membership] = await db
+ .select({ organizationId: schema.member.organizationId })
+ .from(schema.member)
+ .where(
+ and(
+ eq(schema.member.organizationId, collection.organizationId),
+ eq(schema.member.userId, c.get('userId')!),
+ ),
+ )
+ .limit(1)
+ if (!membership) {
return c.json({ error: 'Not authorized for this collection', statusCode: 403 }, 403)
}
@@ -85,7 +95,7 @@ export async function startSession(c: Context) {
.insert(schema.uploadSessions)
.values({
collectionId: collection.id,
- accountId: c.get('accountId')!,
+ userId: c.get('userId')!,
baseVersion: body.base_version ?? null,
message: body.message ?? null,
readme: body.readme ?? null,
@@ -130,7 +140,7 @@ export async function appendBatch(c: Context) {
if (!session) {
return c.json({ error: 'Upload session not found', statusCode: 404 }, 404)
}
- if (session.accountId !== c.get('accountId')) {
+ if (session.userId !== c.get('userId')) {
return c.json({ error: 'Not authorized for this session', statusCode: 403 }, 403)
}
if (session.status !== 'open') {
@@ -265,7 +275,7 @@ export async function getSession(c: Context) {
if (!session) {
return c.json({ error: 'Upload session not found', statusCode: 404 }, 404)
}
- if (session.accountId !== c.get('accountId')) {
+ if (session.userId !== c.get('userId')) {
return c.json({ error: 'Not authorized for this session', statusCode: 403 }, 403)
}
@@ -295,7 +305,7 @@ export async function finalize(c: Context) {
if (!session) {
return c.json({ error: 'Upload session not found', statusCode: 404 }, 404)
}
- if (session.accountId !== c.get('accountId')) {
+ if (session.userId !== c.get('userId')) {
return c.json({ error: 'Not authorized for this session', statusCode: 403 }, 403)
}
if (session.status !== 'open') {
@@ -774,7 +784,7 @@ export async function finalize(c: Context) {
baseNumber: session.baseVersion,
message: session.message ?? null,
readme: readmeValue,
- pushedBy: c.get('accountId') ?? null,
+ pushedBy: c.get('userId') ?? null,
appId: session.appId ?? null,
actorId: session.actorId ?? null,
recordCount,
@@ -856,7 +866,7 @@ export async function cancelSession(c: Context) {
if (!session) {
return c.json({ error: 'Upload session not found', statusCode: 404 }, 404)
}
- if (session.accountId !== c.get('accountId')) {
+ if (session.userId !== c.get('userId')) {
return c.json({ error: 'Not authorized for this session', statusCode: 403 }, 403)
}
diff --git a/src/api/versions.ts b/src/api/versions.ts
index 337115d..ae17860 100644
--- a/src/api/versions.ts
+++ b/src/api/versions.ts
@@ -28,9 +28,15 @@ function filterSchemasForPublic(schemaEntries: SchemaEntry[]): Record {
+ if (!userId) return false
+ const [membership] = await db
+ .select()
+ .from(schema.member)
+ .where(and(eq(schema.member.organizationId, orgId), eq(schema.member.userId, userId)))
+ .limit(1)
+ return !!membership
}
function computeVersionHash(
@@ -141,8 +147,7 @@ export async function list(c: Context) {
const collection = await resolveCollection(owner, slug)
if (!collection) return c.json({ error: 'Collection not found', statusCode: 404 }, 404)
- const accountId = c.get('accountId')
- const ownerAccess = isOwner(accountId, collection.accountId)
+ const ownerAccess = await hasOrgAccess(c.get('userId'), collection.organizationId)
const arkInfo = await getCollectionArkInfo(collection.id).catch(() => null)
const rows = await db
@@ -200,8 +205,7 @@ export async function latest(c: Context) {
version.totalBytes = await backfillTotalBytes(version)
const schemaEntries = await loadVersionSchemas(version.id)
- const accountId = c.get('accountId')
- const ownerAccess = isOwner(accountId, collection.accountId)
+ const ownerAccess = await hasOrgAccess(c.get('userId'), collection.organizationId)
const arkInfo = await getCollectionArkInfo(collection.id).catch(() => null)
const schemasMap = ownerAccess
@@ -241,8 +245,7 @@ export async function getByNumber(c: Context) {
version.totalBytes = await backfillTotalBytes(version)
const schemaEntries = await loadVersionSchemas(version.id)
- const accountId = c.get('accountId')
- const ownerAccess = isOwner(accountId, collection.accountId)
+ const ownerAccess = await hasOrgAccess(c.get('userId'), collection.organizationId)
const arkInfo = await getCollectionArkInfo(collection.id).catch(() => null)
const schemasMap = ownerAccess
@@ -294,8 +297,7 @@ export async function records(c: Context) {
}
// Determine visibility
- const accountId = c.get('accountId')
- const ownerAccess = isOwner(accountId, collection.accountId)
+ const ownerAccess = await hasOrgAccess(c.get('userId'), collection.organizationId)
let privateTypes = new Set()
let schemaEntries: SchemaEntry[] = []
@@ -826,7 +828,7 @@ export async function push(c: Context) {
baseNumber: body.base_version,
message: body.message ?? null,
readme: readmeValue,
- pushedBy: c.get('accountId') ?? null,
+ pushedBy: c.get('userId') ?? null,
appId: body.app_id ?? null,
actorId: body.actor_id ?? null,
recordCount: newRecords.length,
@@ -1006,12 +1008,12 @@ async function resolveCollection(owner: string, slug: string) {
const [result] = await db
.select({
id: schema.collections.id,
- accountId: schema.collections.accountId,
+ organizationId: schema.collections.organizationId,
slug: schema.collections.slug,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, owner), eq(schema.collections.slug, slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, owner), eq(schema.collections.slug, slug)))
.limit(1)
return result ?? null
}
@@ -1023,12 +1025,12 @@ async function getCollectionArkInfo(
.select({
shoulder: schema.arkShoulders.shoulder,
arkId: schema.arkCollections.arkId,
- naan: schema.accounts.arkNaan,
+ naan: schema.organization.arkNaan,
})
.from(schema.arkCollections)
.innerJoin(schema.collections, eq(schema.arkCollections.collectionId, schema.collections.id))
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .innerJoin(schema.arkShoulders, eq(schema.arkShoulders.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .innerJoin(schema.arkShoulders, eq(schema.arkShoulders.organizationId, schema.organization.id))
.where(
and(
eq(schema.arkCollections.collectionId, collectionId),
diff --git a/src/components/BaseLayout.tsx b/src/components/BaseLayout.tsx
index 5b09690..72f8157 100644
--- a/src/components/BaseLayout.tsx
+++ b/src/components/BaseLayout.tsx
@@ -47,7 +47,7 @@ export default function BaseLayout({ children }: { children: React.ReactNode })
Admin
) : (
-
+
Log in
)
@@ -58,7 +58,7 @@ export default function BaseLayout({ children }: { children: React.ReactNode })
orgs={currentUser.orgs ?? []}
/>
) : (
-
+
Log in
)}
diff --git a/src/db/migrations/0000_harsh_white_tiger.sql b/src/db/migrations/0000_harsh_white_tiger.sql
new file mode 100644
index 0000000..9604b2c
--- /dev/null
+++ b/src/db/migrations/0000_harsh_white_tiger.sql
@@ -0,0 +1,295 @@
+CREATE TABLE "account" (
+ "id" text PRIMARY KEY NOT NULL,
+ "account_id" text NOT NULL,
+ "provider_id" text NOT NULL,
+ "user_id" text NOT NULL,
+ "access_token" text,
+ "refresh_token" text,
+ "id_token" text,
+ "access_token_expires_at" timestamp with time zone,
+ "refresh_token_expires_at" timestamp with time zone,
+ "scope" text,
+ "password" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "apikey" (
+ "id" text PRIMARY KEY NOT NULL,
+ "config_id" text DEFAULT 'default' NOT NULL,
+ "name" text,
+ "start" text,
+ "reference_id" text NOT NULL,
+ "prefix" text,
+ "key" text NOT NULL,
+ "refill_interval" integer,
+ "refill_amount" integer,
+ "last_refill_at" timestamp with time zone,
+ "enabled" boolean DEFAULT true,
+ "rate_limit_enabled" boolean DEFAULT true,
+ "rate_limit_time_window" integer DEFAULT 86400000,
+ "rate_limit_max" integer DEFAULT 10,
+ "request_count" integer DEFAULT 0,
+ "remaining" integer,
+ "last_request" timestamp with time zone,
+ "expires_at" timestamp with time zone,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "permissions" text,
+ "metadata" text
+);
+--> statement-breakpoint
+CREATE TABLE "ark_collections" (
+ "collection_id" uuid PRIMARY KEY NOT NULL,
+ "ark_id" text NOT NULL,
+ "enabled" boolean DEFAULT true NOT NULL,
+ "custom_url" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "ark_collections_ark_id_unique" UNIQUE("ark_id")
+);
+--> statement-breakpoint
+CREATE TABLE "ark_record_types" (
+ "collection_id" uuid NOT NULL,
+ "record_type" text NOT NULL,
+ "redirect_url_field" text NOT NULL,
+ CONSTRAINT "ark_record_types_collection_id_record_type_pk" PRIMARY KEY("collection_id","record_type")
+);
+--> statement-breakpoint
+CREATE TABLE "ark_shoulders" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "organization_id" text NOT NULL,
+ "shoulder" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "ark_shoulders_organization_id_unique" UNIQUE("organization_id"),
+ CONSTRAINT "ark_shoulders_shoulder_unique" UNIQUE("shoulder")
+);
+--> statement-breakpoint
+CREATE TABLE "collections" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "organization_id" text NOT NULL,
+ "slug" text NOT NULL,
+ "name" text NOT NULL,
+ "description" text,
+ "public" boolean DEFAULT false NOT NULL,
+ "forked_from" uuid,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "collections_organization_id_slug_unique" UNIQUE("organization_id","slug")
+);
+--> statement-breakpoint
+CREATE TABLE "files" (
+ "hash" text PRIMARY KEY NOT NULL,
+ "size" bigint NOT NULL,
+ "mime_type" text NOT NULL,
+ "storage_key" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "invitation" (
+ "id" text PRIMARY KEY NOT NULL,
+ "organization_id" text NOT NULL,
+ "email" text NOT NULL,
+ "role" text,
+ "status" text DEFAULT 'pending' NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "inviter_id" text NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "member" (
+ "id" text PRIMARY KEY NOT NULL,
+ "organization_id" text NOT NULL,
+ "user_id" text NOT NULL,
+ "role" text DEFAULT 'member' NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "organization" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "slug" text NOT NULL,
+ "logo" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "metadata" text,
+ "bio" text,
+ "website" text,
+ "avatar_url" text,
+ "ark_naan" text,
+ "kf_org_id" text,
+ "is_default" boolean DEFAULT false,
+ CONSTRAINT "organization_slug_unique" UNIQUE("slug")
+);
+--> statement-breakpoint
+CREATE TABLE "records" (
+ "version_id" bigint NOT NULL,
+ "record_id" text NOT NULL,
+ "type" text NOT NULL,
+ "data" jsonb NOT NULL,
+ "private" boolean DEFAULT false NOT NULL,
+ CONSTRAINT "records_version_id_record_id_pk" PRIMARY KEY("version_id","record_id")
+);
+--> statement-breakpoint
+CREATE TABLE "schema_labels" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "schema_id" uuid NOT NULL,
+ "label" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "schema_labels_schema_id_label_unique" UNIQUE("schema_id","label")
+);
+--> statement-breakpoint
+CREATE TABLE "schemas" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "schema" jsonb NOT NULL,
+ "schema_hash" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "schemas_schema_hash_unique" UNIQUE("schema_hash")
+);
+--> statement-breakpoint
+CREATE TABLE "session" (
+ "id" text PRIMARY KEY NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL,
+ "token" text NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "ip_address" text,
+ "user_agent" text,
+ "user_id" text NOT NULL,
+ "active_organization_id" text,
+ CONSTRAINT "session_token_unique" UNIQUE("token")
+);
+--> statement-breakpoint
+CREATE TABLE "sync_runs" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "trigger" text NOT NULL,
+ "status" text NOT NULL,
+ "started_at" timestamp with time zone NOT NULL,
+ "finished_at" timestamp with time zone,
+ "collections_synced" integer DEFAULT 0 NOT NULL,
+ "collections_created" integer DEFAULT 0 NOT NULL,
+ "collections_failed" integer DEFAULT 0 NOT NULL,
+ "versions_pulled" integer DEFAULT 0 NOT NULL,
+ "files_downloaded" integer DEFAULT 0 NOT NULL,
+ "files_skipped" integer DEFAULT 0 NOT NULL,
+ "errors" jsonb DEFAULT '[]'::jsonb NOT NULL,
+ "logs" jsonb DEFAULT '[]'::jsonb NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "upload_records" (
+ "session_id" uuid NOT NULL,
+ "record_id" text NOT NULL,
+ "type" text,
+ "data" jsonb,
+ "private" boolean DEFAULT false,
+ "operation" text NOT NULL,
+ CONSTRAINT "upload_records_session_id_record_id_pk" PRIMARY KEY("session_id","record_id")
+);
+--> statement-breakpoint
+CREATE TABLE "upload_sessions" (
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
+ "collection_id" uuid NOT NULL,
+ "user_id" text NOT NULL,
+ "base_version" integer,
+ "message" text,
+ "readme" text,
+ "app_id" text,
+ "actor_id" text,
+ "schemas" jsonb,
+ "status" text DEFAULT 'open' NOT NULL,
+ "record_count" integer DEFAULT 0 NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "user" (
+ "id" text PRIMARY KEY NOT NULL,
+ "name" text NOT NULL,
+ "email" text NOT NULL,
+ "email_verified" boolean DEFAULT false NOT NULL,
+ "image" text,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "user_email_unique" UNIQUE("email")
+);
+--> statement-breakpoint
+CREATE TABLE "verification" (
+ "id" text PRIMARY KEY NOT NULL,
+ "identifier" text NOT NULL,
+ "value" text NOT NULL,
+ "expires_at" timestamp with time zone NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
+);
+--> statement-breakpoint
+CREATE TABLE "version_files" (
+ "version_id" bigint NOT NULL,
+ "file_hash" text NOT NULL,
+ CONSTRAINT "version_files_version_id_file_hash_pk" PRIMARY KEY("version_id","file_hash")
+);
+--> statement-breakpoint
+CREATE TABLE "version_schemas" (
+ "version_id" bigint NOT NULL,
+ "slug" text NOT NULL,
+ "schema_id" uuid NOT NULL,
+ CONSTRAINT "version_schemas_version_id_slug_pk" PRIMARY KEY("version_id","slug")
+);
+--> statement-breakpoint
+CREATE TABLE "versions" (
+ "id" bigserial PRIMARY KEY NOT NULL,
+ "collection_id" uuid NOT NULL,
+ "number" integer NOT NULL,
+ "semver" text NOT NULL,
+ "hash" text NOT NULL,
+ "public_hash" text,
+ "base_number" integer,
+ "message" text,
+ "readme" text,
+ "pushed_by" text,
+ "app_id" text,
+ "actor_id" text,
+ "signature" text,
+ "record_count" integer NOT NULL,
+ "file_count" integer NOT NULL,
+ "total_bytes" bigint NOT NULL,
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
+ CONSTRAINT "versions_collection_id_number_unique" UNIQUE("collection_id","number"),
+ CONSTRAINT "versions_collection_id_hash_unique" UNIQUE("collection_id","hash")
+);
+--> statement-breakpoint
+ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "ark_collections" ADD CONSTRAINT "ark_collections_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "ark_record_types" ADD CONSTRAINT "ark_record_types_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "ark_shoulders" ADD CONSTRAINT "ark_shoulders_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "collections" ADD CONSTRAINT "collections_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "collections" ADD CONSTRAINT "collections_forked_from_collections_id_fk" FOREIGN KEY ("forked_from") REFERENCES "public"."collections"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "records" ADD CONSTRAINT "records_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "schema_labels" ADD CONSTRAINT "schema_labels_schema_id_schemas_id_fk" FOREIGN KEY ("schema_id") REFERENCES "public"."schemas"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "upload_records" ADD CONSTRAINT "upload_records_session_id_upload_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."upload_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "upload_sessions" ADD CONSTRAINT "upload_sessions_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "upload_sessions" ADD CONSTRAINT "upload_sessions_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "version_files" ADD CONSTRAINT "version_files_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "version_files" ADD CONSTRAINT "version_files_file_hash_files_hash_fk" FOREIGN KEY ("file_hash") REFERENCES "public"."files"("hash") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "version_schemas" ADD CONSTRAINT "version_schemas_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "version_schemas" ADD CONSTRAINT "version_schemas_schema_id_schemas_id_fk" FOREIGN KEY ("schema_id") REFERENCES "public"."schemas"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "versions" ADD CONSTRAINT "versions_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "versions" ADD CONSTRAINT "versions_pushed_by_user_id_fk" FOREIGN KEY ("pushed_by") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
+CREATE INDEX "account_user_id_idx" ON "account" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "apikey_config_id_idx" ON "apikey" USING btree ("config_id");--> statement-breakpoint
+CREATE INDEX "apikey_reference_id_idx" ON "apikey" USING btree ("reference_id");--> statement-breakpoint
+CREATE INDEX "apikey_key_idx" ON "apikey" USING btree ("key");--> statement-breakpoint
+CREATE INDEX "collections_organization_id_idx" ON "collections" USING btree ("organization_id");--> statement-breakpoint
+CREATE INDEX "invitation_organization_id_idx" ON "invitation" USING btree ("organization_id");--> statement-breakpoint
+CREATE INDEX "invitation_email_idx" ON "invitation" USING btree ("email");--> statement-breakpoint
+CREATE INDEX "member_organization_id_idx" ON "member" USING btree ("organization_id");--> statement-breakpoint
+CREATE INDEX "member_user_id_idx" ON "member" USING btree ("user_id");--> statement-breakpoint
+CREATE UNIQUE INDEX "organization_slug_uidx" ON "organization" USING btree ("slug");--> statement-breakpoint
+CREATE INDEX "records_version_id_type_idx" ON "records" USING btree ("version_id","type");--> statement-breakpoint
+CREATE INDEX "schema_labels_label_idx" ON "schema_labels" USING btree ("label");--> statement-breakpoint
+CREATE INDEX "session_user_id_idx" ON "session" USING btree ("user_id");--> statement-breakpoint
+CREATE INDEX "verification_identifier_idx" ON "verification" USING btree ("identifier");--> statement-breakpoint
+CREATE INDEX "version_files_file_hash_idx" ON "version_files" USING btree ("file_hash");--> statement-breakpoint
+CREATE INDEX "version_schemas_schema_id_idx" ON "version_schemas" USING btree ("schema_id");
\ No newline at end of file
diff --git a/src/db/migrations/0000_military_machine_man.sql b/src/db/migrations/0000_military_machine_man.sql
deleted file mode 100644
index 1297b19..0000000
--- a/src/db/migrations/0000_military_machine_man.sql
+++ /dev/null
@@ -1,134 +0,0 @@
-CREATE TABLE "accounts" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "slug" text NOT NULL,
- "type" text NOT NULL,
- "display_name" text NOT NULL,
- "email" text,
- "password_hash" text,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "accounts_slug_unique" UNIQUE("slug")
-);
---> statement-breakpoint
-CREATE TABLE "api_keys" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "account_id" uuid NOT NULL,
- "collection_id" uuid,
- "scope" text NOT NULL,
- "key_hash" text NOT NULL,
- "label" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- "last_used_at" timestamp with time zone
-);
---> statement-breakpoint
-CREATE TABLE "collections" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "account_id" uuid NOT NULL,
- "slug" text NOT NULL,
- "name" text NOT NULL,
- "description" text,
- "public" boolean DEFAULT false NOT NULL,
- "forked_from" uuid,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "collections_account_id_slug_unique" UNIQUE("account_id","slug")
-);
---> statement-breakpoint
-CREATE TABLE "files" (
- "hash" text PRIMARY KEY NOT NULL,
- "size" bigint NOT NULL,
- "mime_type" text NOT NULL,
- "storage_key" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL
-);
---> statement-breakpoint
-CREATE TABLE "org_memberships" (
- "org_id" uuid NOT NULL,
- "user_id" uuid NOT NULL,
- "role" text NOT NULL,
- CONSTRAINT "org_memberships_org_id_user_id_pk" PRIMARY KEY("org_id","user_id")
-);
---> statement-breakpoint
-CREATE TABLE "records" (
- "version_id" bigint NOT NULL,
- "record_id" text NOT NULL,
- "type" text NOT NULL,
- "data" jsonb NOT NULL,
- "private" boolean DEFAULT false NOT NULL,
- CONSTRAINT "records_version_id_record_id_pk" PRIMARY KEY("version_id","record_id")
-);
---> statement-breakpoint
-CREATE TABLE "schema_labels" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "schema_id" uuid NOT NULL,
- "label" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "schema_labels_schema_id_label_unique" UNIQUE("schema_id","label")
-);
---> statement-breakpoint
-CREATE TABLE "schemas" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "schema" jsonb NOT NULL,
- "schema_hash" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "schemas_schema_hash_unique" UNIQUE("schema_hash")
-);
---> statement-breakpoint
-CREATE TABLE "sessions" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "user_id" uuid NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- "expires_at" timestamp with time zone NOT NULL
-);
---> statement-breakpoint
-CREATE TABLE "version_files" (
- "version_id" bigint NOT NULL,
- "file_hash" text NOT NULL,
- CONSTRAINT "version_files_version_id_file_hash_pk" PRIMARY KEY("version_id","file_hash")
-);
---> statement-breakpoint
-CREATE TABLE "version_schemas" (
- "version_id" bigint NOT NULL,
- "slug" text NOT NULL,
- "schema_id" uuid NOT NULL,
- CONSTRAINT "version_schemas_version_id_slug_pk" PRIMARY KEY("version_id","slug")
-);
---> statement-breakpoint
-CREATE TABLE "versions" (
- "id" bigserial PRIMARY KEY NOT NULL,
- "collection_id" uuid NOT NULL,
- "number" integer NOT NULL,
- "semver" text NOT NULL,
- "hash" text NOT NULL,
- "public_hash" text,
- "base_number" integer,
- "message" text,
- "readme" text,
- "pushed_by" uuid,
- "app_id" text,
- "actor_id" text,
- "signature" text,
- "record_count" integer NOT NULL,
- "file_count" integer NOT NULL,
- "total_bytes" bigint NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "versions_collection_id_number_unique" UNIQUE("collection_id","number"),
- CONSTRAINT "versions_collection_id_hash_unique" UNIQUE("collection_id","hash")
-);
---> statement-breakpoint
-ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "api_keys" ADD CONSTRAINT "api_keys_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "collections" ADD CONSTRAINT "collections_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "collections" ADD CONSTRAINT "collections_forked_from_collections_id_fk" FOREIGN KEY ("forked_from") REFERENCES "public"."collections"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "org_memberships" ADD CONSTRAINT "org_memberships_org_id_accounts_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "org_memberships" ADD CONSTRAINT "org_memberships_user_id_accounts_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "records" ADD CONSTRAINT "records_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "schema_labels" ADD CONSTRAINT "schema_labels_schema_id_schemas_id_fk" FOREIGN KEY ("schema_id") REFERENCES "public"."schemas"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_accounts_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "version_files" ADD CONSTRAINT "version_files_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "version_files" ADD CONSTRAINT "version_files_file_hash_files_hash_fk" FOREIGN KEY ("file_hash") REFERENCES "public"."files"("hash") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "version_schemas" ADD CONSTRAINT "version_schemas_version_id_versions_id_fk" FOREIGN KEY ("version_id") REFERENCES "public"."versions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "version_schemas" ADD CONSTRAINT "version_schemas_schema_id_schemas_id_fk" FOREIGN KEY ("schema_id") REFERENCES "public"."schemas"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "versions" ADD CONSTRAINT "versions_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "versions" ADD CONSTRAINT "versions_pushed_by_accounts_id_fk" FOREIGN KEY ("pushed_by") REFERENCES "public"."accounts"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
-CREATE INDEX "schema_labels_label_idx" ON "schema_labels" USING btree ("label");--> statement-breakpoint
-CREATE INDEX "version_schemas_schema_id_idx" ON "version_schemas" USING btree ("schema_id");
\ No newline at end of file
diff --git a/src/db/migrations/0001_daffy_agent_zero.sql b/src/db/migrations/0001_daffy_agent_zero.sql
deleted file mode 100644
index 34ef389..0000000
--- a/src/db/migrations/0001_daffy_agent_zero.sql
+++ /dev/null
@@ -1,35 +0,0 @@
-CREATE TABLE "org_invitations" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "org_id" uuid NOT NULL,
- "email" text NOT NULL,
- "role" text NOT NULL,
- "invited_by" uuid NOT NULL,
- "token" text NOT NULL,
- "expires_at" timestamp with time zone NOT NULL,
- "accepted_at" timestamp with time zone,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "org_invitations_token_unique" UNIQUE("token")
-);
---> statement-breakpoint
-CREATE TABLE "password_reset_tokens" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "user_id" uuid NOT NULL,
- "token_hash" text NOT NULL,
- "expires_at" timestamp with time zone NOT NULL,
- "used_at" timestamp with time zone,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL
-);
---> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "bio" text;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "website" text;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "location" text;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "avatar_url" text;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "email_verified" boolean DEFAULT false NOT NULL;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "notification_prefs" jsonb;--> statement-breakpoint
-ALTER TABLE "api_keys" ADD COLUMN "key_prefix" text;--> statement-breakpoint
-ALTER TABLE "api_keys" ADD COLUMN "expires_at" timestamp with time zone;--> statement-breakpoint
-ALTER TABLE "sessions" ADD COLUMN "user_agent" text;--> statement-breakpoint
-ALTER TABLE "sessions" ADD COLUMN "ip_address" text;--> statement-breakpoint
-ALTER TABLE "org_invitations" ADD CONSTRAINT "org_invitations_org_id_accounts_id_fk" FOREIGN KEY ("org_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "org_invitations" ADD CONSTRAINT "org_invitations_invited_by_accounts_id_fk" FOREIGN KEY ("invited_by") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_accounts_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/src/db/migrations/0002_harsh_madrox.sql b/src/db/migrations/0002_harsh_madrox.sql
deleted file mode 100644
index 5ba1821..0000000
--- a/src/db/migrations/0002_harsh_madrox.sql
+++ /dev/null
@@ -1,29 +0,0 @@
-CREATE TABLE "upload_records" (
- "session_id" uuid NOT NULL,
- "record_id" text NOT NULL,
- "type" text,
- "data" jsonb,
- "private" boolean DEFAULT false,
- "operation" text NOT NULL,
- CONSTRAINT "upload_records_session_id_record_id_pk" PRIMARY KEY("session_id","record_id")
-);
---> statement-breakpoint
-CREATE TABLE "upload_sessions" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "collection_id" uuid NOT NULL,
- "account_id" uuid NOT NULL,
- "base_version" integer,
- "message" text,
- "readme" text,
- "app_id" text,
- "actor_id" text,
- "schemas" jsonb,
- "status" text DEFAULT 'open' NOT NULL,
- "record_count" integer DEFAULT 0 NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- "expires_at" timestamp with time zone NOT NULL
-);
---> statement-breakpoint
-ALTER TABLE "upload_records" ADD CONSTRAINT "upload_records_session_id_upload_sessions_id_fk" FOREIGN KEY ("session_id") REFERENCES "public"."upload_sessions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "upload_sessions" ADD CONSTRAINT "upload_sessions_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "upload_sessions" ADD CONSTRAINT "upload_sessions_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;
\ No newline at end of file
diff --git a/src/db/migrations/0003_cool_leper_queen.sql b/src/db/migrations/0003_cool_leper_queen.sql
deleted file mode 100644
index 3b4a1c2..0000000
--- a/src/db/migrations/0003_cool_leper_queen.sql
+++ /dev/null
@@ -1,3 +0,0 @@
-CREATE INDEX "collections_account_id_idx" ON "collections" USING btree ("account_id");--> statement-breakpoint
-CREATE INDEX "records_version_id_type_idx" ON "records" USING btree ("version_id","type");--> statement-breakpoint
-CREATE INDEX "version_files_file_hash_idx" ON "version_files" USING btree ("file_hash");
\ No newline at end of file
diff --git a/src/db/migrations/0004_powerful_marvex.sql b/src/db/migrations/0004_powerful_marvex.sql
deleted file mode 100644
index 75b3f97..0000000
--- a/src/db/migrations/0004_powerful_marvex.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-CREATE TABLE "sync_runs" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "trigger" text NOT NULL,
- "status" text NOT NULL,
- "started_at" timestamp with time zone NOT NULL,
- "finished_at" timestamp with time zone,
- "collections_synced" integer DEFAULT 0 NOT NULL,
- "collections_created" integer DEFAULT 0 NOT NULL,
- "collections_failed" integer DEFAULT 0 NOT NULL,
- "versions_pulled" integer DEFAULT 0 NOT NULL,
- "files_downloaded" integer DEFAULT 0 NOT NULL,
- "files_skipped" integer DEFAULT 0 NOT NULL,
- "errors" jsonb DEFAULT '[]'::jsonb NOT NULL
-);
diff --git a/src/db/migrations/0005_smiling_baron_zemo.sql b/src/db/migrations/0005_smiling_baron_zemo.sql
deleted file mode 100644
index 0093cd8..0000000
--- a/src/db/migrations/0005_smiling_baron_zemo.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE "sync_runs" ADD COLUMN "logs" jsonb DEFAULT '[]'::jsonb NOT NULL;
\ No newline at end of file
diff --git a/src/db/migrations/0006_ark_identifiers.sql b/src/db/migrations/0006_ark_identifiers.sql
deleted file mode 100644
index 74578d6..0000000
--- a/src/db/migrations/0006_ark_identifiers.sql
+++ /dev/null
@@ -1,29 +0,0 @@
-ALTER TABLE "accounts" ADD COLUMN "ark_naan" text;--> statement-breakpoint
-CREATE TABLE "ark_shoulders" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "account_id" uuid NOT NULL,
- "shoulder" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "ark_shoulders_account_id_unique" UNIQUE("account_id"),
- CONSTRAINT "ark_shoulders_shoulder_unique" UNIQUE("shoulder")
-);
---> statement-breakpoint
-CREATE TABLE "ark_collections" (
- "collection_id" uuid PRIMARY KEY NOT NULL,
- "ark_id" text NOT NULL,
- "enabled" boolean DEFAULT true NOT NULL,
- "custom_url" text,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "ark_collections_ark_id_unique" UNIQUE("ark_id")
-);
---> statement-breakpoint
-CREATE TABLE "ark_record_types" (
- "collection_id" uuid NOT NULL,
- "record_type" text NOT NULL,
- "redirect_url_field" text NOT NULL,
- CONSTRAINT "ark_record_types_collection_id_record_type_pk" PRIMARY KEY("collection_id","record_type")
-);
---> statement-breakpoint
-ALTER TABLE "ark_shoulders" ADD CONSTRAINT "ark_shoulders_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "ark_collections" ADD CONSTRAINT "ark_collections_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
-ALTER TABLE "ark_record_types" ADD CONSTRAINT "ark_record_types_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;
diff --git a/src/db/migrations/0007_kf-auth-integration.sql b/src/db/migrations/0007_kf-auth-integration.sql
deleted file mode 100644
index 02ef80f..0000000
--- a/src/db/migrations/0007_kf-auth-integration.sql
+++ /dev/null
@@ -1,47 +0,0 @@
-CREATE TABLE IF NOT EXISTS "ark_collections" (
- "collection_id" uuid PRIMARY KEY NOT NULL,
- "ark_id" text NOT NULL,
- "enabled" boolean DEFAULT true NOT NULL,
- "custom_url" text,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "ark_collections_ark_id_unique" UNIQUE("ark_id")
-);
---> statement-breakpoint
-CREATE TABLE IF NOT EXISTS "ark_record_types" (
- "collection_id" uuid NOT NULL,
- "record_type" text NOT NULL,
- "redirect_url_field" text NOT NULL,
- CONSTRAINT "ark_record_types_collection_id_record_type_pk" PRIMARY KEY("collection_id","record_type")
-);
---> statement-breakpoint
-CREATE TABLE IF NOT EXISTS "ark_shoulders" (
- "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
- "account_id" uuid NOT NULL,
- "shoulder" text NOT NULL,
- "created_at" timestamp with time zone DEFAULT now() NOT NULL,
- CONSTRAINT "ark_shoulders_account_id_unique" UNIQUE("account_id"),
- CONSTRAINT "ark_shoulders_shoulder_unique" UNIQUE("shoulder")
-);
---> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN IF NOT EXISTS "ark_naan" text;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN IF NOT EXISTS "kf_user_id" text;--> statement-breakpoint
-DO $$ BEGIN
- IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'ark_collections_collection_id_collections_id_fk') THEN
- ALTER TABLE "ark_collections" ADD CONSTRAINT "ark_collections_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;
- END IF;
-END $$;--> statement-breakpoint
-DO $$ BEGIN
- IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'ark_record_types_collection_id_collections_id_fk') THEN
- ALTER TABLE "ark_record_types" ADD CONSTRAINT "ark_record_types_collection_id_collections_id_fk" FOREIGN KEY ("collection_id") REFERENCES "public"."collections"("id") ON DELETE cascade ON UPDATE no action;
- END IF;
-END $$;--> statement-breakpoint
-DO $$ BEGIN
- IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'ark_shoulders_account_id_accounts_id_fk') THEN
- ALTER TABLE "ark_shoulders" ADD CONSTRAINT "ark_shoulders_account_id_accounts_id_fk" FOREIGN KEY ("account_id") REFERENCES "public"."accounts"("id") ON DELETE cascade ON UPDATE no action;
- END IF;
-END $$;--> statement-breakpoint
-DO $$ BEGIN
- IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'accounts_kf_user_id_unique') THEN
- ALTER TABLE "accounts" ADD CONSTRAINT "accounts_kf_user_id_unique" UNIQUE("kf_user_id");
- END IF;
-END $$;
\ No newline at end of file
diff --git a/src/db/migrations/0008_sudden_cyclops.sql b/src/db/migrations/0008_sudden_cyclops.sql
deleted file mode 100644
index 4cfe033..0000000
--- a/src/db/migrations/0008_sudden_cyclops.sql
+++ /dev/null
@@ -1,6 +0,0 @@
-ALTER TABLE "password_reset_tokens" DISABLE ROW LEVEL SECURITY;--> statement-breakpoint
-DROP TABLE "password_reset_tokens" CASCADE;--> statement-breakpoint
-ALTER TABLE "accounts" ADD COLUMN "kf_org_id" text;--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "password_hash";--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "email_verified";--> statement-breakpoint
-ALTER TABLE "accounts" ADD CONSTRAINT "accounts_kf_org_id_unique" UNIQUE("kf_org_id");
\ No newline at end of file
diff --git a/src/db/migrations/0009_salty_colossus.sql b/src/db/migrations/0009_salty_colossus.sql
deleted file mode 100644
index 970401a..0000000
--- a/src/db/migrations/0009_salty_colossus.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE "accounts" ADD COLUMN "kf_orgs" jsonb;
\ No newline at end of file
diff --git a/src/db/migrations/0010_keen_meltdown.sql b/src/db/migrations/0010_keen_meltdown.sql
deleted file mode 100644
index 91c3a75..0000000
--- a/src/db/migrations/0010_keen_meltdown.sql
+++ /dev/null
@@ -1,5 +0,0 @@
-ALTER TABLE "accounts" DROP CONSTRAINT "accounts_kf_user_id_unique";--> statement-breakpoint
-ALTER TABLE "accounts" DROP CONSTRAINT "accounts_kf_org_id_unique";--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "email";--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "kf_user_id";--> statement-breakpoint
-ALTER TABLE "accounts" DROP COLUMN "kf_orgs";
\ No newline at end of file
diff --git a/src/db/migrations/0011_messy_molten_man.sql b/src/db/migrations/0011_messy_molten_man.sql
deleted file mode 100644
index fd40531..0000000
--- a/src/db/migrations/0011_messy_molten_man.sql
+++ /dev/null
@@ -1 +0,0 @@
-ALTER TABLE "accounts" ALTER COLUMN "display_name" DROP NOT NULL;
\ No newline at end of file
diff --git a/src/db/migrations/meta/0000_snapshot.json b/src/db/migrations/meta/0000_snapshot.json
index de3788d..18af9b3 100644
--- a/src/db/migrations/meta/0000_snapshot.json
+++ b/src/db/migrations/meta/0000_snapshot.json
@@ -1,92 +1,64 @@
{
- "id": "1c352149-7929-45b2-b5bd-d35ebb3c6c4e",
+ "id": "18f75d75-9ad4-4456-b6ce-32e3ff1e3eb4",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
- "public.accounts": {
- "name": "accounts",
+ "public.account": {
+ "name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
- "type": "uuid",
+ "type": "text",
"primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
+ "notNull": true
},
- "slug": {
- "name": "slug",
+ "account_id": {
+ "name": "account_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
- "type": {
- "name": "type",
+ "provider_id": {
+ "name": "provider_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
- "display_name": {
- "name": "display_name",
+ "user_id": {
+ "name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
- "email": {
- "name": "email",
+ "access_token": {
+ "name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
- "password_hash": {
- "name": "password_hash",
+ "refresh_token": {
+ "name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
+ "id_token": {
+ "name": "id_token",
+ "type": "text",
"primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
+ "notNull": false
},
- "account_id": {
- "name": "account_id",
- "type": "uuid",
+ "access_token_expires_at": {
+ "name": "access_token_expires_at",
+ "type": "timestamp with time zone",
"primaryKey": false,
- "notNull": true
+ "notNull": false
},
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
+ "refresh_token_expires_at": {
+ "name": "refresh_token_expires_at",
+ "type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
@@ -94,19 +66,13 @@
"name": "scope",
"type": "text",
"primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
+ "notNull": false
},
- "label": {
- "name": "label",
+ "password": {
+ "name": "password",
"type": "text",
"primaryKey": false,
- "notNull": true
+ "notNull": false
},
"created_at": {
"name": "created_at",
@@ -115,29 +81,37 @@
"notNull": true,
"default": "now()"
},
- "last_used_at": {
- "name": "last_used_at",
+ "updated_at": {
+ "name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
- "notNull": false
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "account_user_id_idx": {
+ "name": "account_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
}
},
- "indexes": {},
"foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
+ "account_user_id_user_id_fk": {
+ "name": "account_user_id_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
@@ -149,51 +123,121 @@
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.collections": {
- "name": "collections",
+ "public.apikey": {
+ "name": "apikey",
"schema": "",
"columns": {
"id": {
"name": "id",
- "type": "uuid",
+ "type": "text",
"primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
"notNull": true
},
- "slug": {
- "name": "slug",
+ "config_id": {
+ "name": "config_id",
"type": "text",
"primaryKey": false,
- "notNull": true
+ "notNull": true,
+ "default": "'default'"
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
+ "notNull": false
+ },
+ "start": {
+ "name": "start",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "reference_id": {
+ "name": "reference_id",
+ "type": "text",
+ "primaryKey": false,
"notNull": true
},
- "description": {
- "name": "description",
+ "prefix": {
+ "name": "prefix",
"type": "text",
"primaryKey": false,
"notNull": false
},
- "public": {
- "name": "public",
+ "key": {
+ "name": "key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "refill_interval": {
+ "name": "refill_interval",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "refill_amount": {
+ "name": "refill_amount",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_refill_at": {
+ "name": "last_refill_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "enabled": {
+ "name": "enabled",
"type": "boolean",
"primaryKey": false,
- "notNull": true,
- "default": false
+ "notNull": false,
+ "default": true
},
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
+ "rate_limit_enabled": {
+ "name": "rate_limit_enabled",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": true
+ },
+ "rate_limit_time_window": {
+ "name": "rate_limit_time_window",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 86400000
+ },
+ "rate_limit_max": {
+ "name": "rate_limit_max",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 10
+ },
+ "request_count": {
+ "name": "request_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false,
+ "default": 0
+ },
+ "remaining": {
+ "name": "remaining",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "last_request": {
+ "name": "last_request",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
"primaryKey": false,
"notNull": false
},
@@ -210,68 +254,102 @@
"primaryKey": false,
"notNull": true,
"default": "now()"
+ },
+ "permissions": {
+ "name": "permissions",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
}
},
- "indexes": {},
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "indexes": {
+ "apikey_config_id_idx": {
+ "name": "apikey_config_id_idx",
+ "columns": [
+ {
+ "expression": "config_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
},
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
+ "apikey_reference_id_idx": {
+ "name": "apikey_reference_id_idx",
+ "columns": [
+ {
+ "expression": "reference_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "apikey_key_idx": {
+ "name": "apikey_key_idx",
+ "columns": [
+ {
+ "expression": "key",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
}
},
+ "foreignKeys": {},
"compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
+ "uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.files": {
- "name": "files",
+ "public.ark_collections": {
+ "name": "ark_collections",
"schema": "",
"columns": {
- "hash": {
- "name": "hash",
- "type": "text",
+ "collection_id": {
+ "name": "collection_id",
+ "type": "uuid",
"primaryKey": true,
"notNull": true
},
- "size": {
- "name": "size",
- "type": "bigint",
+ "ark_id": {
+ "name": "ark_id",
+ "type": "text",
"primaryKey": false,
"notNull": true
},
- "mime_type": {
- "name": "mime_type",
- "type": "text",
+ "enabled": {
+ "name": "enabled",
+ "type": "boolean",
"primaryKey": false,
- "notNull": true
+ "notNull": true,
+ "default": true
},
- "storage_key": {
- "name": "storage_key",
+ "custom_url": {
+ "name": "custom_url",
"type": "text",
"primaryKey": false,
- "notNull": true
+ "notNull": false
},
"created_at": {
"name": "created_at",
@@ -282,75 +360,1013 @@
}
},
"indexes": {},
- "foreignKeys": {},
+ "foreignKeys": {
+ "ark_collections_collection_id_collections_id_fk": {
+ "name": "ark_collections_collection_id_collections_id_fk",
+ "tableFrom": "ark_collections",
+ "tableTo": "collections",
+ "columnsFrom": ["collection_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
"compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ark_collections_ark_id_unique": {
+ "name": "ark_collections_ark_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["ark_id"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ark_record_types": {
+ "name": "ark_record_types",
+ "schema": "",
+ "columns": {
+ "collection_id": {
+ "name": "collection_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "record_type": {
+ "name": "record_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "redirect_url_field": {
+ "name": "redirect_url_field",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "ark_record_types_collection_id_collections_id_fk": {
+ "name": "ark_record_types_collection_id_collections_id_fk",
+ "tableFrom": "ark_record_types",
+ "tableTo": "collections",
+ "columnsFrom": ["collection_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "ark_record_types_collection_id_record_type_pk": {
+ "name": "ark_record_types_collection_id_record_type_pk",
+ "columns": ["collection_id", "record_type"]
+ }
+ },
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.ark_shoulders": {
+ "name": "ark_shoulders",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "shoulder": {
+ "name": "shoulder",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "ark_shoulders_organization_id_organization_id_fk": {
+ "name": "ark_shoulders_organization_id_organization_id_fk",
+ "tableFrom": "ark_shoulders",
+ "tableTo": "organization",
+ "columnsFrom": ["organization_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "ark_shoulders_organization_id_unique": {
+ "name": "ark_shoulders_organization_id_unique",
+ "nullsNotDistinct": false,
+ "columns": ["organization_id"]
+ },
+ "ark_shoulders_shoulder_unique": {
+ "name": "ark_shoulders_shoulder_unique",
+ "nullsNotDistinct": false,
+ "columns": ["shoulder"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.collections": {
+ "name": "collections",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "description": {
+ "name": "description",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "public": {
+ "name": "public",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "forked_from": {
+ "name": "forked_from",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "collections_organization_id_idx": {
+ "name": "collections_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "collections_organization_id_organization_id_fk": {
+ "name": "collections_organization_id_organization_id_fk",
+ "tableFrom": "collections",
+ "tableTo": "organization",
+ "columnsFrom": ["organization_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "collections_forked_from_collections_id_fk": {
+ "name": "collections_forked_from_collections_id_fk",
+ "tableFrom": "collections",
+ "tableTo": "collections",
+ "columnsFrom": ["forked_from"],
+ "columnsTo": ["id"],
+ "onDelete": "no action",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "collections_organization_id_slug_unique": {
+ "name": "collections_organization_id_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["organization_id", "slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.files": {
+ "name": "files",
+ "schema": "",
+ "columns": {
+ "hash": {
+ "name": "hash",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "size": {
+ "name": "size",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "mime_type": {
+ "name": "mime_type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "storage_key": {
+ "name": "storage_key",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.invitation": {
+ "name": "invitation",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "email": {
+ "name": "email",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'pending'"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "inviter_id": {
+ "name": "inviter_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ }
+ },
+ "indexes": {
+ "invitation_organization_id_idx": {
+ "name": "invitation_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "invitation_email_idx": {
+ "name": "invitation_email_idx",
+ "columns": [
+ {
+ "expression": "email",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "invitation_organization_id_organization_id_fk": {
+ "name": "invitation_organization_id_organization_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "organization",
+ "columnsFrom": ["organization_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "invitation_inviter_id_user_id_fk": {
+ "name": "invitation_inviter_id_user_id_fk",
+ "tableFrom": "invitation",
+ "tableTo": "user",
+ "columnsFrom": ["inviter_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.member": {
+ "name": "member",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "organization_id": {
+ "name": "organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "role": {
+ "name": "role",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'member'"
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "member_organization_id_idx": {
+ "name": "member_organization_id_idx",
+ "columns": [
+ {
+ "expression": "organization_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ },
+ "member_user_id_idx": {
+ "name": "member_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "member_organization_id_organization_id_fk": {
+ "name": "member_organization_id_organization_id_fk",
+ "tableFrom": "member",
+ "tableTo": "organization",
+ "columnsFrom": ["organization_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "member_user_id_user_id_fk": {
+ "name": "member_user_id_user_id_fk",
+ "tableFrom": "member",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {},
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.organization": {
+ "name": "organization",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "name": {
+ "name": "name",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "slug": {
+ "name": "slug",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "logo": {
+ "name": "logo",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "metadata": {
+ "name": "metadata",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "bio": {
+ "name": "bio",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "website": {
+ "name": "website",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "avatar_url": {
+ "name": "avatar_url",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "ark_naan": {
+ "name": "ark_naan",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "kf_org_id": {
+ "name": "kf_org_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "is_default": {
+ "name": "is_default",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": false,
+ "default": false
+ }
+ },
+ "indexes": {
+ "organization_slug_uidx": {
+ "name": "organization_slug_uidx",
+ "columns": [
+ {
+ "expression": "slug",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": true,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "organization_slug_unique": {
+ "name": "organization_slug_unique",
+ "nullsNotDistinct": false,
+ "columns": ["slug"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.records": {
+ "name": "records",
+ "schema": "",
+ "columns": {
+ "version_id": {
+ "name": "version_id",
+ "type": "bigint",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "record_id": {
+ "name": "record_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "type": {
+ "name": "type",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "data": {
+ "name": "data",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "private": {
+ "name": "private",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ }
+ },
+ "indexes": {
+ "records_version_id_type_idx": {
+ "name": "records_version_id_type_idx",
+ "columns": [
+ {
+ "expression": "version_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ },
+ {
+ "expression": "type",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "records_version_id_versions_id_fk": {
+ "name": "records_version_id_versions_id_fk",
+ "tableFrom": "records",
+ "tableTo": "versions",
+ "columnsFrom": ["version_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "records_version_id_record_id_pk": {
+ "name": "records_version_id_record_id_pk",
+ "columns": ["version_id", "record_id"]
+ }
+ },
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.org_memberships": {
- "name": "org_memberships",
+ "public.schema_labels": {
+ "name": "schema_labels",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "schema_id": {
+ "name": "schema_id",
+ "type": "uuid",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "label": {
+ "name": "label",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {
+ "schema_labels_label_idx": {
+ "name": "schema_labels_label_idx",
+ "columns": [
+ {
+ "expression": "label",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "schema_labels_schema_id_schemas_id_fk": {
+ "name": "schema_labels_schema_id_schemas_id_fk",
+ "tableFrom": "schema_labels",
+ "tableTo": "schemas",
+ "columnsFrom": ["schema_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "schema_labels_schema_id_label_unique": {
+ "name": "schema_labels_schema_id_label_unique",
+ "nullsNotDistinct": false,
+ "columns": ["schema_id", "label"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.schemas": {
+ "name": "schemas",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "schema": {
+ "name": "schema",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "schema_hash": {
+ "name": "schema_hash",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "schemas_schema_hash_unique": {
+ "name": "schemas_schema_hash_unique",
+ "nullsNotDistinct": false,
+ "columns": ["schema_hash"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.session": {
+ "name": "session",
+ "schema": "",
+ "columns": {
+ "id": {
+ "name": "id",
+ "type": "text",
+ "primaryKey": true,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "token": {
+ "name": "token",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "created_at": {
+ "name": "created_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
+ },
+ "ip_address": {
+ "name": "ip_address",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_agent": {
+ "name": "user_agent",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "user_id": {
+ "name": "user_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "active_organization_id": {
+ "name": "active_organization_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ }
+ },
+ "indexes": {
+ "session_user_id_idx": {
+ "name": "session_user_id_idx",
+ "columns": [
+ {
+ "expression": "user_id",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
+ "foreignKeys": {
+ "session_user_id_user_id_fk": {
+ "name": "session_user_id_user_id_fk",
+ "tableFrom": "session",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {},
+ "uniqueConstraints": {
+ "session_token_unique": {
+ "name": "session_token_unique",
+ "nullsNotDistinct": false,
+ "columns": ["token"]
+ }
+ },
+ "policies": {},
+ "checkConstraints": {},
+ "isRLSEnabled": false
+ },
+ "public.sync_runs": {
+ "name": "sync_runs",
"schema": "",
"columns": {
- "org_id": {
- "name": "org_id",
+ "id": {
+ "name": "id",
"type": "uuid",
+ "primaryKey": true,
+ "notNull": true,
+ "default": "gen_random_uuid()"
+ },
+ "trigger": {
+ "name": "trigger",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "started_at": {
+ "name": "started_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "finished_at": {
+ "name": "finished_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "collections_synced": {
+ "name": "collections_synced",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "collections_created": {
+ "name": "collections_created",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "collections_failed": {
+ "name": "collections_failed",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
+ "versions_pulled": {
+ "name": "versions_pulled",
+ "type": "integer",
"primaryKey": false,
- "notNull": true
+ "notNull": true,
+ "default": 0
},
- "user_id": {
- "name": "user_id",
- "type": "uuid",
+ "files_downloaded": {
+ "name": "files_downloaded",
+ "type": "integer",
"primaryKey": false,
- "notNull": true
+ "notNull": true,
+ "default": 0
},
- "role": {
- "name": "role",
- "type": "text",
+ "files_skipped": {
+ "name": "files_skipped",
+ "type": "integer",
"primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "notNull": true,
+ "default": 0
},
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
+ "errors": {
+ "name": "errors",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
+ },
+ "logs": {
+ "name": "logs",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'[]'::jsonb"
}
},
+ "indexes": {},
+ "foreignKeys": {},
+ "compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.records": {
- "name": "records",
+ "public.upload_records": {
+ "name": "upload_records",
"schema": "",
"columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
+ "session_id": {
+ "name": "session_id",
+ "type": "uuid",
"primaryKey": false,
"notNull": true
},
@@ -364,38 +1380,44 @@
"name": "type",
"type": "text",
"primaryKey": false,
- "notNull": true
+ "notNull": false
},
"data": {
"name": "data",
"type": "jsonb",
"primaryKey": false,
- "notNull": true
+ "notNull": false
},
"private": {
"name": "private",
"type": "boolean",
"primaryKey": false,
- "notNull": true,
+ "notNull": false,
"default": false
+ },
+ "operation": {
+ "name": "operation",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
}
},
"indexes": {},
"foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
+ "upload_records_session_id_upload_sessions_id_fk": {
+ "name": "upload_records_session_id_upload_sessions_id_fk",
+ "tableFrom": "upload_records",
+ "tableTo": "upload_sessions",
+ "columnsFrom": ["session_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
+ "upload_records_session_id_record_id_pk": {
+ "name": "upload_records_session_id_record_id_pk",
+ "columns": ["session_id", "record_id"]
}
},
"uniqueConstraints": {},
@@ -403,8 +1425,8 @@
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.schema_labels": {
- "name": "schema_labels",
+ "public.upload_sessions": {
+ "name": "upload_sessions",
"schema": "",
"columns": {
"id": {
@@ -414,125 +1436,198 @@
"notNull": true,
"default": "gen_random_uuid()"
},
- "schema_id": {
- "name": "schema_id",
+ "collection_id": {
+ "name": "collection_id",
"type": "uuid",
"primaryKey": false,
"notNull": true
},
- "label": {
- "name": "label",
+ "user_id": {
+ "name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
+ "base_version": {
+ "name": "base_version",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "message": {
+ "name": "message",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "readme": {
+ "name": "readme",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "app_id": {
+ "name": "app_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "actor_id": {
+ "name": "actor_id",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "schemas": {
+ "name": "schemas",
+ "type": "jsonb",
+ "primaryKey": false,
+ "notNull": false
+ },
+ "status": {
+ "name": "status",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "'open'"
+ },
+ "record_count": {
+ "name": "record_count",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "default": 0
+ },
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true
}
},
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
+ "indexes": {},
"foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
+ "upload_sessions_collection_id_collections_id_fk": {
+ "name": "upload_sessions_collection_id_collections_id_fk",
+ "tableFrom": "upload_sessions",
+ "tableTo": "collections",
+ "columnsFrom": ["collection_id"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ },
+ "upload_sessions_user_id_user_id_fk": {
+ "name": "upload_sessions_user_id_user_id_fk",
+ "tableFrom": "upload_sessions",
+ "tableTo": "user",
+ "columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
+ "uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.schemas": {
- "name": "schemas",
+ "public.user": {
+ "name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
- "type": "uuid",
+ "type": "text",
"primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
+ "notNull": true
},
- "schema": {
- "name": "schema",
- "type": "jsonb",
+ "name": {
+ "name": "name",
+ "type": "text",
"primaryKey": false,
"notNull": true
},
- "schema_hash": {
- "name": "schema_hash",
+ "email": {
+ "name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
+ "email_verified": {
+ "name": "email_verified",
+ "type": "boolean",
+ "primaryKey": false,
+ "notNull": true,
+ "default": false
+ },
+ "image": {
+ "name": "image",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": false
+ },
"created_at": {
"name": "created_at",
"type": "timestamp with time zone",
"primaryKey": false,
"notNull": true,
"default": "now()"
+ },
+ "updated_at": {
+ "name": "updated_at",
+ "type": "timestamp with time zone",
+ "primaryKey": false,
+ "notNull": true,
+ "default": "now()"
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
+ "user_email_unique": {
+ "name": "user_email_unique",
"nullsNotDistinct": false,
- "columns": ["schema_hash"]
+ "columns": ["email"]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
- "public.sessions": {
- "name": "sessions",
+ "public.verification": {
+ "name": "verification",
"schema": "",
"columns": {
"id": {
"name": "id",
- "type": "uuid",
+ "type": "text",
"primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
+ "notNull": true
},
- "user_id": {
- "name": "user_id",
- "type": "uuid",
+ "identifier": {
+ "name": "identifier",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "value": {
+ "name": "value",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true
+ },
+ "expires_at": {
+ "name": "expires_at",
+ "type": "timestamp with time zone",
"primaryKey": false,
"notNull": true
},
@@ -543,25 +1638,32 @@
"notNull": true,
"default": "now()"
},
- "expires_at": {
- "name": "expires_at",
+ "updated_at": {
+ "name": "updated_at",
"type": "timestamp with time zone",
"primaryKey": false,
- "notNull": true
+ "notNull": true,
+ "default": "now()"
}
},
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
+ "indexes": {
+ "verification_identifier_idx": {
+ "name": "verification_identifier_idx",
+ "columns": [
+ {
+ "expression": "identifier",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
}
},
+ "foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
@@ -585,7 +1687,23 @@
"notNull": true
}
},
- "indexes": {},
+ "indexes": {
+ "version_files_file_hash_idx": {
+ "name": "version_files_file_hash_idx",
+ "columns": [
+ {
+ "expression": "file_hash",
+ "isExpression": false,
+ "asc": true,
+ "nulls": "last"
+ }
+ ],
+ "isUnique": false,
+ "concurrently": false,
+ "method": "btree",
+ "with": {}
+ }
+ },
"foreignKeys": {
"version_files_version_id_versions_id_fk": {
"name": "version_files_version_id_versions_id_fk",
@@ -748,7 +1866,7 @@
},
"pushed_by": {
"name": "pushed_by",
- "type": "uuid",
+ "type": "text",
"primaryKey": false,
"notNull": false
},
@@ -807,10 +1925,10 @@
"onDelete": "cascade",
"onUpdate": "no action"
},
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
+ "versions_pushed_by_user_id_fk": {
+ "name": "versions_pushed_by_user_id_fk",
"tableFrom": "versions",
- "tableTo": "accounts",
+ "tableTo": "user",
"columnsFrom": ["pushed_by"],
"columnsTo": ["id"],
"onDelete": "no action",
diff --git a/src/db/migrations/meta/0001_snapshot.json b/src/db/migrations/meta/0001_snapshot.json
deleted file mode 100644
index 065c85d..0000000
--- a/src/db/migrations/meta/0001_snapshot.json
+++ /dev/null
@@ -1,1065 +0,0 @@
-{
- "id": "101eb60b-6cba-4841-8129-26738d4ac704",
- "prevId": "1c352149-7929-45b2-b5bd-d35ebb3c6c4e",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0002_snapshot.json b/src/db/migrations/meta/0002_snapshot.json
deleted file mode 100644
index 4c7f139..0000000
--- a/src/db/migrations/meta/0002_snapshot.json
+++ /dev/null
@@ -1,1244 +0,0 @@
-{
- "id": "a0292107-c832-4338-8cd8-638e32d20203",
- "prevId": "101eb60b-6cba-4841-8129-26738d4ac704",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0003_snapshot.json b/src/db/migrations/meta/0003_snapshot.json
deleted file mode 100644
index 97c48a0..0000000
--- a/src/db/migrations/meta/0003_snapshot.json
+++ /dev/null
@@ -1,1298 +0,0 @@
-{
- "id": "73aa620e-858b-48a3-9bcb-3b974ca6b7a7",
- "prevId": "a0292107-c832-4338-8cd8-638e32d20203",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0004_snapshot.json b/src/db/migrations/meta/0004_snapshot.json
deleted file mode 100644
index 650d8be..0000000
--- a/src/db/migrations/meta/0004_snapshot.json
+++ /dev/null
@@ -1,1391 +0,0 @@
-{
- "id": "6fa76243-2cfe-4d7f-a8c1-aed442118724",
- "prevId": "73aa620e-858b-48a3-9bcb-3b974ca6b7a7",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0005_snapshot.json b/src/db/migrations/meta/0005_snapshot.json
deleted file mode 100644
index 45f2a41..0000000
--- a/src/db/migrations/meta/0005_snapshot.json
+++ /dev/null
@@ -1,1398 +0,0 @@
-{
- "id": "ea5ffb7d-a1a1-48d4-b1f6-25b8e70ef73c",
- "prevId": "6fa76243-2cfe-4d7f-a8c1-aed442118724",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0007_snapshot.json b/src/db/migrations/meta/0007_snapshot.json
deleted file mode 100644
index db18ca5..0000000
--- a/src/db/migrations/meta/0007_snapshot.json
+++ /dev/null
@@ -1,1582 +0,0 @@
-{
- "id": "1d7b9127-9ab3-4de0-876b-add5e0cd8833",
- "prevId": "ea5ffb7d-a1a1-48d4-b1f6-25b8e70ef73c",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "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
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "email_verified": {
- "name": "email_verified",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "ark_naan": {
- "name": "ark_naan",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_user_id": {
- "name": "kf_user_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- },
- "accounts_kf_user_id_unique": {
- "name": "accounts_kf_user_id_unique",
- "nullsNotDistinct": false,
- "columns": ["kf_user_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_collections": {
- "name": "ark_collections",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true
- },
- "ark_id": {
- "name": "ark_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "enabled": {
- "name": "enabled",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": true
- },
- "custom_url": {
- "name": "custom_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_collections_collection_id_collections_id_fk": {
- "name": "ark_collections_collection_id_collections_id_fk",
- "tableFrom": "ark_collections",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_collections_ark_id_unique": {
- "name": "ark_collections_ark_id_unique",
- "nullsNotDistinct": false,
- "columns": ["ark_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_record_types": {
- "name": "ark_record_types",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_type": {
- "name": "record_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "redirect_url_field": {
- "name": "redirect_url_field",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_record_types_collection_id_collections_id_fk": {
- "name": "ark_record_types_collection_id_collections_id_fk",
- "tableFrom": "ark_record_types",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "ark_record_types_collection_id_record_type_pk": {
- "name": "ark_record_types_collection_id_record_type_pk",
- "columns": ["collection_id", "record_type"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_shoulders": {
- "name": "ark_shoulders",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "shoulder": {
- "name": "shoulder",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_shoulders_account_id_accounts_id_fk": {
- "name": "ark_shoulders_account_id_accounts_id_fk",
- "tableFrom": "ark_shoulders",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_shoulders_account_id_unique": {
- "name": "ark_shoulders_account_id_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id"]
- },
- "ark_shoulders_shoulder_unique": {
- "name": "ark_shoulders_shoulder_unique",
- "nullsNotDistinct": false,
- "columns": ["shoulder"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.password_reset_tokens": {
- "name": "password_reset_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
- },
- "token_hash": {
- "name": "token_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "used_at": {
- "name": "used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "password_reset_tokens_user_id_accounts_id_fk": {
- "name": "password_reset_tokens_user_id_accounts_id_fk",
- "tableFrom": "password_reset_tokens",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0008_snapshot.json b/src/db/migrations/meta/0008_snapshot.json
deleted file mode 100644
index 8ecae2f..0000000
--- a/src/db/migrations/meta/0008_snapshot.json
+++ /dev/null
@@ -1,1519 +0,0 @@
-{
- "id": "33f1ef8c-7579-47cd-89cc-9048f6a29871",
- "prevId": "1d7b9127-9ab3-4de0-876b-add5e0cd8833",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "ark_naan": {
- "name": "ark_naan",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_user_id": {
- "name": "kf_user_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_org_id": {
- "name": "kf_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- },
- "accounts_kf_user_id_unique": {
- "name": "accounts_kf_user_id_unique",
- "nullsNotDistinct": false,
- "columns": ["kf_user_id"]
- },
- "accounts_kf_org_id_unique": {
- "name": "accounts_kf_org_id_unique",
- "nullsNotDistinct": false,
- "columns": ["kf_org_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_collections": {
- "name": "ark_collections",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true
- },
- "ark_id": {
- "name": "ark_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "enabled": {
- "name": "enabled",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": true
- },
- "custom_url": {
- "name": "custom_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_collections_collection_id_collections_id_fk": {
- "name": "ark_collections_collection_id_collections_id_fk",
- "tableFrom": "ark_collections",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_collections_ark_id_unique": {
- "name": "ark_collections_ark_id_unique",
- "nullsNotDistinct": false,
- "columns": ["ark_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_record_types": {
- "name": "ark_record_types",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_type": {
- "name": "record_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "redirect_url_field": {
- "name": "redirect_url_field",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_record_types_collection_id_collections_id_fk": {
- "name": "ark_record_types_collection_id_collections_id_fk",
- "tableFrom": "ark_record_types",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "ark_record_types_collection_id_record_type_pk": {
- "name": "ark_record_types_collection_id_record_type_pk",
- "columns": ["collection_id", "record_type"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_shoulders": {
- "name": "ark_shoulders",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "shoulder": {
- "name": "shoulder",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_shoulders_account_id_accounts_id_fk": {
- "name": "ark_shoulders_account_id_accounts_id_fk",
- "tableFrom": "ark_shoulders",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_shoulders_account_id_unique": {
- "name": "ark_shoulders_account_id_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id"]
- },
- "ark_shoulders_shoulder_unique": {
- "name": "ark_shoulders_shoulder_unique",
- "nullsNotDistinct": false,
- "columns": ["shoulder"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0009_snapshot.json b/src/db/migrations/meta/0009_snapshot.json
deleted file mode 100644
index b44e6d4..0000000
--- a/src/db/migrations/meta/0009_snapshot.json
+++ /dev/null
@@ -1,1525 +0,0 @@
-{
- "id": "58f002f7-8fdd-4c06-8cb5-25bf3c2a62e2",
- "prevId": "33f1ef8c-7579-47cd-89cc-9048f6a29871",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "ark_naan": {
- "name": "ark_naan",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_user_id": {
- "name": "kf_user_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_org_id": {
- "name": "kf_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_orgs": {
- "name": "kf_orgs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- },
- "accounts_kf_user_id_unique": {
- "name": "accounts_kf_user_id_unique",
- "nullsNotDistinct": false,
- "columns": ["kf_user_id"]
- },
- "accounts_kf_org_id_unique": {
- "name": "accounts_kf_org_id_unique",
- "nullsNotDistinct": false,
- "columns": ["kf_org_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_collections": {
- "name": "ark_collections",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true
- },
- "ark_id": {
- "name": "ark_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "enabled": {
- "name": "enabled",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": true
- },
- "custom_url": {
- "name": "custom_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_collections_collection_id_collections_id_fk": {
- "name": "ark_collections_collection_id_collections_id_fk",
- "tableFrom": "ark_collections",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_collections_ark_id_unique": {
- "name": "ark_collections_ark_id_unique",
- "nullsNotDistinct": false,
- "columns": ["ark_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_record_types": {
- "name": "ark_record_types",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_type": {
- "name": "record_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "redirect_url_field": {
- "name": "redirect_url_field",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_record_types_collection_id_collections_id_fk": {
- "name": "ark_record_types_collection_id_collections_id_fk",
- "tableFrom": "ark_record_types",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "ark_record_types_collection_id_record_type_pk": {
- "name": "ark_record_types_collection_id_record_type_pk",
- "columns": ["collection_id", "record_type"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_shoulders": {
- "name": "ark_shoulders",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "shoulder": {
- "name": "shoulder",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_shoulders_account_id_accounts_id_fk": {
- "name": "ark_shoulders_account_id_accounts_id_fk",
- "tableFrom": "ark_shoulders",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_shoulders_account_id_unique": {
- "name": "ark_shoulders_account_id_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id"]
- },
- "ark_shoulders_shoulder_unique": {
- "name": "ark_shoulders_shoulder_unique",
- "nullsNotDistinct": false,
- "columns": ["shoulder"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0010_snapshot.json b/src/db/migrations/meta/0010_snapshot.json
deleted file mode 100644
index ad825e9..0000000
--- a/src/db/migrations/meta/0010_snapshot.json
+++ /dev/null
@@ -1,1497 +0,0 @@
-{
- "id": "30fdfcfb-99e9-48b7-841b-0a91525bc555",
- "prevId": "58f002f7-8fdd-4c06-8cb5-25bf3c2a62e2",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "ark_naan": {
- "name": "ark_naan",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_org_id": {
- "name": "kf_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_collections": {
- "name": "ark_collections",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true
- },
- "ark_id": {
- "name": "ark_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "enabled": {
- "name": "enabled",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": true
- },
- "custom_url": {
- "name": "custom_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_collections_collection_id_collections_id_fk": {
- "name": "ark_collections_collection_id_collections_id_fk",
- "tableFrom": "ark_collections",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_collections_ark_id_unique": {
- "name": "ark_collections_ark_id_unique",
- "nullsNotDistinct": false,
- "columns": ["ark_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_record_types": {
- "name": "ark_record_types",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_type": {
- "name": "record_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "redirect_url_field": {
- "name": "redirect_url_field",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_record_types_collection_id_collections_id_fk": {
- "name": "ark_record_types_collection_id_collections_id_fk",
- "tableFrom": "ark_record_types",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "ark_record_types_collection_id_record_type_pk": {
- "name": "ark_record_types_collection_id_record_type_pk",
- "columns": ["collection_id", "record_type"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_shoulders": {
- "name": "ark_shoulders",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "shoulder": {
- "name": "shoulder",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_shoulders_account_id_accounts_id_fk": {
- "name": "ark_shoulders_account_id_accounts_id_fk",
- "tableFrom": "ark_shoulders",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_shoulders_account_id_unique": {
- "name": "ark_shoulders_account_id_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id"]
- },
- "ark_shoulders_shoulder_unique": {
- "name": "ark_shoulders_shoulder_unique",
- "nullsNotDistinct": false,
- "columns": ["shoulder"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/0011_snapshot.json b/src/db/migrations/meta/0011_snapshot.json
deleted file mode 100644
index 846ac21..0000000
--- a/src/db/migrations/meta/0011_snapshot.json
+++ /dev/null
@@ -1,1497 +0,0 @@
-{
- "id": "9898328b-fc86-4214-a39d-4a17f262e207",
- "prevId": "30fdfcfb-99e9-48b7-841b-0a91525bc555",
- "version": "7",
- "dialect": "postgresql",
- "tables": {
- "public.accounts": {
- "name": "accounts",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "display_name": {
- "name": "display_name",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "bio": {
- "name": "bio",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "website": {
- "name": "website",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "location": {
- "name": "location",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "avatar_url": {
- "name": "avatar_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "notification_prefs": {
- "name": "notification_prefs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "ark_naan": {
- "name": "ark_naan",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "kf_org_id": {
- "name": "kf_org_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "accounts_slug_unique": {
- "name": "accounts_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.api_keys": {
- "name": "api_keys",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "scope": {
- "name": "scope",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_hash": {
- "name": "key_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "key_prefix": {
- "name": "key_prefix",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "last_used_at": {
- "name": "last_used_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- }
- },
- "indexes": {},
- "foreignKeys": {
- "api_keys_account_id_accounts_id_fk": {
- "name": "api_keys_account_id_accounts_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "api_keys_collection_id_collections_id_fk": {
- "name": "api_keys_collection_id_collections_id_fk",
- "tableFrom": "api_keys",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_collections": {
- "name": "ark_collections",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true
- },
- "ark_id": {
- "name": "ark_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "enabled": {
- "name": "enabled",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": true
- },
- "custom_url": {
- "name": "custom_url",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_collections_collection_id_collections_id_fk": {
- "name": "ark_collections_collection_id_collections_id_fk",
- "tableFrom": "ark_collections",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_collections_ark_id_unique": {
- "name": "ark_collections_ark_id_unique",
- "nullsNotDistinct": false,
- "columns": ["ark_id"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_record_types": {
- "name": "ark_record_types",
- "schema": "",
- "columns": {
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_type": {
- "name": "record_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "redirect_url_field": {
- "name": "redirect_url_field",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_record_types_collection_id_collections_id_fk": {
- "name": "ark_record_types_collection_id_collections_id_fk",
- "tableFrom": "ark_record_types",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "ark_record_types_collection_id_record_type_pk": {
- "name": "ark_record_types_collection_id_record_type_pk",
- "columns": ["collection_id", "record_type"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.ark_shoulders": {
- "name": "ark_shoulders",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "shoulder": {
- "name": "shoulder",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "ark_shoulders_account_id_accounts_id_fk": {
- "name": "ark_shoulders_account_id_accounts_id_fk",
- "tableFrom": "ark_shoulders",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "ark_shoulders_account_id_unique": {
- "name": "ark_shoulders_account_id_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id"]
- },
- "ark_shoulders_shoulder_unique": {
- "name": "ark_shoulders_shoulder_unique",
- "nullsNotDistinct": false,
- "columns": ["shoulder"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.collections": {
- "name": "collections",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "name": {
- "name": "name",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "description": {
- "name": "description",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "public": {
- "name": "public",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- },
- "forked_from": {
- "name": "forked_from",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "updated_at": {
- "name": "updated_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "collections_account_id_idx": {
- "name": "collections_account_id_idx",
- "columns": [
- {
- "expression": "account_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "collections_account_id_accounts_id_fk": {
- "name": "collections_account_id_accounts_id_fk",
- "tableFrom": "collections",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "collections_forked_from_collections_id_fk": {
- "name": "collections_forked_from_collections_id_fk",
- "tableFrom": "collections",
- "tableTo": "collections",
- "columnsFrom": ["forked_from"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "collections_account_id_slug_unique": {
- "name": "collections_account_id_slug_unique",
- "nullsNotDistinct": false,
- "columns": ["account_id", "slug"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.files": {
- "name": "files",
- "schema": "",
- "columns": {
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": true,
- "notNull": true
- },
- "size": {
- "name": "size",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "mime_type": {
- "name": "mime_type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "storage_key": {
- "name": "storage_key",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_invitations": {
- "name": "org_invitations",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "email": {
- "name": "email",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "invited_by": {
- "name": "invited_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "token": {
- "name": "token",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "accepted_at": {
- "name": "accepted_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_invitations_org_id_accounts_id_fk": {
- "name": "org_invitations_org_id_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_invitations_invited_by_accounts_id_fk": {
- "name": "org_invitations_invited_by_accounts_id_fk",
- "tableFrom": "org_invitations",
- "tableTo": "accounts",
- "columnsFrom": ["invited_by"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "org_invitations_token_unique": {
- "name": "org_invitations_token_unique",
- "nullsNotDistinct": false,
- "columns": ["token"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.org_memberships": {
- "name": "org_memberships",
- "schema": "",
- "columns": {
- "org_id": {
- "name": "org_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "user_id": {
- "name": "user_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "role": {
- "name": "role",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "org_memberships_org_id_accounts_id_fk": {
- "name": "org_memberships_org_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["org_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "org_memberships_user_id_accounts_id_fk": {
- "name": "org_memberships_user_id_accounts_id_fk",
- "tableFrom": "org_memberships",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "org_memberships_org_id_user_id_pk": {
- "name": "org_memberships_org_id_user_id_pk",
- "columns": ["org_id", "user_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.records": {
- "name": "records",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": true,
- "default": false
- }
- },
- "indexes": {
- "records_version_id_type_idx": {
- "name": "records_version_id_type_idx",
- "columns": [
- {
- "expression": "version_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- },
- {
- "expression": "type",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "records_version_id_versions_id_fk": {
- "name": "records_version_id_versions_id_fk",
- "tableFrom": "records",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "records_version_id_record_id_pk": {
- "name": "records_version_id_record_id_pk",
- "columns": ["version_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schema_labels": {
- "name": "schema_labels",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "label": {
- "name": "label",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {
- "schema_labels_label_idx": {
- "name": "schema_labels_label_idx",
- "columns": [
- {
- "expression": "label",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "schema_labels_schema_id_schemas_id_fk": {
- "name": "schema_labels_schema_id_schemas_id_fk",
- "tableFrom": "schema_labels",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schema_labels_schema_id_label_unique": {
- "name": "schema_labels_schema_id_label_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_id", "label"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.schemas": {
- "name": "schemas",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "schema": {
- "name": "schema",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true
- },
- "schema_hash": {
- "name": "schema_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "schemas_schema_hash_unique": {
- "name": "schemas_schema_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["schema_hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sessions": {
- "name": "sessions",
- "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
- },
- "user_agent": {
- "name": "user_agent",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "ip_address": {
- "name": "ip_address",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "sessions_user_id_accounts_id_fk": {
- "name": "sessions_user_id_accounts_id_fk",
- "tableFrom": "sessions",
- "tableTo": "accounts",
- "columnsFrom": ["user_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.sync_runs": {
- "name": "sync_runs",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "trigger": {
- "name": "trigger",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "started_at": {
- "name": "started_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- },
- "finished_at": {
- "name": "finished_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": false
- },
- "collections_synced": {
- "name": "collections_synced",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_created": {
- "name": "collections_created",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "collections_failed": {
- "name": "collections_failed",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "versions_pulled": {
- "name": "versions_pulled",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_downloaded": {
- "name": "files_downloaded",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "files_skipped": {
- "name": "files_skipped",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "errors": {
- "name": "errors",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- },
- "logs": {
- "name": "logs",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": true,
- "default": "'[]'::jsonb"
- }
- },
- "indexes": {},
- "foreignKeys": {},
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_records": {
- "name": "upload_records",
- "schema": "",
- "columns": {
- "session_id": {
- "name": "session_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "record_id": {
- "name": "record_id",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "type": {
- "name": "type",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "data": {
- "name": "data",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "private": {
- "name": "private",
- "type": "boolean",
- "primaryKey": false,
- "notNull": false,
- "default": false
- },
- "operation": {
- "name": "operation",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_records_session_id_upload_sessions_id_fk": {
- "name": "upload_records_session_id_upload_sessions_id_fk",
- "tableFrom": "upload_records",
- "tableTo": "upload_sessions",
- "columnsFrom": ["session_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "upload_records_session_id_record_id_pk": {
- "name": "upload_records_session_id_record_id_pk",
- "columns": ["session_id", "record_id"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.upload_sessions": {
- "name": "upload_sessions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "uuid",
- "primaryKey": true,
- "notNull": true,
- "default": "gen_random_uuid()"
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "account_id": {
- "name": "account_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "base_version": {
- "name": "base_version",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "schemas": {
- "name": "schemas",
- "type": "jsonb",
- "primaryKey": false,
- "notNull": false
- },
- "status": {
- "name": "status",
- "type": "text",
- "primaryKey": false,
- "notNull": true,
- "default": "'open'"
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true,
- "default": 0
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- },
- "expires_at": {
- "name": "expires_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {},
- "foreignKeys": {
- "upload_sessions_collection_id_collections_id_fk": {
- "name": "upload_sessions_collection_id_collections_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "upload_sessions_account_id_accounts_id_fk": {
- "name": "upload_sessions_account_id_accounts_id_fk",
- "tableFrom": "upload_sessions",
- "tableTo": "accounts",
- "columnsFrom": ["account_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_files": {
- "name": "version_files",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "file_hash": {
- "name": "file_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_files_file_hash_idx": {
- "name": "version_files_file_hash_idx",
- "columns": [
- {
- "expression": "file_hash",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_files_version_id_versions_id_fk": {
- "name": "version_files_version_id_versions_id_fk",
- "tableFrom": "version_files",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_files_file_hash_files_hash_fk": {
- "name": "version_files_file_hash_files_hash_fk",
- "tableFrom": "version_files",
- "tableTo": "files",
- "columnsFrom": ["file_hash"],
- "columnsTo": ["hash"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_files_version_id_file_hash_pk": {
- "name": "version_files_version_id_file_hash_pk",
- "columns": ["version_id", "file_hash"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.version_schemas": {
- "name": "version_schemas",
- "schema": "",
- "columns": {
- "version_id": {
- "name": "version_id",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "slug": {
- "name": "slug",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "schema_id": {
- "name": "schema_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- }
- },
- "indexes": {
- "version_schemas_schema_id_idx": {
- "name": "version_schemas_schema_id_idx",
- "columns": [
- {
- "expression": "schema_id",
- "isExpression": false,
- "asc": true,
- "nulls": "last"
- }
- ],
- "isUnique": false,
- "concurrently": false,
- "method": "btree",
- "with": {}
- }
- },
- "foreignKeys": {
- "version_schemas_version_id_versions_id_fk": {
- "name": "version_schemas_version_id_versions_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "versions",
- "columnsFrom": ["version_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "version_schemas_schema_id_schemas_id_fk": {
- "name": "version_schemas_schema_id_schemas_id_fk",
- "tableFrom": "version_schemas",
- "tableTo": "schemas",
- "columnsFrom": ["schema_id"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {
- "version_schemas_version_id_slug_pk": {
- "name": "version_schemas_version_id_slug_pk",
- "columns": ["version_id", "slug"]
- }
- },
- "uniqueConstraints": {},
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- },
- "public.versions": {
- "name": "versions",
- "schema": "",
- "columns": {
- "id": {
- "name": "id",
- "type": "bigserial",
- "primaryKey": true,
- "notNull": true
- },
- "collection_id": {
- "name": "collection_id",
- "type": "uuid",
- "primaryKey": false,
- "notNull": true
- },
- "number": {
- "name": "number",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "semver": {
- "name": "semver",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "hash": {
- "name": "hash",
- "type": "text",
- "primaryKey": false,
- "notNull": true
- },
- "public_hash": {
- "name": "public_hash",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "base_number": {
- "name": "base_number",
- "type": "integer",
- "primaryKey": false,
- "notNull": false
- },
- "message": {
- "name": "message",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "readme": {
- "name": "readme",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "pushed_by": {
- "name": "pushed_by",
- "type": "uuid",
- "primaryKey": false,
- "notNull": false
- },
- "app_id": {
- "name": "app_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "actor_id": {
- "name": "actor_id",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "signature": {
- "name": "signature",
- "type": "text",
- "primaryKey": false,
- "notNull": false
- },
- "record_count": {
- "name": "record_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "file_count": {
- "name": "file_count",
- "type": "integer",
- "primaryKey": false,
- "notNull": true
- },
- "total_bytes": {
- "name": "total_bytes",
- "type": "bigint",
- "primaryKey": false,
- "notNull": true
- },
- "created_at": {
- "name": "created_at",
- "type": "timestamp with time zone",
- "primaryKey": false,
- "notNull": true,
- "default": "now()"
- }
- },
- "indexes": {},
- "foreignKeys": {
- "versions_collection_id_collections_id_fk": {
- "name": "versions_collection_id_collections_id_fk",
- "tableFrom": "versions",
- "tableTo": "collections",
- "columnsFrom": ["collection_id"],
- "columnsTo": ["id"],
- "onDelete": "cascade",
- "onUpdate": "no action"
- },
- "versions_pushed_by_accounts_id_fk": {
- "name": "versions_pushed_by_accounts_id_fk",
- "tableFrom": "versions",
- "tableTo": "accounts",
- "columnsFrom": ["pushed_by"],
- "columnsTo": ["id"],
- "onDelete": "no action",
- "onUpdate": "no action"
- }
- },
- "compositePrimaryKeys": {},
- "uniqueConstraints": {
- "versions_collection_id_number_unique": {
- "name": "versions_collection_id_number_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "number"]
- },
- "versions_collection_id_hash_unique": {
- "name": "versions_collection_id_hash_unique",
- "nullsNotDistinct": false,
- "columns": ["collection_id", "hash"]
- }
- },
- "policies": {},
- "checkConstraints": {},
- "isRLSEnabled": false
- }
- },
- "enums": {},
- "schemas": {},
- "sequences": {},
- "roles": {},
- "policies": {},
- "views": {},
- "_meta": {
- "columns": {},
- "schemas": {},
- "tables": {}
- }
-}
diff --git a/src/db/migrations/meta/_journal.json b/src/db/migrations/meta/_journal.json
index 7263189..cf4c535 100644
--- a/src/db/migrations/meta/_journal.json
+++ b/src/db/migrations/meta/_journal.json
@@ -5,85 +5,8 @@
{
"idx": 0,
"version": "7",
- "when": 1777578810241,
- "tag": "0000_military_machine_man",
- "breakpoints": true
- },
- {
- "idx": 1,
- "version": "7",
- "when": 1777584805442,
- "tag": "0001_daffy_agent_zero",
- "breakpoints": true
- },
- {
- "idx": 2,
- "version": "7",
- "when": 1777605287163,
- "tag": "0002_harsh_madrox",
- "breakpoints": true
- },
- {
- "idx": 3,
- "version": "7",
- "when": 1777606816957,
- "tag": "0003_cool_leper_queen",
- "breakpoints": true
- },
- {
- "idx": 4,
- "version": "7",
- "when": 1777691764763,
- "tag": "0004_powerful_marvex",
- "breakpoints": true
- },
- {
- "idx": 5,
- "version": "7",
- "when": 1777695250467,
- "tag": "0005_smiling_baron_zemo",
- "breakpoints": true
- },
- {
- "idx": 6,
- "version": "7",
- "when": 1746388800000,
- "tag": "0006_ark_identifiers",
- "breakpoints": true
- },
- {
- "idx": 7,
- "version": "7",
- "when": 1778791722837,
- "tag": "0007_kf-auth-integration",
- "breakpoints": true
- },
- {
- "idx": 8,
- "version": "7",
- "when": 1778806667338,
- "tag": "0008_sudden_cyclops",
- "breakpoints": true
- },
- {
- "idx": 9,
- "version": "7",
- "when": 1778809182465,
- "tag": "0009_salty_colossus",
- "breakpoints": true
- },
- {
- "idx": 10,
- "version": "7",
- "when": 1778810604741,
- "tag": "0010_keen_meltdown",
- "breakpoints": true
- },
- {
- "idx": 11,
- "version": "7",
- "when": 1778812081690,
- "tag": "0011_messy_molten_man",
+ "when": 1779936029364,
+ "tag": "0000_harsh_white_tiger",
"breakpoints": true
}
]
diff --git a/src/db/schema.ts b/src/db/schema.ts
index 5e0ab2c..597ab07 100644
--- a/src/db/schema.ts
+++ b/src/db/schema.ts
@@ -10,70 +10,186 @@ import {
text,
timestamp,
unique,
+ uniqueIndex,
uuid,
} from 'drizzle-orm/pg-core'
-// --- Accounts ---
+// --- Better-auth managed tables ---
-export const accounts = pgTable('accounts', {
- // For user accounts: id = KF Auth user.id (set on OIDC callback).
- // For org accounts: id = auto-generated UUID.
- id: uuid('id').defaultRandom().primaryKey(),
- slug: text('slug').unique().notNull(),
- type: text('type', { enum: ['user', 'org'] }).notNull(),
- // displayName/avatarUrl: stored for org accounts only.
- // For user accounts, name + avatar are fetched from KF Auth on demand.
- displayName: text('display_name'),
- bio: text('bio'),
- website: text('website'),
- location: text('location'),
- avatarUrl: text('avatar_url'),
- notificationPrefs: jsonb('notification_prefs'),
- arkNaan: text('ark_naan'),
- // Links this account to a KF Organization. NOT unique — multiple UL orgs can belong to the same KF org.
- kfOrgId: text('kf_org_id'),
+export const user = pgTable('user', {
+ id: text('id').primaryKey(),
+ name: text('name').notNull(),
+ email: text('email').notNull().unique(),
+ emailVerified: boolean('email_verified').default(false).notNull(),
+ image: text('image'),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { withTimezone: true })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
})
-export const orgMemberships = pgTable(
- 'org_memberships',
+export const session = pgTable(
+ 'session',
{
- orgId: uuid('org_id')
+ id: text('id').primaryKey(),
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
+ token: text('token').notNull().unique(),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { withTimezone: true })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
+ ipAddress: text('ip_address'),
+ userAgent: text('user_agent'),
+ userId: text('user_id')
.notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- userId: uuid('user_id')
+ .references(() => user.id, { onDelete: 'cascade' }),
+ activeOrganizationId: text('active_organization_id'),
+ },
+ (t) => [index('session_user_id_idx').on(t.userId)],
+)
+
+export const account = pgTable(
+ 'account',
+ {
+ id: text('id').primaryKey(),
+ accountId: text('account_id').notNull(),
+ providerId: text('provider_id').notNull(),
+ userId: text('user_id')
.notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- role: text('role', { enum: ['owner', 'admin', 'member'] }).notNull(),
+ .references(() => user.id, { onDelete: 'cascade' }),
+ accessToken: text('access_token'),
+ refreshToken: text('refresh_token'),
+ idToken: text('id_token'),
+ accessTokenExpiresAt: timestamp('access_token_expires_at', { withTimezone: true }),
+ refreshTokenExpiresAt: timestamp('refresh_token_expires_at', { withTimezone: true }),
+ scope: text('scope'),
+ password: text('password'),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { withTimezone: true })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
},
- (t) => [primaryKey({ columns: [t.orgId, t.userId] })],
+ (t) => [index('account_user_id_idx').on(t.userId)],
)
-export const sessions = pgTable('sessions', {
- id: uuid('id').defaultRandom().primaryKey(),
- userId: uuid('user_id')
- .notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- userAgent: text('user_agent'),
- ipAddress: text('ip_address'),
- createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
- expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
-})
+export const verification = pgTable(
+ 'verification',
+ {
+ id: text('id').primaryKey(),
+ identifier: text('identifier').notNull(),
+ value: text('value').notNull(),
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { withTimezone: true })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
+ },
+ (t) => [index('verification_identifier_idx').on(t.identifier)],
+)
-export const apiKeys = pgTable('api_keys', {
- id: uuid('id').defaultRandom().primaryKey(),
- accountId: uuid('account_id')
- .notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- collectionId: uuid('collection_id').references(() => collections.id, { onDelete: 'cascade' }),
- scope: text('scope', { enum: ['read', 'write', 'admin'] }).notNull(),
- keyHash: text('key_hash').notNull(),
- keyPrefix: text('key_prefix'),
- label: text('label').notNull(),
- expiresAt: timestamp('expires_at', { withTimezone: true }),
- createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
- lastUsedAt: timestamp('last_used_at', { withTimezone: true }),
-})
+// --- Organization (better-auth managed + custom fields) ---
+
+export const organization = pgTable(
+ 'organization',
+ {
+ id: text('id').primaryKey(),
+ name: text('name').notNull(),
+ slug: text('slug').notNull().unique(),
+ logo: text('logo'),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ metadata: text('metadata'),
+ bio: text('bio'),
+ website: text('website'),
+ avatarUrl: text('avatar_url'),
+ arkNaan: text('ark_naan'),
+ kfOrgId: text('kf_org_id'),
+ isDefault: boolean('is_default').default(false),
+ },
+ (t) => [uniqueIndex('organization_slug_uidx').on(t.slug)],
+)
+
+export const member = pgTable(
+ 'member',
+ {
+ id: text('id').primaryKey(),
+ organizationId: text('organization_id')
+ .notNull()
+ .references(() => organization.id, { onDelete: 'cascade' }),
+ userId: text('user_id')
+ .notNull()
+ .references(() => user.id, { onDelete: 'cascade' }),
+ role: text('role').default('member').notNull(),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ },
+ (t) => [
+ index('member_organization_id_idx').on(t.organizationId),
+ index('member_user_id_idx').on(t.userId),
+ ],
+)
+
+export const invitation = pgTable(
+ 'invitation',
+ {
+ id: text('id').primaryKey(),
+ organizationId: text('organization_id')
+ .notNull()
+ .references(() => organization.id, { onDelete: 'cascade' }),
+ email: text('email').notNull(),
+ role: text('role'),
+ status: text('status').default('pending').notNull(),
+ expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ inviterId: text('inviter_id')
+ .notNull()
+ .references(() => user.id, { onDelete: 'cascade' }),
+ },
+ (t) => [
+ index('invitation_organization_id_idx').on(t.organizationId),
+ index('invitation_email_idx').on(t.email),
+ ],
+)
+
+// --- API Keys (better-auth managed) ---
+
+export const apikey = pgTable(
+ 'apikey',
+ {
+ id: text('id').primaryKey(),
+ configId: text('config_id').default('default').notNull(),
+ name: text('name'),
+ start: text('start'),
+ referenceId: text('reference_id').notNull(),
+ prefix: text('prefix'),
+ key: text('key').notNull(),
+ refillInterval: integer('refill_interval'),
+ refillAmount: integer('refill_amount'),
+ lastRefillAt: timestamp('last_refill_at', { withTimezone: true }),
+ enabled: boolean('enabled').default(true),
+ rateLimitEnabled: boolean('rate_limit_enabled').default(true),
+ rateLimitTimeWindow: integer('rate_limit_time_window').default(86400000),
+ rateLimitMax: integer('rate_limit_max').default(10),
+ requestCount: integer('request_count').default(0),
+ remaining: integer('remaining'),
+ lastRequest: timestamp('last_request', { withTimezone: true }),
+ expiresAt: timestamp('expires_at', { withTimezone: true }),
+ createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
+ updatedAt: timestamp('updated_at', { withTimezone: true })
+ .defaultNow()
+ .$onUpdate(() => new Date())
+ .notNull(),
+ permissions: text('permissions'),
+ metadata: text('metadata'),
+ },
+ (t) => [
+ index('apikey_config_id_idx').on(t.configId),
+ index('apikey_reference_id_idx').on(t.referenceId),
+ index('apikey_key_idx').on(t.key),
+ ],
+)
// --- Collections ---
@@ -81,9 +197,9 @@ export const collections = pgTable(
'collections',
{
id: uuid('id').defaultRandom().primaryKey(),
- accountId: uuid('account_id')
+ organizationId: text('organization_id')
.notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
+ .references(() => organization.id, { onDelete: 'cascade' }),
slug: text('slug').notNull(),
name: text('name').notNull(),
description: text('description'),
@@ -92,7 +208,10 @@ export const collections = pgTable(
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull(),
},
- (t) => [unique().on(t.accountId, t.slug), index('collections_account_id_idx').on(t.accountId)],
+ (t) => [
+ unique().on(t.organizationId, t.slug),
+ index('collections_organization_id_idx').on(t.organizationId),
+ ],
)
// --- Versions ---
@@ -111,7 +230,7 @@ export const versions = pgTable(
baseNumber: integer('base_number'),
message: text('message'),
readme: text('readme'),
- pushedBy: uuid('pushed_by').references(() => accounts.id),
+ pushedBy: text('pushed_by').references(() => user.id),
appId: text('app_id'),
actorId: text('actor_id'),
signature: text('signature'),
@@ -209,24 +328,6 @@ export const schemaLabels = pgTable(
(t) => [unique().on(t.schemaId, t.label), index('schema_labels_label_idx').on(t.label)],
)
-// --- Org Invitations ---
-
-export const orgInvitations = pgTable('org_invitations', {
- id: uuid('id').defaultRandom().primaryKey(),
- orgId: uuid('org_id')
- .notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- email: text('email').notNull(),
- role: text('role', { enum: ['owner', 'admin', 'member'] }).notNull(),
- invitedBy: uuid('invited_by')
- .notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
- token: text('token').notNull().unique(),
- expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
- acceptedAt: timestamp('accepted_at', { withTimezone: true }),
- createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
-})
-
// --- Upload Sessions (chunked push) ---
export const uploadSessions = pgTable('upload_sessions', {
@@ -234,9 +335,9 @@ export const uploadSessions = pgTable('upload_sessions', {
collectionId: uuid('collection_id')
.notNull()
.references(() => collections.id, { onDelete: 'cascade' }),
- accountId: uuid('account_id')
+ userId: text('user_id')
.notNull()
- .references(() => accounts.id, { onDelete: 'cascade' }),
+ .references(() => user.id, { onDelete: 'cascade' }),
baseVersion: integer('base_version'),
message: text('message'),
readme: text('readme'),
@@ -288,10 +389,10 @@ export const syncRuns = pgTable('sync_runs', {
export const arkShoulders = pgTable('ark_shoulders', {
id: uuid('id').defaultRandom().primaryKey(),
- accountId: uuid('account_id')
+ organizationId: text('organization_id')
.notNull()
.unique()
- .references(() => accounts.id, { onDelete: 'cascade' }),
+ .references(() => organization.id, { onDelete: 'cascade' }),
shoulder: text('shoulder').notNull().unique(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
})
diff --git a/src/db/seed.ts b/src/db/seed.ts
index 4f517d2..839b085 100644
--- a/src/db/seed.ts
+++ b/src/db/seed.ts
@@ -61,7 +61,7 @@ async function seed() {
const force = process.argv.includes('--force')
console.log('[seed] Seeding database...')
- const existing = await db.select().from(schema.accounts).limit(1)
+ const existing = await db.select().from(schema.organization).limit(1)
if (existing.length > 0 && !force) {
console.log('[seed] Database already seeded, skipping. Use --force to re-seed.')
process.exit(0)
@@ -77,42 +77,60 @@ async function seed() {
await db.delete(schema.schemas)
await db.delete(schema.versions)
await db.delete(schema.collections)
- await db.delete(schema.apiKeys)
- await db.delete(schema.sessions)
- await db.delete(schema.orgMemberships)
- await db.delete(schema.accounts)
+ await db.delete(schema.member)
+ await db.delete(schema.organization)
+ await db.delete(schema.session)
+ await db.delete(schema.account)
+ await db.delete(schema.user)
}
- const adminId = uuidv4()
+ const adminId = 'seed-admin-user'
- await db.insert(schema.accounts).values({
+ await db.insert(schema.user).values({
id: adminId,
+ name: 'Admin',
+ email: 'admin@underlay.org',
+ emailVerified: true,
+ })
+
+ const adminOrgId = uuidv4()
+ await db.insert(schema.organization).values({
+ id: adminOrgId,
slug: 'admin',
- type: 'user',
+ name: 'Admin',
+ isDefault: true,
+ })
+
+ await db.insert(schema.member).values({
+ id: crypto.randomUUID(),
+ organizationId: adminOrgId,
+ userId: adminId,
+ role: 'owner',
})
const kfId = uuidv4()
- await db.insert(schema.accounts).values({
+ await db.insert(schema.organization).values({
id: kfId,
slug: 'knowledge-futures',
- type: 'org',
- displayName: 'Knowledge Futures',
+ name: 'Knowledge Futures',
+ isDefault: false,
})
- await db.insert(schema.orgMemberships).values({
- orgId: kfId,
+ await db.insert(schema.member).values({
+ id: crypto.randomUUID(),
+ organizationId: kfId,
userId: adminId,
role: 'owner',
})
- console.log('[seed] Created admin user (admin@underlay.org / admin)')
+ console.log('[seed] Created admin user + default org')
console.log('[seed] Created Knowledge Futures org')
// --- Collection 1: PubPub Archive ---
const pubpubId = uuidv4()
await db.insert(schema.collections).values({
id: pubpubId,
- accountId: kfId,
+ organizationId: kfId,
slug: 'pubpub-archive',
name: 'PubPub Archive',
description:
@@ -358,7 +376,7 @@ async function seed() {
const grantsId = uuidv4()
await db.insert(schema.collections).values({
id: grantsId,
- accountId: kfId,
+ organizationId: kfId,
slug: 'open-grants',
name: 'Open Grants Dataset',
description:
@@ -547,7 +565,7 @@ async function seed() {
const climateId = uuidv4()
await db.insert(schema.collections).values({
id: climateId,
- accountId: kfId,
+ organizationId: kfId,
slug: 'climate-observations',
name: 'Global Climate Observations',
description:
@@ -775,7 +793,7 @@ async function seed() {
const pubnotesId = uuidv4()
await db.insert(schema.collections).values({
id: pubnotesId,
- accountId: kfId,
+ organizationId: kfId,
slug: 'pub-notes',
name: 'Pub Notes',
description:
diff --git a/src/lib/ark.ts b/src/lib/ark.ts
index 55c05f3..d3c961b 100644
--- a/src/lib/ark.ts
+++ b/src/lib/ark.ts
@@ -56,11 +56,11 @@ export function nextShoulderCounter(count: number): string {
return result
}
-export async function getOrMintShoulder(accountId: string): Promise {
+export async function getOrMintShoulder(organizationId: string): Promise {
const [existing] = await db
.select({ shoulder: schema.arkShoulders.shoulder })
.from(schema.arkShoulders)
- .where(eq(schema.arkShoulders.accountId, accountId))
+ .where(eq(schema.arkShoulders.organizationId, organizationId))
.limit(1)
if (existing) return existing.shoulder
@@ -72,7 +72,7 @@ export async function getOrMintShoulder(accountId: string): Promise {
const digit = Math.floor(Math.random() * 10).toString()
const shoulder = `ul${counter}${digit}`
try {
- await db.insert(schema.arkShoulders).values({ accountId, shoulder })
+ await db.insert(schema.arkShoulders).values({ organizationId, shoulder })
return shoulder
} catch (e: any) {
// Retry on unique constraint violation (concurrent insert)
diff --git a/src/lib/auth-client.ts b/src/lib/auth-client.ts
new file mode 100644
index 0000000..1711ba3
--- /dev/null
+++ b/src/lib/auth-client.ts
@@ -0,0 +1,9 @@
+import { apiKeyClient } from '@better-auth/api-key/client'
+import { genericOAuthClient } from 'better-auth/client/plugins'
+import { organizationClient } from 'better-auth/client/plugins'
+import { createAuthClient } from 'better-auth/react'
+
+export const authClient = createAuthClient({
+ baseURL: typeof window !== 'undefined' ? window.location.origin : '',
+ plugins: [genericOAuthClient(), organizationClient(), apiKeyClient()],
+})
diff --git a/src/lib/auth-internal.server.ts b/src/lib/auth-internal.server.ts
index 9833fba..4003985 100644
--- a/src/lib/auth-internal.server.ts
+++ b/src/lib/auth-internal.server.ts
@@ -9,8 +9,7 @@
* AUTH_INTERNAL_API_KEY — shared secret for service-to-service calls
*/
-import { OIDC_ISSUER_INTERNAL_URL } from './oidc.server.js'
-
+const OIDC_ISSUER_INTERNAL_URL = process.env.OIDC_ISSUER_INTERNAL_URL ?? 'http://localhost:3000'
const AUTH_INTERNAL_API_URL = process.env.AUTH_INTERNAL_API_URL ?? OIDC_ISSUER_INTERNAL_URL
const AUTH_INTERNAL_API_KEY = process.env.AUTH_INTERNAL_API_KEY ?? ''
@@ -120,4 +119,4 @@ export async function getAuthUserWithEmail(
}
}
-export { AUTH_INTERNAL_API_URL, AUTH_INTERNAL_API_KEY }
+export { AUTH_INTERNAL_API_URL, AUTH_INTERNAL_API_KEY, OIDC_ISSUER_INTERNAL_URL }
diff --git a/src/lib/auth.server.ts b/src/lib/auth.server.ts
index dae0f06..631a2bf 100644
--- a/src/lib/auth.server.ts
+++ b/src/lib/auth.server.ts
@@ -1,101 +1,49 @@
import { eq } from 'drizzle-orm'
import { db, schema } from '../db/client.server.js'
-import { getKfProfile } from './kf-profile-cache.server.js'
+import { auth } from './auth.js'
export interface SessionUser {
id: string
- slug: string
displayName: string | null
- type: string
- bio: string | null
avatarUrl: string | null
+ defaultOrg: { slug: string; displayName: string | null } | null
orgs: Array<{ slug: string; displayName: string | null; role: string }>
}
-/**
- * Extract the session cookie from a Request object and look up the user.
- * Returns the user data or null if not authenticated.
- *
- * For user accounts, displayName and avatarUrl are fetched from KF Auth
- * (via in-memory cache with 5-min TTL). For org accounts, they come from
- * the local DB.
- */
export async function getSessionUser(request: Request): Promise {
- const cookieHeader = request.headers.get('cookie')
- if (!cookieHeader) return null
-
- // Parse session cookie
- const cookies = Object.fromEntries(
- cookieHeader.split(';').map((c) => {
- const [key, ...rest] = c.trim().split('=')
- return [key, rest.join('=')] as [string, string]
- }),
- )
-
- let sessionId = cookies['session']
- if (!sessionId) return null
-
- // Strip signature if present (legacy signed cookies)
- const dotIdx = sessionId.lastIndexOf('.')
- if (dotIdx > 0) {
- sessionId = sessionId.slice(0, dotIdx)
- }
-
- // Look up session
- const [session] = await db
- .select()
- .from(schema.sessions)
- .where(eq(schema.sessions.id, sessionId))
- .limit(1)
-
- if (!session || new Date(session.expiresAt) <= new Date()) {
+ let session: Awaited>
+ try {
+ session = await auth.api.getSession({ headers: request.headers })
+ } catch {
return null
}
+ if (!session) return null
- // Look up user
- const [user] = await db
- .select()
- .from(schema.accounts)
- .where(eq(schema.accounts.id, session.userId))
- .limit(1)
+ const u = session.user
- if (!user) return null
-
- // For user accounts, fetch profile from KF Auth (name + image).
- // For org accounts, use locally stored displayName + avatarUrl.
- let displayName = user.displayName
- let avatarUrl = user.avatarUrl
-
- if (user.type === 'user') {
- const profile = await getKfProfile(user.id)
- if (profile) {
- displayName = profile.name
- avatarUrl = profile.image
- }
- }
-
- // Look up org memberships
const memberships = await db
.select({
- orgSlug: schema.accounts.slug,
- orgDisplayName: schema.accounts.displayName,
- role: schema.orgMemberships.role,
+ orgId: schema.organization.id,
+ orgSlug: schema.organization.slug,
+ orgName: schema.organization.name,
+ isDefault: schema.organization.isDefault,
+ role: schema.member.role,
})
- .from(schema.orgMemberships)
- .innerJoin(schema.accounts, eq(schema.orgMemberships.orgId, schema.accounts.id))
- .where(eq(schema.orgMemberships.userId, user.id))
+ .from(schema.member)
+ .innerJoin(schema.organization, eq(schema.member.organizationId, schema.organization.id))
+ .where(eq(schema.member.userId, u.id))
+
+ const defaultOrg = memberships.find((m) => m.isDefault) ?? null
return {
- id: user.id,
- slug: user.slug,
- displayName,
- type: user.type,
- bio: user.bio,
- avatarUrl,
+ id: u.id,
+ displayName: defaultOrg?.orgName ?? u.name,
+ avatarUrl: u.image ?? null,
+ defaultOrg: defaultOrg ? { slug: defaultOrg.orgSlug, displayName: defaultOrg.orgName } : null,
orgs: memberships.map((m) => ({
slug: m.orgSlug,
- displayName: m.orgDisplayName,
+ displayName: m.orgName,
role: m.role,
})),
}
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
new file mode 100644
index 0000000..f16d1b4
--- /dev/null
+++ b/src/lib/auth.ts
@@ -0,0 +1,105 @@
+import { apiKey } from '@better-auth/api-key'
+import { betterAuth } from 'better-auth'
+import { drizzleAdapter } from 'better-auth/adapters/drizzle'
+import { genericOAuth } from 'better-auth/plugins'
+import { organization } from 'better-auth/plugins/organization'
+import { eq } from 'drizzle-orm'
+
+import { db, schema } from '../db/client.server.js'
+
+const KF_AUTH_URL = process.env.OIDC_ISSUER_URL ?? 'http://localhost:3000'
+const KF_AUTH_INTERNAL_URL = process.env.OIDC_ISSUER_INTERNAL_URL ?? KF_AUTH_URL
+
+export const auth = betterAuth({
+ database: drizzleAdapter(db, { provider: 'pg' }),
+ baseURL: process.env.APP_URL ?? 'http://localhost:4100',
+ basePath: '/api/auth',
+ secret: process.env.SESSION_SECRET ?? 'dev-secret-change-me',
+
+ advanced: {
+ database: {
+ generateId: () => crypto.randomUUID(),
+ },
+ },
+
+ plugins: [
+ genericOAuth({
+ config: [
+ {
+ providerId: 'kf-auth',
+ authorizationUrl: `${KF_AUTH_URL}/api/auth/oauth2/authorize`,
+ tokenUrl: `${KF_AUTH_INTERNAL_URL}/api/auth/oauth2/token`,
+ userInfoUrl: `${KF_AUTH_INTERNAL_URL}/api/auth/oauth2/userinfo`,
+ clientId: process.env.OIDC_CLIENT_ID ?? 'kf_underlay',
+ clientSecret: process.env.OIDC_CLIENT_SECRET ?? '',
+ scopes: ['openid', 'profile', 'email'],
+ pkce: true,
+ mapProfileToUser: (profile) => ({
+ name: profile.name ?? profile.email?.split('@')[0] ?? 'User',
+ email: profile.email,
+ image: profile.picture ?? null,
+ }),
+ },
+ ],
+ }),
+ organization({
+ schema: {
+ organization: {
+ additionalFields: {
+ bio: { type: 'string', required: false, input: true },
+ website: { type: 'string', required: false, input: true },
+ avatarUrl: { type: 'string', required: false, input: true },
+ arkNaan: { type: 'string', required: false, input: true },
+ kfOrgId: { type: 'string', required: false, input: true },
+ isDefault: { type: 'boolean', required: false, input: true, defaultValue: false },
+ },
+ },
+ },
+ }),
+ apiKey(),
+ ],
+
+ databaseHooks: {
+ user: {
+ create: {
+ after: async (user) => {
+ const baseSlug = (user.email.split('@')[0] ?? 'user')
+ .toLowerCase()
+ .replace(/[^a-z0-9-]/g, '-')
+ .replace(/-+/g, '-')
+ .slice(0, 30)
+
+ let slug = baseSlug
+ let attempt = 0
+ while (true) {
+ const [conflict] = await db
+ .select({ id: schema.organization.id })
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, slug))
+ .limit(1)
+ if (!conflict) break
+ attempt++
+ slug = `${baseSlug}-${attempt}`
+ }
+
+ const orgId = crypto.randomUUID()
+ await db.insert(schema.organization).values({
+ id: orgId,
+ name: user.name,
+ slug,
+ isDefault: true,
+ })
+
+ await db.insert(schema.member).values({
+ id: crypto.randomUUID(),
+ organizationId: orgId,
+ userId: user.id,
+ role: 'owner',
+ })
+ },
+ },
+ },
+ },
+})
+
+export { KF_AUTH_URL, KF_AUTH_INTERNAL_URL }
diff --git a/src/lib/kf-auth.server.ts b/src/lib/kf-auth.server.ts
deleted file mode 100644
index 95e2340..0000000
--- a/src/lib/kf-auth.server.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * Legacy re-export shim.
- * New code should import from './oidc.server.js' and './auth-internal.server.js' directly.
- */
-
-export {
- buildAuthorizeUrl,
- exchangeCode,
- fetchUserInfo,
- extractOrgs,
- OIDC_ISSUER_URL,
- OIDC_CLIENT_ID,
- REDIRECT_URI,
- type OIDCOrg as KFOrg,
- type OIDCUserInfo as KFUserInfo,
- initOidc,
-} from './oidc.server.js'
diff --git a/src/lib/kf-orgs.server.ts b/src/lib/kf-orgs.server.ts
deleted file mode 100644
index 9498c9e..0000000
--- a/src/lib/kf-orgs.server.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * Legacy re-export shim.
- * New code should import from './auth-internal.server.js' directly.
- */
-
-export { fetchAuthOrgs as fetchKfOrgs, type AuthOrg as KFOrg } from './auth-internal.server.js'
diff --git a/src/lib/kf-profile-cache.server.ts b/src/lib/kf-profile-cache.server.ts
deleted file mode 100644
index c4e8041..0000000
--- a/src/lib/kf-profile-cache.server.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * Legacy re-export shim.
- * New code should import from './auth-internal.server.js' directly.
- */
-
-export {
- getAuthProfile as getKfProfile,
- type AuthProfile as KFProfile,
-} from './auth-internal.server.js'
diff --git a/src/lib/mirror-sync.ts b/src/lib/mirror-sync.ts
index a561dd7..e3ecc7a 100644
--- a/src/lib/mirror-sync.ts
+++ b/src/lib/mirror-sync.ts
@@ -95,24 +95,24 @@ export async function cleanupStaleRuns(): Promise {
return rows.length
}
-/** Ensure a system account exists for mirrored content */
-async function ensureMirrorAccount(ownerSlug: string): Promise {
+/** Ensure a system org exists for mirrored content */
+async function ensureMirrorOrg(ownerSlug: string): Promise {
const [existing] = await db
- .select({ id: schema.accounts.id })
- .from(schema.accounts)
- .where(eq(schema.accounts.slug, ownerSlug))
+ .select({ id: schema.organization.id })
+ .from(schema.organization)
+ .where(eq(schema.organization.slug, ownerSlug))
.limit(1)
if (existing) return existing.id
const [created] = await db
- .insert(schema.accounts)
+ .insert(schema.organization)
.values({
+ id: crypto.randomUUID(),
slug: ownerSlug,
- type: 'org',
- displayName: ownerSlug,
+ name: ownerSlug,
})
- .returning({ id: schema.accounts.id })
+ .returning({ id: schema.organization.id })
return created!.id
}
@@ -417,15 +417,15 @@ async function syncCollection(
emit: (type: SyncProgressEvent['type'], message: string) => void,
signal: AbortSignal,
): Promise {
- // Ensure the owner account exists locally
- const accountId = await ensureMirrorAccount(uc.ownerSlug)
+ // Ensure the owner org exists locally
+ const organizationId = await ensureMirrorOrg(uc.ownerSlug)
// Check if collection exists locally
const [localColl] = await db
.select({ id: schema.collections.id })
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, uc.ownerSlug), eq(schema.collections.slug, uc.slug)))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
+ .where(and(eq(schema.organization.slug, uc.ownerSlug), eq(schema.collections.slug, uc.slug)))
.limit(1)
let collectionId: string
@@ -435,7 +435,7 @@ async function syncCollection(
const [created] = await db
.insert(schema.collections)
.values({
- accountId,
+ organizationId,
slug: uc.slug,
name: uc.name,
description: uc.description,
@@ -701,13 +701,13 @@ export async function getMirrorStatus(): Promise<{
const collections = await db
.select({
- ownerSlug: schema.accounts.slug,
+ ownerSlug: schema.organization.slug,
slug: schema.collections.slug,
name: schema.collections.name,
updatedAt: schema.collections.updatedAt,
})
.from(schema.collections)
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
+ .innerJoin(schema.organization, eq(schema.collections.organizationId, schema.organization.id))
.where(eq(schema.collections.public, true))
// Get latest version for each collection
@@ -717,8 +717,11 @@ export async function getMirrorStatus(): Promise<{
.select({ semver: schema.versions.semver })
.from(schema.versions)
.innerJoin(schema.collections, eq(schema.versions.collectionId, schema.collections.id))
- .innerJoin(schema.accounts, eq(schema.collections.accountId, schema.accounts.id))
- .where(and(eq(schema.accounts.slug, c.ownerSlug), eq(schema.collections.slug, c.slug)))
+ .innerJoin(
+ schema.organization,
+ eq(schema.collections.organizationId, schema.organization.id),
+ )
+ .where(and(eq(schema.organization.slug, c.ownerSlug), eq(schema.collections.slug, c.slug)))
.orderBy(sql`${schema.versions.number} desc`)
.limit(1)
diff --git a/src/lib/oidc.server.ts b/src/lib/oidc.server.ts
deleted file mode 100644
index b51343b..0000000
--- a/src/lib/oidc.server.ts
+++ /dev/null
@@ -1,228 +0,0 @@
-/**
- * Generic OIDC client with auto-discovery.
- *
- * Reads endpoints from the provider's .well-known/openid-configuration.
- * Works with any standards-compliant OIDC provider (KF Auth, Keycloak, Auth0, etc.).
- *
- * Env vars:
- * OIDC_ISSUER_URL — browser-facing issuer URL
- * OIDC_ISSUER_INTERNAL_URL — server-to-server URL for Docker (falls back to OIDC_ISSUER_URL)
- * OIDC_CLIENT_ID — OAuth client ID
- * OIDC_CLIENT_SECRET — OAuth client secret
- * OIDC_ORGS_CLAIM — custom claim key for org memberships (default: https://knowledgefutures.org/orgs)
- */
-
-import crypto from 'node:crypto'
-
-// --- Config (with backward-compat fallbacks) ---
-
-const OIDC_ISSUER_URL = process.env.OIDC_ISSUER_URL ?? 'http://localhost:3000'
-
-const OIDC_ISSUER_INTERNAL_URL = process.env.OIDC_ISSUER_INTERNAL_URL ?? OIDC_ISSUER_URL
-
-const OIDC_CLIENT_ID = process.env.OIDC_CLIENT_ID ?? 'kf_underlay'
-
-const OIDC_CLIENT_SECRET = process.env.OIDC_CLIENT_SECRET ?? ''
-
-const OIDC_ORGS_CLAIM = process.env.OIDC_ORGS_CLAIM ?? 'https://knowledgefutures.org/orgs'
-
-const APP_URL = process.env.APP_URL ?? 'http://localhost:4100'
-const REDIRECT_URI = `${APP_URL}/auth/callback`
-
-// --- OIDC Discovery ---
-
-interface OIDCDiscovery {
- issuer: string
- authorization_endpoint: string
- token_endpoint: string
- userinfo_endpoint: string
- jwks_uri?: string
-}
-
-let discoveryCache: OIDCDiscovery | null = null
-let discoveryPromise: Promise | null = null
-
-/**
- * Fetch and cache the OIDC discovery document.
- * Uses the internal URL for server-to-server fetch.
- * Throws if discovery fails — app should not start without valid OIDC config.
- */
-async function discover(): Promise {
- if (discoveryCache) return discoveryCache
- if (discoveryPromise) return discoveryPromise
-
- discoveryPromise = (async () => {
- const url = `${OIDC_ISSUER_INTERNAL_URL}/.well-known/openid-configuration`
- const res = await fetch(url)
- if (!res.ok) {
- throw new Error(
- `OIDC discovery failed: ${res.status} from ${url}. ` +
- `Ensure OIDC_ISSUER_URL points to a valid OIDC provider.`,
- )
- }
- const config = (await res.json()) as OIDCDiscovery
- discoveryCache = config
- return config
- })()
-
- return discoveryPromise
-}
-
-/**
- * Initialize OIDC — call at app startup to fail fast if provider is unreachable.
- */
-export async function initOidc(): Promise {
- await discover()
-}
-
-/**
- * Rewrite a discovered endpoint URL to use the internal host.
- * Discovery may return URLs with the public host (BETTER_AUTH_URL),
- * but server-to-server calls must use OIDC_ISSUER_INTERNAL_URL.
- */
-function internalEndpoint(discoveredUrl: string): string {
- const url = new URL(discoveredUrl)
- const base = new URL(OIDC_ISSUER_INTERNAL_URL)
- url.protocol = base.protocol
- url.host = base.host
- return url.toString()
-}
-
-// --- PKCE helpers ---
-
-/** Generate a random code_verifier (43–128 chars, URL-safe). */
-export function generateCodeVerifier(): string {
- return crypto.randomBytes(32).toString('base64url')
-}
-
-/** Derive the S256 code_challenge from a code_verifier. */
-export function generateCodeChallenge(verifier: string): string {
- return crypto.createHash('sha256').update(verifier).digest('base64url')
-}
-
-// --- OIDC Flows ---
-
-/**
- * Build the URL to redirect the user to for authentication.
- * Uses the discovered authorization_endpoint.
- * Returns the URL and the PKCE code_verifier (must be stored server-side).
- */
-export async function buildAuthorizeUrl(
- state: string,
-): Promise<{ url: string; codeVerifier: string }> {
- const config = await discover()
- const codeVerifier = generateCodeVerifier()
- const codeChallenge = generateCodeChallenge(codeVerifier)
-
- // Use the browser-facing issuer URL for authorize (user's browser navigates here)
- // Discovery may return an internal URL, so construct from OIDC_ISSUER_URL + path
- const authorizeUrl = new URL(config.authorization_endpoint)
- // Replace host with the browser-facing URL if discovery returned an internal one
- const browserBase = new URL(OIDC_ISSUER_URL)
- authorizeUrl.protocol = browserBase.protocol
- authorizeUrl.host = browserBase.host
-
- const params = new URLSearchParams({
- client_id: OIDC_CLIENT_ID,
- redirect_uri: REDIRECT_URI,
- response_type: 'code',
- scope: 'openid profile email',
- state,
- code_challenge: codeChallenge,
- code_challenge_method: 'S256',
- })
-
- return { url: `${authorizeUrl.toString()}?${params}`, codeVerifier }
-}
-
-export interface TokenResponse {
- access_token: string
- token_type: string
- expires_in: number
- id_token?: string
- refresh_token?: string
-}
-
-/**
- * Exchange an authorization code for tokens.
- * Uses the discovered token_endpoint (server-to-server).
- */
-export async function exchangeCode(code: string, codeVerifier: string): Promise {
- const config = await discover()
- const body = new URLSearchParams({
- grant_type: 'authorization_code',
- code,
- redirect_uri: REDIRECT_URI,
- client_id: OIDC_CLIENT_ID,
- client_secret: OIDC_CLIENT_SECRET,
- code_verifier: codeVerifier,
- })
-
- const res = await fetch(internalEndpoint(config.token_endpoint), {
- method: 'POST',
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- body,
- })
-
- if (!res.ok) {
- const text = await res.text()
- throw new Error(`Token exchange failed: ${res.status} ${text}`)
- }
-
- return res.json() as Promise
-}
-
-export interface OIDCOrg {
- id: string
- name: string
- slug: string
- type: 'personal' | 'shared'
- role: string
-}
-
-export interface OIDCUserInfo {
- sub: string
- name?: string
- email?: string
- picture?: string
- [key: string]: unknown
-}
-
-/**
- * Fetch user info from the OIDC provider using an access token.
- * Uses the discovered userinfo_endpoint (server-to-server).
- */
-export async function fetchUserInfo(accessToken: string): Promise {
- const config = await discover()
- const res = await fetch(internalEndpoint(config.userinfo_endpoint), {
- headers: { Authorization: `Bearer ${accessToken}` },
- })
-
- if (!res.ok) {
- throw new Error(`UserInfo failed: ${res.status}`)
- }
-
- return res.json() as Promise
-}
-
-/**
- * Extract org memberships from the userinfo response.
- * Uses the configurable OIDC_ORGS_CLAIM key.
- */
-export function extractOrgs(userInfo: OIDCUserInfo): OIDCOrg[] {
- const orgs = userInfo[OIDC_ORGS_CLAIM]
- if (Array.isArray(orgs)) return orgs as OIDCOrg[]
- return []
-}
-
-// --- Exports ---
-
-export {
- OIDC_ISSUER_URL,
- OIDC_ISSUER_INTERNAL_URL,
- OIDC_CLIENT_ID,
- OIDC_CLIENT_SECRET,
- OIDC_ORGS_CLAIM,
- APP_URL,
- REDIRECT_URI,
-}
diff --git a/src/routes/docs/integration.tsx b/src/routes/docs/integration.tsx
index 2279a8f..962b655 100644
--- a/src/routes/docs/integration.tsx
+++ b/src/routes/docs/integration.tsx
@@ -306,7 +306,8 @@ export default function DocsIntegration() {
Built by Knowledge Futures , a 501(c)(3)
- nonprofit. Contact: team@knowledgefutures.org
+ public charity. Contact:{' '}
+ team@knowledgefutures.org
)
diff --git a/src/routes/forgot-password.tsx b/src/routes/forgot-password.tsx
index 28fbb19..ae18a6a 100644
--- a/src/routes/forgot-password.tsx
+++ b/src/routes/forgot-password.tsx
@@ -5,7 +5,7 @@ import BaseLayout from '~/components/BaseLayout'
export default function ForgotPasswordPage() {
useEffect(() => {
// Password management now happens via KF Auth
- window.location.href = '/auth/login'
+ window.location.href = '/login'
}, [])
return (
diff --git a/src/routes/index.tsx b/src/routes/index.tsx
index 62f1ff0..22d6158 100644
--- a/src/routes/index.tsx
+++ b/src/routes/index.tsx
@@ -95,7 +95,7 @@ export default function Home() {
Built by Knowledge Futures
- A 501(c)(3) nonprofit building open infrastructure for knowledge sharing.
+ A 501(c)(3) public charity building open infrastructure for knowledge sharing.
@@ -226,7 +226,7 @@ GET /collections/:owner/:slug/export # full archive`}
Built by Knowledge Futures
- A 501(c)(3) nonprofit building open infrastructure for knowledge sharing.
+ A 501(c)(3) public charity building open infrastructure for knowledge sharing.
diff --git a/src/routes/invitations/accept.tsx b/src/routes/invitations/accept.tsx
index 071ddf1..861625e 100644
--- a/src/routes/invitations/accept.tsx
+++ b/src/routes/invitations/accept.tsx
@@ -67,7 +67,7 @@ export default function InvitationsAccept() {
You may need to{' '}
log in
@@ -92,7 +92,7 @@ export default function InvitationsAccept() {
Try again
diff --git a/src/routes/logout.tsx b/src/routes/logout.tsx
index f445e57..404493f 100644
--- a/src/routes/logout.tsx
+++ b/src/routes/logout.tsx
@@ -1,15 +1,14 @@
import { useEffect } from 'react'
import BaseLayout from '~/components/BaseLayout'
+import { authClient } from '~/lib/auth-client'
import { useSSRData } from '~/lib/ssr-data'
export default function LogoutPage() {
const kfAuthUrl = useSSRData('kfAuthUrl')
useEffect(() => {
- // Clear the local Underlay session, then redirect to KF Auth signout
- // so the IdP session is also cleared (prevents auto-re-login)
- fetch('/auth/logout', { method: 'POST', credentials: 'include' }).finally(() => {
+ authClient.signOut().then(() => {
const appHomeUrl = window.location.origin
const signoutUrl = kfAuthUrl
? `${kfAuthUrl}/auth/signout?redirect_uri=${encodeURIComponent(appHomeUrl)}`
diff --git a/src/routes/query.tsx b/src/routes/query.tsx
index db529c3..5179f0d 100644
--- a/src/routes/query.tsx
+++ b/src/routes/query.tsx
@@ -39,7 +39,7 @@ export default function QueryPage() {
orgs={currentUser.orgs ?? []}
/>
) : (
-
+
Log in
)}
diff --git a/src/routes/reset-password.tsx b/src/routes/reset-password.tsx
index 1ba3a63..7338c4e 100644
--- a/src/routes/reset-password.tsx
+++ b/src/routes/reset-password.tsx
@@ -4,7 +4,7 @@ import BaseLayout from '~/components/BaseLayout'
export default function ResetPasswordPage() {
useEffect(() => {
- window.location.href = '/auth/login'
+ window.location.href = '/login'
}, [])
return (
diff --git a/src/routes/signup.tsx b/src/routes/signup.tsx
index e213e46..62a8953 100644
--- a/src/routes/signup.tsx
+++ b/src/routes/signup.tsx
@@ -5,7 +5,7 @@ import BaseLayout from '~/components/BaseLayout'
export default function SignupPage() {
useEffect(() => {
// Account creation now happens via KF Auth
- window.location.href = '/auth/login'
+ window.location.href = '/login'
}, [])
return (
diff --git a/tools/seedMirror.ts b/tools/seedMirror.ts
index bbc6063..5726137 100644
--- a/tools/seedMirror.ts
+++ b/tools/seedMirror.ts
@@ -1,32 +1,28 @@
/**
- * Seed script for mirror mode — creates only an admin user.
+ * Seed script for mirror mode — creates only an admin org.
* Does not create collections, schemas, or records (those come from sync).
*
* Usage: npm run tool:seed-mirror
*/
-import { v4 as uuidv4 } from 'uuid'
-
import { db, schema } from '../src/db/client.server.js'
async function seedMirror() {
console.log('[seed-mirror] Seeding mirror database...')
- const existing = await db.select().from(schema.accounts).limit(1)
+ const existing = await db.select().from(schema.organization).limit(1)
if (existing.length > 0) {
- console.log('[seed-mirror] Admin account already exists, skipping.')
+ console.log('[seed-mirror] Admin org already exists, skipping.')
process.exit(0)
}
- const adminId = uuidv4()
-
- await db.insert(schema.accounts).values({
- id: adminId,
+ await db.insert(schema.organization).values({
+ id: crypto.randomUUID(),
slug: 'admin',
- type: 'user',
+ name: 'Admin',
})
- console.log('[seed-mirror] Created admin user')
+ console.log('[seed-mirror] Created admin org')
console.log('[seed-mirror] Done. Log in via KF Auth.')
process.exit(0)
}
From 17fa61a387cfe2a0f6b63b02ca2d75e051c0d821 Mon Sep 17 00:00:00 2001
From: Travis Rich
Date: Wed, 27 May 2026 23:06:22 -0400
Subject: [PATCH 2/6] implement api keys with better auth
---
server.ts | 27 +++-
src/api/auth.server.ts | 6 +-
src/lib/auth.ts | 12 +-
src/routes/[owner]/[collection]/settings.tsx | 2 +-
src/routes/[owner]/settings/index.tsx | 2 +-
src/routes/[owner]/settings/keys.tsx | 122 ++++++++++---------
src/routes/dashboard.tsx | 4 +-
src/routes/settings/index.tsx | 2 +-
src/routes/settings/keys.tsx | 100 ++++++---------
9 files changed, 147 insertions(+), 130 deletions(-)
diff --git a/server.ts b/server.ts
index 8f34bf0..fac9364 100644
--- a/server.ts
+++ b/server.ts
@@ -1,7 +1,8 @@
import { existsSync, readFileSync } from 'node:fs'
+import { createServer as createHttpServer } from 'node:http'
import { resolve } from 'node:path'
-import { serve } from '@hono/node-server'
+import { getRequestListener, serve } from '@hono/node-server'
import { serveStatic } from '@hono/node-server/serve-static'
import { Hono } from 'hono'
import { cors } from 'hono/cors'
@@ -27,6 +28,7 @@ import { getMirrorConfig } from '~/lib/mirror-config'
const isProd = process.env.NODE_ENV === 'production'
let vite: ViteDevServer | undefined
+let devHttpServer: import('node:http').Server | undefined
// In dev, proxy API modules through Vite's SSR loader for hot reload
function hot>(staticMod: T, modulePath: string): T {
@@ -279,9 +281,10 @@ if (isProd) {
return c.html(page, statusCode ?? 200)
})
} else {
+ devHttpServer = createHttpServer()
const { createServer: createViteServer } = await import('vite')
vite = await createViteServer({
- server: { middlewareMode: true },
+ server: { middlewareMode: true, hmr: { server: devHttpServer } },
appType: 'custom',
})
@@ -330,5 +333,23 @@ if (isProd) {
const port = Number(process.env.PORT) || 3000
+const KF_AUTH_INTERNAL_URL =
+ process.env.OIDC_ISSUER_INTERNAL_URL ?? process.env.OIDC_ISSUER_URL ?? 'http://localhost:3000'
+try {
+ const res = await fetch(`${KF_AUTH_INTERNAL_URL}/api/health`, {
+ signal: AbortSignal.timeout(5000),
+ })
+ if (!res.ok) throw new Error(`status ${res.status}`)
+} catch (err: any) {
+ console.error(`FATAL: KF Auth not reachable at ${KF_AUTH_INTERNAL_URL}/api/health`)
+ console.error(err.message)
+ process.exit(1)
+}
+
console.log(`Server running at http://localhost:${port}`)
-serve({ fetch: app.fetch, port })
+if (devHttpServer) {
+ devHttpServer.on('request', getRequestListener(app.fetch))
+ devHttpServer.listen(port)
+} else {
+ serve({ fetch: app.fetch, port })
+}
diff --git a/src/api/auth.server.ts b/src/api/auth.server.ts
index da3439d..9a8db17 100644
--- a/src/api/auth.server.ts
+++ b/src/api/auth.server.ts
@@ -7,7 +7,7 @@ export type AuthEnv = {
Variables: {
userId?: string
apiKeyScope?: 'read' | 'write' | 'admin'
- apiKeyCollectionId?: string | null
+ apiKeyCollectionIds?: string[]
sessionUserId?: string
}
}
@@ -47,6 +47,10 @@ export const authMiddleware = createMiddleware(async (c, next) => {
} else {
c.set('apiKeyScope', 'read')
}
+ const meta = (result.key as any).metadata as Record | null
+ if (meta?.collectionIds?.length) {
+ c.set('apiKeyCollectionIds', meta.collectionIds)
+ }
return next()
}
} catch {
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index f16d1b4..9de8753 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -56,7 +56,17 @@ export const auth = betterAuth({
},
},
}),
- apiKey(),
+ apiKey({
+ enableMetadata: true,
+ permissions: {
+ defaultPermissions: async (_referenceId, ctx) => {
+ const scope = ctx.body?.metadata?.scope ?? 'read'
+ if (scope === 'admin') return { collections: ['admin', 'write', 'read'] }
+ if (scope === 'write') return { collections: ['write', 'read'] }
+ return { collections: ['read'] }
+ },
+ },
+ }),
],
databaseHooks: {
diff --git a/src/routes/[owner]/[collection]/settings.tsx b/src/routes/[owner]/[collection]/settings.tsx
index 3287951..6a22c63 100644
--- a/src/routes/[owner]/[collection]/settings.tsx
+++ b/src/routes/[owner]/[collection]/settings.tsx
@@ -232,7 +232,7 @@ export default function CollectionSettingsPage() {
onChange={(e) =>
setSlugValue(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))
}
- pattern="[a-z0-9][a-z0-9-]*[a-z0-9]"
+ pattern="[a-z0-9][-a-z0-9]*[a-z0-9]"
className="bg-parchment border-rule focus:border-ink w-full border px-3 py-2 font-mono text-sm focus:outline-none"
/>
{slugValue !== collection && (
diff --git a/src/routes/[owner]/settings/index.tsx b/src/routes/[owner]/settings/index.tsx
index 737bccf..bd07f88 100644
--- a/src/routes/[owner]/settings/index.tsx
+++ b/src/routes/[owner]/settings/index.tsx
@@ -298,7 +298,7 @@ export default function OwnerSettings() {
onChange={(e) =>
setSlugValue(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))
}
- pattern="[a-z0-9][a-z0-9-]*[a-z0-9]"
+ pattern="[a-z0-9][-a-z0-9]*[a-z0-9]"
className="bg-parchment border-rule focus:border-ink w-full border px-3 py-2 font-mono text-sm focus:outline-none"
/>
{slugValue !== owner && (
diff --git a/src/routes/[owner]/settings/keys.tsx b/src/routes/[owner]/settings/keys.tsx
index 0527a75..fb9e836 100644
--- a/src/routes/[owner]/settings/keys.tsx
+++ b/src/routes/[owner]/settings/keys.tsx
@@ -4,8 +4,19 @@ import { Link, useParams } from 'react-router'
import { ApiPlayground } from '~/components/ApiPlayground'
import BaseLayout from '~/components/BaseLayout'
import { NotFoundError } from '~/components/NotFound'
+import { authClient } from '~/lib/auth-client'
import { useSSRData } from '~/lib/ssr-data'
+interface Key {
+ id: string
+ name: string
+ start?: string
+ permissions?: Record
+ metadata?: { collectionIds?: string[] }
+ createdAt: string
+ expiresAt?: string
+}
+
function isExpiringSoon(expiresAt: string | null): boolean {
if (!expiresAt) return false
const daysLeft = (new Date(expiresAt).getTime() - Date.now()) / (1000 * 60 * 60 * 24)
@@ -17,26 +28,36 @@ function isExpired(expiresAt: string | null): boolean {
return new Date(expiresAt) < new Date()
}
+function getScope(permissions?: Record): string {
+ const perms = permissions?.['collections'] ?? []
+ if (perms.includes('admin')) return 'admin'
+ if (perms.includes('write')) return 'write'
+ return 'read'
+}
+
export default function OwnerSettingsKeys() {
const { owner } = useParams()
const currentUser = useSSRData('currentUser')
const [orgData, setOrgData] = useState(null)
const [isAdmin, setIsAdmin] = useState(false)
- const [keys, setKeys] = useState([])
+ const [keys, setKeys] = useState([])
const [collections, setCollections] = useState([])
const [loading, setLoading] = useState(true)
- const [success, setSuccess] = useState('')
const [error, setError] = useState('')
- const [newKeyResult, setNewKeyResult] = useState<{ key: string; label: string } | null>(null)
+ const [newKeyResult, setNewKeyResult] = useState<{ key: string; name: string } | null>(null)
const [submitting, setSubmitting] = useState(false)
- // Create key form
const [keyLabel, setKeyLabel] = useState('')
const [keyScope, setKeyScope] = useState('write')
const [keyCollectionId, setKeyCollectionId] = useState('')
const [keyExpiresIn, setKeyExpiresIn] = useState('')
+ async function loadKeys() {
+ const { data } = await authClient.apiKey.list()
+ if (data) setKeys((data as any).apiKeys ?? [])
+ }
+
useEffect(() => {
if (!owner || !currentUser) return
@@ -53,20 +74,17 @@ export default function OwnerSettingsKeys() {
fetch(`/api/accounts/${owner}`, { credentials: 'include' }).then((r) =>
r.ok ? r.json() : null,
),
- fetch(`/api/accounts/${owner}/keys`, { credentials: 'include' }).then((r) =>
- r.ok ? r.json() : [],
- ),
+ loadKeys(),
fetch(`/api/accounts/${owner}/collections`, { credentials: 'include' }).then((r) =>
r.ok ? r.json() : [],
),
- ]).then(([org, k, cols]) => {
+ ]).then(([org, , cols]) => {
if (!org) {
setLoading(false)
return
}
setOrgData(org)
- setKeys(k)
- setCollections(cols)
+ setCollections(cols as any)
setLoading(false)
})
}, [owner, currentUser])
@@ -78,32 +96,24 @@ export default function OwnerSettingsKeys() {
async function handleCreateKey(e: FormEvent) {
e.preventDefault()
- setSuccess('')
setError('')
setNewKeyResult(null)
setSubmitting(true)
try {
- const res = await fetch(`/api/accounts/${owner}/keys`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- credentials: 'include',
- body: JSON.stringify({
- label: keyLabel,
- scope: keyScope,
- collectionId: keyCollectionId || undefined,
- expiresIn: keyExpiresIn ? parseInt(keyExpiresIn) : undefined,
- }),
- })
- if (res.ok) {
- const data = await res.json()
- setNewKeyResult({ key: data.key, label: data.label })
+ const metadata: Record = { scope: keyScope }
+ if (keyCollectionId) metadata.collectionIds = [keyCollectionId]
+ const { data, error: err } = await authClient.apiKey.create({
+ name: keyLabel,
+ metadata,
+ expiresIn: keyExpiresIn ? parseInt(keyExpiresIn) * 24 * 60 * 60 : undefined,
+ prefix: 'ul',
+ } as any)
+ if (err) {
+ setError(err.message ?? 'Failed to create key.')
+ } else if (data) {
+ setNewKeyResult({ key: (data as any).key, name: keyLabel })
setKeyLabel('')
- // Refresh keys
- const refreshRes = await fetch(`/api/accounts/${owner}/keys`, { credentials: 'include' })
- if (refreshRes.ok) setKeys(await refreshRes.json())
- } else {
- const body = await res.json().catch(() => ({}))
- setError(body.error ?? 'Failed to create key.')
+ await loadKeys()
}
} finally {
setSubmitting(false)
@@ -111,15 +121,9 @@ export default function OwnerSettingsKeys() {
}
async function handleDeleteKey(keyId: string) {
- setSuccess('')
setError('')
- await fetch(`/api/accounts/${owner}/keys/${keyId}`, {
- method: 'DELETE',
- credentials: 'include',
- })
- setSuccess('Key revoked.')
- const refreshRes = await fetch(`/api/accounts/${owner}/keys`, { credentials: 'include' })
- if (refreshRes.ok) setKeys(await refreshRes.json())
+ await authClient.apiKey.delete({ keyId } as any)
+ setKeys((prev) => prev.filter((k) => k.id !== keyId))
}
if (loading) {
@@ -156,11 +160,6 @@ export default function OwnerSettingsKeys() {
- {success && (
-
- {success}
-
- )}
{error && (
{error}
@@ -169,7 +168,7 @@ export default function OwnerSettingsKeys() {
{newKeyResult && (
-
Key created: {newKeyResult.label}
+
Key created: {newKeyResult.name}
Copy this key now — it won't be shown again.
@@ -232,7 +231,7 @@ export default function OwnerSettingsKeys() {
- Expiration
+ Expiration (optional)
setKeyExpiresIn(e.target.value)}
className="bg-parchment border-rule focus:border-ink w-full border px-2 py-1.5 text-sm focus:outline-none"
>
- Never
+ Never expires
+ 7 days
30 days
90 days
1 year
@@ -273,30 +273,36 @@ export default function OwnerSettingsKeys() {
No API keys for this organization.
) : (
- {keys.map((k: any) => (
+ {keys.map((k) => (
- {k.label}
- {k.keyPrefix && (
- {k.keyPrefix}…
+ {k.name}
+ {k.start && (
+ {k.start}…
)}
- {k.scope}
- Created {new Date(k.createdAt).toLocaleDateString()}
- {k.lastUsedAt && (
- · Last used {new Date(k.lastUsedAt).toLocaleDateString()}
+
+ {getScope(k.permissions)}
+
+ {k.metadata?.collectionIds?.length && (
+
+ {k.metadata.collectionIds
+ .map((id) => collections.find((c) => c.id === id)?.slug ?? id.slice(0, 8))
+ .join(', ')}
+
)}
+ Created {new Date(k.createdAt).toLocaleDateString()}
{k.expiresAt && !isExpired(k.expiresAt) && (
)}
- {isExpired(k.expiresAt) && (
+ {isExpired(k.expiresAt ?? null) && (
· Expired
)}
diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx
index 3f677c0..d2fc83a 100644
--- a/src/routes/dashboard.tsx
+++ b/src/routes/dashboard.tsx
@@ -330,7 +330,7 @@ export default function Dashboard() {
setSlugValue(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))
}
- pattern="[a-z0-9][a-z0-9-]*[a-z0-9]"
+ pattern="[a-z0-9][-a-z0-9]*[a-z0-9]"
className="bg-parchment border-rule focus:border-ink w-full border px-3 py-2 font-mono text-sm focus:outline-none"
/>
{slugValue !== me?.slug && (
diff --git a/src/routes/settings/keys.tsx b/src/routes/settings/keys.tsx
index 33445a1..ffc47e1 100644
--- a/src/routes/settings/keys.tsx
+++ b/src/routes/settings/keys.tsx
@@ -3,15 +3,16 @@ import { Link } from 'react-router'
import { ApiPlayground } from '~/components/ApiPlayground'
import BaseLayout from '~/components/BaseLayout'
+import { authClient } from '~/lib/auth-client'
import { useSSRData } from '~/lib/ssr-data'
interface Key {
id: string
- label: string
- keyPrefix?: string
- scope: string
+ name: string
+ start?: string
+ permissions?: Record
+ metadata?: { scope?: string; collectionIds?: string[] }
createdAt: string
- lastUsedAt?: string
expiresAt?: string
}
@@ -31,28 +32,34 @@ function isExpired(expiresAt: string | null): boolean {
return new Date(expiresAt) < new Date()
}
+function getScope(permissions?: Record): string {
+ const perms = permissions?.['collections'] ?? []
+ if (perms.includes('admin')) return 'admin'
+ if (perms.includes('write')) return 'write'
+ return 'read'
+}
+
export default function SettingsKeys() {
const me = useSSRData('currentUser')
const [keys, setKeys] = useState([])
const [collections, setCollections] = useState([])
- const [newKeyResult, setNewKeyResult] = useState<{ key: string; label: string } | null>(null)
+ const [newKeyResult, setNewKeyResult] = useState<{ key: string; name: string } | null>(null)
const [error, setError] = useState('')
- // Create key form
const [label, setLabel] = useState('')
const [scope, setScope] = useState('write')
- const [collectionId, setCollectionId] = useState('')
const [expiresIn, setExpiresIn] = useState('')
const [submitting, setSubmitting] = useState(false)
+ async function loadKeys() {
+ const { data } = await authClient.apiKey.list()
+ if (data) setKeys((data as any).apiKeys ?? [])
+ }
+
useEffect(() => {
if (!me) return
-
- fetch('/api/accounts/keys', { credentials: 'include' })
- .then((r) => (r.ok ? r.json() : []))
- .then(setKeys)
-
+ loadKeys()
fetch(`/api/accounts/${me.slug}/collections`, { credentials: 'include' })
.then((r) => (r.ok ? r.json() : []))
.then(setCollections)
@@ -64,27 +71,18 @@ export default function SettingsKeys() {
setNewKeyResult(null)
setSubmitting(true)
try {
- const res = await fetch('/api/accounts/keys', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- credentials: 'include',
- body: JSON.stringify({
- label,
- scope,
- collectionId: collectionId || undefined,
- expiresIn: expiresIn ? parseInt(expiresIn) : undefined,
- }),
- })
- if (res.ok) {
- const data = await res.json()
- setNewKeyResult({ key: data.key, label: data.label })
+ const { data, error: err } = await authClient.apiKey.create({
+ name: label,
+ metadata: { scope },
+ expiresIn: expiresIn ? parseInt(expiresIn) * 24 * 60 * 60 : undefined,
+ prefix: 'ul',
+ } as any)
+ if (err) {
+ setError(err.message ?? 'Failed to create key.')
+ } else if (data) {
+ setNewKeyResult({ key: (data as any).key, name: label })
setLabel('')
- // Refresh keys list
- const keysRes = await fetch('/api/accounts/keys', { credentials: 'include' })
- if (keysRes.ok) setKeys(await keysRes.json())
- } else {
- const body = await res.json().catch(() => ({}))
- setError(body.error ?? 'Failed to create key.')
+ await loadKeys()
}
} finally {
setSubmitting(false)
@@ -92,10 +90,7 @@ export default function SettingsKeys() {
}
async function handleRevokeKey(keyId: string) {
- await fetch(`/api/accounts/keys/${keyId}`, {
- method: 'DELETE',
- credentials: 'include',
- })
+ await authClient.apiKey.delete({ keyId } as any)
setKeys((prev) => prev.filter((k) => k.id !== keyId))
}
@@ -120,7 +115,7 @@ export default function SettingsKeys() {
{newKeyResult && (
-
Key created: {newKeyResult.label}
+
Key created: {newKeyResult.name}
Copy this key now — it won't be shown again.
@@ -169,24 +164,6 @@ export default function SettingsKeys() {
admin — full access
-
-
- Scope to collection (optional)
-
- setCollectionId(e.target.value)}
- className="bg-parchment border-rule focus:border-ink w-full border px-2 py-1.5 text-sm focus:outline-none"
- >
- All collections
- {collections.map((c) => (
-
- {c.slug}
-
- ))}
-
-
Expiration (optional)
@@ -236,17 +213,16 @@ export default function SettingsKeys() {
>
- {k.label}
- {k.keyPrefix && (
- {k.keyPrefix}…
+ {k.name}
+ {k.start && (
+ {k.start}…
)}
-
{k.scope}
+
+ {getScope(k.permissions)}
+
Created {new Date(k.createdAt).toLocaleDateString()}
- {k.lastUsedAt && (
-
· Last used {new Date(k.lastUsedAt).toLocaleDateString()}
- )}
{k.expiresAt && !isExpired(k.expiresAt) && (
Date: Wed, 27 May 2026 23:14:12 -0400
Subject: [PATCH 3/6] Better delete handling
---
src/api/accounts.ts | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/api/accounts.ts b/src/api/accounts.ts
index b388b76..4918c4a 100644
--- a/src/api/accounts.ts
+++ b/src/api/accounts.ts
@@ -246,6 +246,13 @@ export async function deleteMe(c: Context) {
// Non-fatal
}
+ // Clean up better-auth managed rows + domain data
+ await db.delete(schema.apikey).where(eq(schema.apikey.referenceId, userId))
+ await db.delete(schema.apikey).where(eq(schema.apikey.referenceId, defaultOrg.id))
+ await db.delete(schema.invitation).where(eq(schema.invitation.organizationId, defaultOrg.id))
+ await db.delete(schema.member).where(eq(schema.member.userId, userId))
+ await db.delete(schema.session).where(eq(schema.session.userId, userId))
+ await db.delete(schema.account).where(eq(schema.account.userId, userId))
await db.delete(schema.organization).where(eq(schema.organization.id, defaultOrg.id))
await db.delete(schema.user).where(eq(schema.user.id, userId))
return c.json({ ok: true })
From a6cfcc74d3f4c3c0b08e72b8c672d6f9ffc5d30c Mon Sep 17 00:00:00 2001
From: Travis Rich
Date: Wed, 27 May 2026 23:31:41 -0400
Subject: [PATCH 4/6] Fix kf org check/integration
---
src/api/accounts.ts | 12 +++++++++++-
src/routes/dashboard.tsx | 19 +++++++++----------
2 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/src/api/accounts.ts b/src/api/accounts.ts
index 4918c4a..0efb46a 100644
--- a/src/api/accounts.ts
+++ b/src/api/accounts.ts
@@ -172,8 +172,18 @@ export async function updateMe(c: Context) {
export async function availableKfOrgs(c: Context) {
const userId = c.get('userId')!
+
+ // Look up the KF Auth user ID from the OAuth account link
+ const [acct] = await db
+ .select({ accountId: schema.account.accountId })
+ .from(schema.account)
+ .where(and(eq(schema.account.userId, userId), eq(schema.account.providerId, 'kf-auth')))
+ .limit(1)
+
+ if (!acct) return c.json([])
+
const { fetchAuthOrgs } = await import('../lib/auth-internal.server.js')
- return c.json(await fetchAuthOrgs(userId))
+ return c.json(await fetchAuthOrgs(acct.accountId))
}
// Upload org avatar
diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx
index d2fc83a..637e954 100644
--- a/src/routes/dashboard.tsx
+++ b/src/routes/dashboard.tsx
@@ -2,6 +2,7 @@ import { type FormEvent, useEffect, useState } from 'react'
import { Link } from 'react-router'
import BaseLayout from '~/components/BaseLayout'
+import { authClient } from '~/lib/auth-client'
import { useSSRData } from '~/lib/ssr-data'
interface Collection {
@@ -94,17 +95,15 @@ export default function Dashboard() {
setOrgError('')
setSubmitting(true)
try {
- const res = await fetch('/api/accounts/orgs', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- credentials: 'include',
- body: JSON.stringify({ slug: orgSlug, displayName: orgDisplayName, kfOrgId: orgKfOrgId }),
- })
- if (res.ok) {
+ const { data, error } = await authClient.organization.create({
+ name: orgDisplayName,
+ slug: orgSlug,
+ kfOrgId: orgKfOrgId || undefined,
+ } as any)
+ if (error) {
+ setOrgError(error.message ?? 'Failed to create organization')
+ } else if (data) {
window.location.reload()
- } else {
- const err = await res.json()
- setOrgError(err.error ?? 'Failed to create organization')
}
} finally {
setSubmitting(false)
From d1876773091f4544354744a35b31db3c73a1e0c4 Mon Sep 17 00:00:00 2001
From: Travis Rich
Date: Thu, 28 May 2026 09:33:10 -0400
Subject: [PATCH 5/6] Update ai.txt
---
public/.well-known/ai.txt | 38 ++++++++++++++++++++++++++++----------
1 file changed, 28 insertions(+), 10 deletions(-)
diff --git a/public/.well-known/ai.txt b/public/.well-known/ai.txt
index 14fb9fe..3098848 100644
--- a/public/.well-known/ai.txt
+++ b/public/.well-known/ai.txt
@@ -15,12 +15,14 @@ There are two auth methods:
1. API Key (for programmatic access):
Header: Authorization: Bearer ul_
Keys have scopes: read, write, admin.
- Create keys at https://underlay.org/settings/keys.
+ Keys can optionally be scoped to specific collections via metadata.
+ Create keys at https://underlay.org/settings/keys (personal) or /:owner/settings/keys (organization).
+ Keys are managed via better-auth's apiKey plugin at /api/auth/api-key/*.
2. Session cookie (for browser use):
- Users sign in via KF Auth SSO (OIDC) at https://underlay.org/login.
- Accounts are created automatically on first sign-in.
- GET /api/accounts/me returns the current user (works with either auth method).
+ Users sign in via KF Auth SSO (OAuth2/PKCE) at https://underlay.org/login.
+ Accounts are created automatically on first sign-in, along with a default organization.
+ GET /api/accounts/me returns the current user and their organization memberships.
All GET requests are public — no auth required to read public data.
All write requests (POST, PATCH, PUT, DELETE) require authentication.
@@ -47,7 +49,8 @@ To get the higher limit, authenticate with an API key (recommended for any autom
## Core Concepts
-- Collection: a named, versioned body of data owned by an account. Identified by :owner/:slug.
+- Organization: an entity that owns collections. Every user gets a default organization on signup. Identified by :slug. Managed via better-auth's organization plugin at /api/auth/organization/*.
+- Collection: a named, versioned body of data owned by an organization. Identified by :owner/:slug.
- Version: an immutable snapshot containing a JSON Schema, records, and file references. Sequential integer numbers, auto-derived semver.
- Record: a flat JSON object with { id, type, data }. Records reference other records by id and files by hash.
- File: a binary blob stored by SHA-256 hash, referenced in record data as {"$file": "sha256:"}.
@@ -342,12 +345,24 @@ When schemas are returned via the collection schemas endpoint, known labels are
---
+## Organization Management
+
+Organizations are managed via better-auth's organization plugin at /api/auth/organization/*.
+Every user gets a default organization on signup. Users can create additional organizations.
+
+POST /api/auth/organization/create → create org {"name", "slug"}
+GET /api/auth/organization/list → list user's organizations
+PATCH /api/auth/organization/update → update org
+DELETE /api/auth/organization/delete → delete org
+
+Member management (invite, remove, update roles) is also under /api/auth/organization/*.
+
## Collection Management
POST /api/accounts/:owner/collections → create collection {"slug", "name", "description", "public"}
PATCH /api/collections/:owner/:slug → update {"name", "description", "public"}
DELETE /api/collections/:owner/:slug → delete collection (requires admin scope)
-GET /api/accounts/:owner/collections → list collections for an account
+GET /api/accounts/:owner/collections → list collections for an organization
---
@@ -409,10 +424,13 @@ article-2 is only visible to the collection owner. Public readers see article-1
## API Key Management
-POST /api/accounts/keys → create key {"label": "my-app", "scope": "write"}
- Response includes the key once: {"key": "ul_abc123...", "id": "..."}
-GET /api/accounts/keys → list keys (id, label, scope, createdAt, lastUsedAt — not the key itself)
-DELETE /api/accounts/keys/:id → revoke a key
+API keys are managed via better-auth's apiKey plugin. All endpoints are under /api/auth/api-key/*.
+
+POST /api/auth/api-key/create → create key {"name": "my-app", "metadata": {"scope": "write"}, "prefix": "ul"}
+ The scope in metadata is translated to permissions server-side.
+ Response includes the key once: {"key": "ul_abc123...", "id": "..."}
+GET /api/auth/api-key/list → list keys (id, name, start, permissions, metadata, createdAt, expiresAt)
+POST /api/auth/api-key/delete → revoke a key {"keyId": "..."}
---
From 447eae81cc6faa5d4d26d3dca0c1ba2bafbde3a5 Mon Sep 17 00:00:00 2001
From: Travis Rich
Date: Fri, 29 May 2026 09:06:15 -0400
Subject: [PATCH 6/6] Language and account updates
---
server.ts | 1 +
src/api/accounts.ts | 35 +++
src/lib/auth.server.ts | 12 +-
src/routes/[owner]/[collection]/settings.tsx | 9 +-
src/routes/[owner]/index.tsx | 110 +++------
src/routes/[owner]/settings/index.tsx | 17 +-
src/routes/[owner]/settings/members.tsx | 244 ++++++-------------
src/routes/dashboard.tsx | 63 +----
8 files changed, 179 insertions(+), 312 deletions(-)
diff --git a/server.ts b/server.ts
index fac9364..e351769 100644
--- a/server.ts
+++ b/server.ts
@@ -210,6 +210,7 @@ app.get('/api/collections/:owner/:slug/versions/:n/diff', versions.diff)
app.get('/api/accounts/me', requireAuth(), accounts.getMe)
app.get('/api/accounts/available-kf-orgs', requireAuth(), accounts.availableKfOrgs)
app.get('/api/accounts/:slug', accounts.getBySlug)
+app.get('/api/accounts/:slug/members', accounts.listMembers)
app.patch('/api/accounts/me', requireAuth(), accounts.updateMe)
app.post('/api/accounts/:slug/avatar', requireAuth(), accounts.uploadOrgAvatar)
app.delete('/api/accounts/me', requireAuth(), accounts.deleteMe)
diff --git a/src/api/accounts.ts b/src/api/accounts.ts
index 0efb46a..6af0930 100644
--- a/src/api/accounts.ts
+++ b/src/api/accounts.ts
@@ -121,6 +121,41 @@ export async function getBySlug(c: Context) {
return c.json({ ...org, arkShoulder: shoulderRow?.shoulder ?? null })
}
+// List public members for an org
+export async function listMembers(c: Context) {
+ const slug = c.req.param('slug')!
+ const org = await findOrgBySlug(slug)
+ if (!org) return c.json({ error: 'Not found', statusCode: 404 }, 404)
+
+ const defaultOrgSub = db
+ .select({
+ userId: schema.member.userId,
+ slug: schema.organization.slug,
+ name: schema.organization.name,
+ })
+ .from(schema.member)
+ .innerJoin(
+ schema.organization,
+ and(
+ eq(schema.member.organizationId, schema.organization.id),
+ eq(schema.organization.isDefault, true),
+ ),
+ )
+ .as('default_org')
+
+ const rows = await db
+ .select({
+ role: schema.member.role,
+ slug: defaultOrgSub.slug,
+ displayName: defaultOrgSub.name,
+ })
+ .from(schema.member)
+ .leftJoin(defaultOrgSub, eq(schema.member.userId, defaultOrgSub.userId))
+ .where(eq(schema.member.organizationId, org.id))
+
+ return c.json(rows)
+}
+
// Update own profile (updates the user's default org)
export async function updateMe(c: Context) {
const { slug, displayName, bio, website } = await c.req.json()
diff --git a/src/lib/auth.server.ts b/src/lib/auth.server.ts
index 631a2bf..f2a7910 100644
--- a/src/lib/auth.server.ts
+++ b/src/lib/auth.server.ts
@@ -5,10 +5,17 @@ import { auth } from './auth.js'
export interface SessionUser {
id: string
+ slug: string | null
displayName: string | null
avatarUrl: string | null
defaultOrg: { slug: string; displayName: string | null } | null
- orgs: Array<{ slug: string; displayName: string | null; role: string }>
+ orgs: Array<{
+ organizationId: string
+ slug: string
+ displayName: string | null
+ role: string
+ isDefault: boolean | null
+ }>
}
export async function getSessionUser(request: Request): Promise {
@@ -38,13 +45,16 @@ export async function getSessionUser(request: Request): Promise ({
+ organizationId: m.orgId,
slug: m.orgSlug,
displayName: m.orgName,
role: m.role,
+ isDefault: m.isDefault,
})),
}
}
diff --git a/src/routes/[owner]/[collection]/settings.tsx b/src/routes/[owner]/[collection]/settings.tsx
index 6a22c63..77af5df 100644
--- a/src/routes/[owner]/[collection]/settings.tsx
+++ b/src/routes/[owner]/[collection]/settings.tsx
@@ -369,7 +369,7 @@ export default function CollectionSettingsPage() {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
- body: JSON.stringify({ targetAccountSlug: transferTarget }),
+ body: JSON.stringify({ targetOrgSlug: transferTarget }),
})
if (res.ok) {
window.location.href = `/${transferTarget}/${data?.slug ?? collection}/settings`
@@ -385,7 +385,7 @@ export default function CollectionSettingsPage() {
>
- Target account
+ Target organization
— Select —
- {currentUser?.slug && currentUser.slug !== owner && (
-
- {currentUser.displayName ?? currentUser.slug} (personal)
-
- )}
{currentUser?.orgs
?.filter(
(o: any) => o.slug !== owner && (o.role === 'owner' || o.role === 'admin'),
diff --git a/src/routes/[owner]/index.tsx b/src/routes/[owner]/index.tsx
index adec0b0..7ef1ddb 100644
--- a/src/routes/[owner]/index.tsx
+++ b/src/routes/[owner]/index.tsx
@@ -26,21 +26,19 @@ export default function OwnerPage() {
fetch(`/api/accounts/${owner}/collections`, { credentials: 'include' }).then((r) =>
r.ok ? r.json() : [],
),
- ]).then(([acct, cols]) => {
+ fetch(`/api/accounts/${owner}/members`, { credentials: 'include' }).then((r) =>
+ r.ok ? r.json() : [],
+ ),
+ ]).then(([acct, cols, mems]) => {
if (!acct) {
setLoading(false)
return
}
setAccount(acct)
setCollections(cols)
+ setMembers(mems)
- if (acct.type === 'org') {
- fetch(`/api/accounts/${owner}/members`, { credentials: 'include' })
- .then((r) => (r.ok ? r.json() : []))
- .then(setMembers)
- }
-
- if (currentUser && acct.type === 'org') {
+ if (currentUser) {
setIsMember(currentUser.orgs?.some((o: any) => o.slug === owner) ?? false)
}
@@ -88,12 +86,7 @@ export default function OwnerPage() {
)}
-
-
@{account.slug}
-
- {account.type === 'org' ? 'Organization' : 'User'}
-
-
+ @{account.slug}
{account.bio && {account.bio}
}
@@ -166,64 +159,8 @@ export default function OwnerPage() {
- {/* Public member list for orgs */}
- {account.type === 'org' && members.length > 0 ? (
-
- {/* Collections - main column */}
-
-
- Collections ({collections.length})
-
-
- {collections.length === 0 ? (
-
No public collections yet.
- ) : (
-
- {collections.map((c: any) => (
-
-
- {c.name}
-
- {c.public ? 'public' : 'private'}
-
-
- {c.description && (
-
{c.description}
- )}
-
- ))}
-
- )}
-
-
- {/* Members - right sidebar */}
-
-
- Members
-
-
- {members.map((m: any) => (
-
- {m.slug}
-
- {m.role}
-
-
- ))}
-
-
-
- ) : (
- <>
+
+
Collections ({collections.length})
@@ -231,7 +168,7 @@ export default function OwnerPage() {
{collections.length === 0 ? (
No public collections yet.
) : (
-
+
{collections.map((c: any) => (
)}
- >
- )}
+
+
+ {members.length > 0 && (
+
+
+ Members
+
+
+ {members.map((m: any) => (
+
+ {m.slug}
+
+ {m.role}
+
+
+ ))}
+
+
+ )}
+
)
diff --git a/src/routes/[owner]/settings/index.tsx b/src/routes/[owner]/settings/index.tsx
index bd07f88..c20a9b2 100644
--- a/src/routes/[owner]/settings/index.tsx
+++ b/src/routes/[owner]/settings/index.tsx
@@ -366,18 +366,19 @@ export default function OwnerSettings() {
)}
- {/* KF Organization Link */}
+ {/* Transfer Organization */}
{isOwner && (
- KF Organization
+ Transfer Organization
- Link this Underlay organization to a Knowledge Futures organization. This determines
- which KF org manages this account.
+ Transfer this Underlay organization to a different KF Account you belong to. This
+ changes which KF Account is responsible for billing and ownership, but does not affect
+ permissions or membership on the Underlay side.
{kfOrgsLoading ? (
-
Loading KF organizations…
+
Loading KF Accounts…
) : (