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/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": "..."} --- diff --git a/server.ts b/server.ts index c8ef98b..e351769 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' @@ -17,17 +18,17 @@ 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 +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 { @@ -49,7 +50,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 +76,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 +206,14 @@ 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.get('/api/accounts/:slug/members', accounts.listMembers) 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) => { @@ -285,9 +282,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', }) @@ -336,12 +334,23 @@ 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.') +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/accounts.ts b/src/api/accounts.ts index 589f59a..6af0930 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,193 @@ 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'))) + .from(schema.organization) + .where(eq(schema.organization.slug, slug)) .limit(1) + return org ?? null +} - if (!org) return c.json({ error: 'Organization not found', statusCode: 404 }, 404) - - // Must be owner or admin +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 || 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, - ) + if (!membership) return null + if (minRole === 'owner' && membership.role !== 'owner') return null + if (minRole === 'admin' && membership.role === 'member') return null + return membership } -// 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) +// --- Current user --- - 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 - 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 keys = await db + const memberships = 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, + organizationId: schema.member.organizationId, + role: schema.member.role, + slug: schema.organization.slug, + name: schema.organization.name, + isDefault: schema.organization.isDefault, }) - .from(schema.apiKeys) - .where(eq(schema.apiKeys.accountId, org.id)) - - return c.json(keys) + .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, + }) } -// Delete org API key -export async function deleteOrgKey(c: Context) { +// Get org by slug (public) +export async function getBySlug(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 }) -} - -// --- 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) - } + const org = await findOrgBySlug(slug) - 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, - ) - } + if (!org) return c.json({ error: 'Not found', statusCode: 404 }, 404) - const existing = await db - .select() - .from(schema.accounts) - .where(eq(schema.accounts.slug, slug)) + const [shoulderRow] = await db + .select({ shoulder: schema.arkShoulders.shoulder }) + .from(schema.arkShoulders) + .where(eq(schema.arkShoulders.organizationId, org.id)) .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) + return c.json({ ...org, arkShoulder: shoulderRow?.shoulder ?? null }) } -/** - * 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 +// 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 [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 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) - - if (!membership) return c.json({ error: 'Forbidden', statusCode: 403 }, 403) - - const members = await db + const defaultOrgSub = db .select({ - userId: schema.orgMemberships.userId, - role: schema.orgMemberships.role, - slug: schema.accounts.slug, - displayName: schema.accounts.displayName, + userId: schema.member.userId, + slug: schema.organization.slug, + name: schema.organization.name, }) - .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', - }) - - return c.json({ ok: true, username, role }, 201) -} - -// Update member role -export async function updateMember(c: Context) { - const slug = c.req.param('slug')! - const userId = c.req.param('userId')! - const { 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 - const [callerMembership] = await db - .select() - .from(schema.orgMemberships) - .where( + .from(schema.member) + .innerJoin( + schema.organization, and( - eq(schema.orgMemberships.orgId, org.id), - eq(schema.orgMemberships.userId, c.get('accountId')!), + eq(schema.member.organizationId, schema.organization.id), + eq(schema.organization.isDefault, true), ), ) - .limit(1) + .as('default_org') - 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))) + 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({ ok: true }) + return c.json(rows) } -// 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))) + const organizationId = defaultMembership.organizationId - 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) - - 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')! + + // 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(acct.accountId)) } // 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 +238,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 +259,46 @@ 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() - - const [invitation] = await db - .select() - .from(schema.orgInvitations) - .where(eq(schema.orgInvitations.token, token)) - .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 (!userEmail || userEmail !== invitation.email) { - return c.json( - { error: 'This invitation was sent to a different email address', statusCode: 403 }, - 403, - ) - } - - // 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')! +// Delete own account +export async function deleteMe(c: Context) { + const { confirmSlug } = await c.req.json() + 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'))) + 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 (!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 (!defaultOrg) return c.json({ error: 'Account not found', statusCode: 404 }, 404) - if (!callerMembership || callerMembership.role !== 'owner') { - return c.json({ error: 'Must be an owner to delete the organization', statusCode: 403 }, 403) + if (confirmSlug !== defaultOrg.slug) { + return c.json({ error: 'Username confirmation does not match', statusCode: 422 }, 422) } - // Cascade will handle memberships, collections, etc. - await db.delete(schema.accounts).where(eq(schema.accounts.id, org.id)) + try { + const avatarKeys = await listS3Objects(`avatars/${defaultOrg.id}/`) + if (avatarKeys.length > 0) await deleteS3Objects(avatarKeys) + } catch { + // 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 }) } 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 0226999..4b72e1f 100644 --- a/src/api/auth.server.ts +++ b/src/api/auth.server.ts @@ -1,18 +1,20 @@ import crypto from 'node:crypto' -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' + +function timingSafeEquals(a: string, b: string): boolean { + if (a.length !== b.length) return false + return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)) +} export type AuthEnv = { Variables: { - accountId?: string + userId?: string apiKeyScope?: 'read' | 'write' | 'admin' - apiKeyCollectionId?: string | null + apiKeyCollectionIds?: string[] sessionUserId?: string } } @@ -21,12 +23,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' - -function timingSafeEquals(a: string, b: string): boolean { - if (a.length !== b.length) return false - return crypto.timingSafeEqual(Buffer.from(a), Buffer.from(b)) -} export const authMiddleware = createMiddleware(async (c, next) => { // Internal service calls (legacy header) @@ -37,69 +33,60 @@ 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 && timingSafeEquals(auth, `Bearer ${authInternalApiKey}`)) { + const authorization = c.req.header('authorization') + if ( + authInternalApiKey && + authorization && + timingSafeEquals(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') + } + const meta = (result.key as any).metadata as Record | null + if (meta?.collectionIds?.length) { + c.set('apiKeyCollectionIds', meta.collectionIds) } + 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) @@ -110,7 +97,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') { @@ -122,18 +109,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 f69913f..7f3d3e8 100644 --- a/src/api/collections.ts +++ b/src/api/collections.ts @@ -29,7 +29,7 @@ export async function list(c: Context) { ) } if (owner) { - conditions.push(eq(schema.accounts.slug, owner)) + conditions.push(eq(schema.organization.slug, owner)) } const results = await db @@ -38,13 +38,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) @@ -98,14 +98,14 @@ export async function list(c: Context) { const ownerFacets = await db .select({ - slug: schema.accounts.slug, - name: schema.accounts.displayName, + slug: schema.organization.slug, + name: schema.organization.name, count: sql`count(*)::int`, }) .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(...facetConditions)) - .groupBy(schema.accounts.slug, schema.accounts.displayName) + .groupBy(schema.organization.slug, schema.organization.name) .orderBy(sql`count(*) DESC`) return c.json({ @@ -140,42 +140,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) { @@ -185,7 +177,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, @@ -194,10 +186,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 { @@ -218,44 +210,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 } @@ -305,12 +292,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) { @@ -339,20 +329,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) { @@ -371,12 +361,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) @@ -398,20 +388,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) { @@ -422,80 +412,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, ) } @@ -505,24 +479,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, ) } @@ -530,41 +504,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)) } @@ -600,18 +570,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 9b4e30a..0000000 --- a/src/api/kf-auth.ts +++ /dev/null @@ -1,193 +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 (validate it's a safe relative path) - const rawReturn = getCookie(c, RETURN_COOKIE) ?? '/dashboard' - deleteCookie(c, RETURN_COOKIE, { path: '/' }) - const returnTo = - rawReturn.startsWith('/') && !rawReturn.startsWith('//') ? rawReturn : '/dashboard' - - 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..f2a7910 100644 --- a/src/lib/auth.server.ts +++ b/src/lib/auth.server.ts @@ -1,102 +1,60 @@ 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 + slug: string | null displayName: string | null - type: string - bio: string | null avatarUrl: string | null - orgs: Array<{ slug: string; displayName: string | null; role: string }> + defaultOrg: { slug: string; displayName: string | null } | null + orgs: Array<{ + organizationId: string + slug: string + displayName: string | null + role: string + isDefault: boolean | null + }> } -/** - * 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, + slug: defaultOrg?.orgSlug ?? null, + displayName: defaultOrg?.orgName ?? u.name, + avatarUrl: u.image ?? null, + defaultOrg: defaultOrg ? { slug: defaultOrg.orgSlug, displayName: defaultOrg.orgName } : null, orgs: memberships.map((m) => ({ + organizationId: m.orgId, slug: m.orgSlug, - displayName: m.orgDisplayName, + displayName: m.orgName, role: m.role, + isDefault: m.isDefault, })), } } diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..9de8753 --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,115 @@ +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({ + 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: { + 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/[owner]/[collection]/settings.tsx b/src/routes/[owner]/[collection]/settings.tsx index 3287951..77af5df 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 && ( @@ -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() { >
+ 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() {
handleChangeRole(m.userId, e.target.value)} + onChange={(e) => handleChangeRole(m.id, e.target.value)} className="bg-parchment border-rule cursor-pointer border px-1.5 py-0.5 text-xs" > @@ -270,7 +228,7 @@ export default function OwnerSettingsMembers() {
{isAdmin && m.userId !== currentUser.id && ( - - -

Invite by email

+

Invite by email

)} - {/* Pending invitations */} {pendingInvitations.length > 0 && (

Pending Invitations

@@ -378,9 +295,11 @@ export default function OwnerSettingsMembers() {
{inv.email} {inv.role} - - Expires {new Date(inv.expiresAt).toLocaleDateString()} - + {inv.expiresAt && ( + + Expires {new Date(inv.expiresAt).toLocaleDateString()} + + )}
{isAdmin && (
- {/* Personal collections */} -
-

- Your Collections ({collections.length}) -

- - {collections.length === 0 ? ( -
-

No collections yet.

-

- Use the API to create your first collection. -

-
- ) : ( -
- {collections - .filter((c) => - matchesFilter(`${me.slug}/${c.slug} ${c.name} ${c.description ?? ''}`), - ) - .map((c) => ( - -
- - {me.slug}/{c.slug} - - {c.description && ( -

- {c.description} -

- )} -
- - {c.public ? 'public' : 'private'} - - - ))} -
- )} -
- - {/* Org collections */} + {/* Collections by org */} {orgs.map((org) => (
@@ -330,7 +276,7 @@ export default function Dashboard() {

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/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/settings/index.tsx b/src/routes/settings/index.tsx index b68fdeb..18289d4 100644 --- a/src/routes/settings/index.tsx +++ b/src/routes/settings/index.tsx @@ -238,7 +238,7 @@ export default function Settings() { 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 !== 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() {
-
- - -