mirror of
https://github.com/hanzoai/captable.git
synced 2026-10-16 14:21:29 +00:00
Merge branch 'main' into rbac-documents
This commit is contained in:
+4
-4
@@ -36,14 +36,14 @@
|
||||
"@blocknote/react": "^0.12.2",
|
||||
"@hono/swagger-ui": "^0.3.0",
|
||||
"@hono/zod-openapi": "^0.14.8",
|
||||
"@hookform/resolvers": "^3.6.0",
|
||||
"@hookform/resolvers": "^3.9.0",
|
||||
"@next-auth/prisma-adapter": "^1.0.7",
|
||||
"@prisma/client": "^5.13.0",
|
||||
"@prisma/generator-helper": "^5.9.1",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
@@ -68,7 +68,7 @@
|
||||
"@t3-oss/env-nextjs": "^0.10.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@tanstack/react-table": "^8.19.2",
|
||||
"@tremor/react": "^3.17.2",
|
||||
"@tremor/react": "^3.17.4",
|
||||
"@trpc/client": "^10.43.6",
|
||||
"@trpc/next": "^10.45.2",
|
||||
"@trpc/react-query": "^10.43.6",
|
||||
@@ -104,7 +104,7 @@
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-hook-form": "^7.51.5",
|
||||
"react-hook-form": "^7.52.1",
|
||||
"react-number-format": "^5.3.4",
|
||||
"react-pdf": "^8.0.2",
|
||||
"sharp": "^0.33.3",
|
||||
|
||||
Generated
+51
-52
@@ -27,8 +27,8 @@ importers:
|
||||
specifier: ^0.14.8
|
||||
version: 0.14.8([email protected])([email protected])
|
||||
'@hookform/resolvers':
|
||||
specifier: ^3.6.0
|
||||
version: 3.6.0([email protected]1.5([email protected]))
|
||||
specifier: ^3.9.0
|
||||
version: 3.9.0([email protected]2.1([email protected]))
|
||||
'@next-auth/prisma-adapter':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(@prisma/[email protected]([email protected]))([email protected]([email protected](@babel/[email protected])(@opentelemetry/[email protected])([email protected]([email protected]))([email protected]))([email protected])([email protected]([email protected]))([email protected]))
|
||||
@@ -48,8 +48,8 @@ importers:
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-checkbox':
|
||||
specifier: ^1.0.4
|
||||
version: 1.0.4(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.0.5
|
||||
version: 1.0.5(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
@@ -123,8 +123,8 @@ importers:
|
||||
specifier: ^8.19.2
|
||||
version: 8.19.2([email protected]([email protected]))([email protected])
|
||||
'@tremor/react':
|
||||
specifier: ^3.17.2
|
||||
version: 3.17.2([email protected]([email protected]))([email protected])([email protected])
|
||||
specifier: ^3.17.4
|
||||
version: 3.17.4([email protected]([email protected]))([email protected])([email protected])
|
||||
'@trpc/client':
|
||||
specifier: ^10.43.6
|
||||
version: 10.45.2(@trpc/[email protected])
|
||||
@@ -231,8 +231,8 @@ importers:
|
||||
specifier: ^14.2.3
|
||||
version: 14.2.3([email protected])
|
||||
react-hook-form:
|
||||
specifier: ^7.51.5
|
||||
version: 7.51.5([email protected])
|
||||
specifier: ^7.52.1
|
||||
version: 7.52.1([email protected])
|
||||
react-number-format:
|
||||
specifier: ^5.3.4
|
||||
version: 5.3.4([email protected]([email protected]))([email protected])
|
||||
@@ -1432,8 +1432,8 @@ packages:
|
||||
hono: '>=3.9.0'
|
||||
zod: ^3.19.1
|
||||
|
||||
'@hookform/resolvers@3.6.0':
|
||||
resolution: {integrity: sha512-UBcpyOX3+RR+dNnqBd0lchXpoL8p4xC21XP8H6Meb8uve5Br1GCnmg0PcBoKKqPKgGu9GHQ/oygcmPrQhetwqw==}
|
||||
'@hookform/resolvers@3.9.0':
|
||||
resolution: {integrity: sha512-bU0Gr4EepJ/EQsH/IwEzYLsT/PEj5C0ynLQ4m+GSHS+xKH4TfSelhluTgOaoc4kA5s7eCsQbM4wvZLzELmWzUg==}
|
||||
peerDependencies:
|
||||
react-hook-form: ^7.0.0
|
||||
|
||||
@@ -2068,13 +2068,13 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-checkbox@1.0.4':
|
||||
resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==}
|
||||
'@radix-ui/react-checkbox@1.1.1':
|
||||
resolution: {integrity: sha512-0i/EKJ222Afa1FE0C6pNJxDq1itzcl3HChE9DwskA4th4KRse8ojx8a1nVcOjwJdbpDLcz7uol77yYnQNMHdKw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
@@ -3607,8 +3607,8 @@ packages:
|
||||
react: ^17.0.0 || ^18.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0
|
||||
|
||||
'@tremor/[email protected].2':
|
||||
resolution: {integrity: sha512-zQovHBTonoeJbVk23VAqDeu9yL8N9N2884WYtLr0QvEo4kfC/YUYHkwyg5R7i8ahiJcYbR5zbCG8r9Y4Oqg9qQ==}
|
||||
'@tremor/[email protected].4':
|
||||
resolution: {integrity: sha512-teItTLbZglXJwakW8XYtRfIXZvp1Y6HKsaD9MC8cJqXG3vf1n6D9aPz3OKyWmDkBYE1Yjo/PTmEHKQIwN+2suQ==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: '>=16.6.0'
|
||||
@@ -7225,11 +7225,11 @@ packages:
|
||||
peerDependencies:
|
||||
react: '>= 16.8 || 18.0.0'
|
||||
|
||||
[email protected]1.5:
|
||||
resolution: {integrity: sha512-J2ILT5gWx1XUIJRETiA7M19iXHlG74+6O3KApzvqB/w8S5NQR7AbU8HVZrMALdmDgWpRPYiZJl0zx8Z4L2mP6Q==}
|
||||
[email protected]2.1:
|
||||
resolution: {integrity: sha512-uNKIhaoICJ5KQALYZ4TOaOLElyM+xipord+Ha3crEFhTntdLvWZqVY49Wqd/0GiVCA/f9NjemLeiNPjG7Hpurg==}
|
||||
engines: {node: '>=12.22.0'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
|
||||
[email protected]:
|
||||
resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==}
|
||||
@@ -8730,10 +8730,10 @@ snapshots:
|
||||
'@aws-crypto/sha1-browser': 3.0.0
|
||||
'@aws-crypto/sha256-browser': 3.0.0
|
||||
'@aws-crypto/sha256-js': 3.0.0
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/client-sts': 3.577.0
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0
|
||||
'@aws-sdk/client-sts': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/core': 3.576.0
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/middleware-bucket-endpoint': 3.577.0
|
||||
'@aws-sdk/middleware-expect-continue': 3.577.0
|
||||
'@aws-sdk/middleware-flexible-checksums': 3.577.0
|
||||
@@ -8788,13 +8788,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])':
|
||||
'@aws-sdk/[email protected]':
|
||||
dependencies:
|
||||
'@aws-crypto/sha256-browser': 3.0.0
|
||||
'@aws-crypto/sha256-js': 3.0.0
|
||||
'@aws-sdk/client-sts': 3.577.0
|
||||
'@aws-sdk/client-sts': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/core': 3.576.0
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/middleware-host-header': 3.577.0
|
||||
'@aws-sdk/middleware-logger': 3.577.0
|
||||
'@aws-sdk/middleware-recursion-detection': 3.577.0
|
||||
@@ -8831,7 +8831,6 @@ snapshots:
|
||||
'@smithy/util-utf8': 3.0.0
|
||||
tslib: 2.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-sts'
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected]':
|
||||
@@ -8877,13 +8876,13 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected]':
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])':
|
||||
dependencies:
|
||||
'@aws-crypto/sha256-browser': 3.0.0
|
||||
'@aws-crypto/sha256-js': 3.0.0
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0
|
||||
'@aws-sdk/core': 3.576.0
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-node': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/middleware-host-header': 3.577.0
|
||||
'@aws-sdk/middleware-logger': 3.577.0
|
||||
'@aws-sdk/middleware-recursion-detection': 3.577.0
|
||||
@@ -8920,6 +8919,7 @@ snapshots:
|
||||
'@smithy/util-utf8': 3.0.0
|
||||
tslib: 2.6.2
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/client-sso-oidc'
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected]':
|
||||
@@ -8951,13 +8951,13 @@ snapshots:
|
||||
'@smithy/util-stream': 3.0.1
|
||||
tslib: 2.6.2
|
||||
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])(@aws-sdk/[email protected])':
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))':
|
||||
dependencies:
|
||||
'@aws-sdk/client-sts': 3.577.0
|
||||
'@aws-sdk/client-sts': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-env': 3.577.0
|
||||
'@aws-sdk/credential-provider-process': 3.577.0
|
||||
'@aws-sdk/credential-provider-sso': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/types': 3.577.0
|
||||
'@smithy/credential-provider-imds': 3.0.0
|
||||
'@smithy/property-provider': 3.0.0
|
||||
@@ -8968,14 +8968,14 @@ snapshots:
|
||||
- '@aws-sdk/client-sso-oidc'
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])(@aws-sdk/[email protected])':
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))':
|
||||
dependencies:
|
||||
'@aws-sdk/credential-provider-env': 3.577.0
|
||||
'@aws-sdk/credential-provider-http': 3.577.0
|
||||
'@aws-sdk/credential-provider-ini': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-ini': 3.577.0(@aws-sdk/[email protected])(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/credential-provider-process': 3.577.0
|
||||
'@aws-sdk/credential-provider-sso': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/credential-provider-web-identity': 3.577.0(@aws-sdk/[email protected](@aws-sdk/[email protected]))
|
||||
'@aws-sdk/types': 3.577.0
|
||||
'@smithy/credential-provider-imds': 3.0.0
|
||||
'@smithy/property-provider': 3.0.0
|
||||
@@ -9008,9 +9008,9 @@ snapshots:
|
||||
- '@aws-sdk/client-sso-oidc'
|
||||
- aws-crt
|
||||
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])':
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected](@aws-sdk/[email protected]))':
|
||||
dependencies:
|
||||
'@aws-sdk/client-sts': 3.577.0
|
||||
'@aws-sdk/client-sts': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/types': 3.577.0
|
||||
'@smithy/property-provider': 3.0.0
|
||||
'@smithy/types': 3.0.0
|
||||
@@ -9137,7 +9137,7 @@ snapshots:
|
||||
|
||||
'@aws-sdk/[email protected](@aws-sdk/[email protected])':
|
||||
dependencies:
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0(@aws-sdk/[email protected])
|
||||
'@aws-sdk/client-sso-oidc': 3.577.0
|
||||
'@aws-sdk/types': 3.577.0
|
||||
'@smithy/property-provider': 3.0.0
|
||||
'@smithy/shared-ini-file-loader': 3.0.0
|
||||
@@ -9988,9 +9988,9 @@ snapshots:
|
||||
hono: 4.4.8
|
||||
zod: 3.23.8
|
||||
|
||||
'@hookform/resolvers@3.6.0([email protected]1.5([email protected]))':
|
||||
'@hookform/resolvers@3.9.0([email protected]2.1([email protected]))':
|
||||
dependencies:
|
||||
react-hook-form: 7.51.5([email protected])
|
||||
react-hook-form: 7.52.1([email protected])
|
||||
|
||||
'@img/[email protected]':
|
||||
optionalDependencies:
|
||||
@@ -10731,17 +10731,16 @@ snapshots:
|
||||
'@types/react': 18.3.2
|
||||
'@types/react-dom': 18.3.0
|
||||
|
||||
'@radix-ui/react-checkbox@1.0.4(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
||||
'@radix-ui/react-checkbox@1.1.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.24.5
|
||||
'@radix-ui/primitive': 1.0.1
|
||||
'@radix-ui/react-compose-refs': 1.0.1(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-context': 1.0.1(@types/react@18.3.2)([email protected])
|
||||
'@radix-ui/react-presence': 1.0.1(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-primitive': 1.0.3(@types/react[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-use-controllable-state': 1.0.1(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-use-previous': 1.0.1(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-use-size': 1.0.1(@types/[email protected])([email protected])
|
||||
'@radix-ui/primitive': 1.1.0
|
||||
'@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-context': 1.1.0(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-presence': 1.1.0(@types/react[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.2)([email protected])
|
||||
'@radix-ui/react-use-previous': 1.1.0(@types/[email protected])([email protected])
|
||||
'@radix-ui/react-use-size': 1.1.0(@types/[email protected])([email protected])
|
||||
react: 18.3.1
|
||||
react-dom: 18.2.0([email protected])
|
||||
optionalDependencies:
|
||||
@@ -12793,7 +12792,7 @@ snapshots:
|
||||
react: 18.3.1
|
||||
react-dom: 18.2.0([email protected])
|
||||
|
||||
'@tremor/[email protected].2([email protected]([email protected]))([email protected])([email protected])':
|
||||
'@tremor/[email protected].4([email protected]([email protected]))([email protected])([email protected])':
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.19.2([email protected]([email protected]))([email protected])
|
||||
'@headlessui/react': 1.7.19([email protected]([email protected]))([email protected])
|
||||
@@ -17531,7 +17530,7 @@ snapshots:
|
||||
prop-types: 15.8.1
|
||||
react: 18.3.1
|
||||
|
||||
[email protected]1.5([email protected]):
|
||||
[email protected]2.1([email protected]):
|
||||
dependencies:
|
||||
react: 18.3.1
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `userId` on the `ApiKey` table. All the data in the column will be lost.
|
||||
- Added the required column `companyId` to the `ApiKey` table without a default value. This is not possible if the table is not empty.
|
||||
- Added the required column `membershipId` to the `ApiKey` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- DropIndex
|
||||
DROP INDEX "ApiKey_userId_active_idx";
|
||||
|
||||
-- DropIndex
|
||||
DROP INDEX "ApiKey_userId_idx";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ApiKey" DROP COLUMN "userId",
|
||||
ADD COLUMN "companyId" TEXT NOT NULL,
|
||||
ADD COLUMN "membershipId" TEXT NOT NULL;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKey_membershipId_idx" ON "ApiKey"("membershipId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKey_membershipId_active_idx" ON "ApiKey"("membershipId", "active");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ApiKey_companyId_idx" ON "ApiKey"("companyId");
|
||||
+11
-6
@@ -60,7 +60,6 @@ model User {
|
||||
companies Member[]
|
||||
passkeys Passkey[]
|
||||
verificationToken VerificationToken[]
|
||||
apiKeys ApiKey[]
|
||||
|
||||
lastSignedIn DateTime @default(now())
|
||||
identityProvider String?
|
||||
@@ -156,7 +155,8 @@ model Company {
|
||||
dataRooms DataRoom[]
|
||||
eSignAudits EsignAudit[]
|
||||
billingCustomers BillingCustomer[]
|
||||
Role CustomRole[]
|
||||
customRoles CustomRole[]
|
||||
apiKeys ApiKey[]
|
||||
|
||||
@@unique([publicId])
|
||||
}
|
||||
@@ -198,6 +198,7 @@ model Member {
|
||||
updates Update[]
|
||||
dataRooms DataRoomRecipient[]
|
||||
UpdateRecipient UpdateRecipient[]
|
||||
ApiKey ApiKey[]
|
||||
|
||||
@@unique([companyId, userId])
|
||||
@@index([companyId])
|
||||
@@ -1099,10 +1100,14 @@ model ApiKey {
|
||||
updatedAt DateTime @updatedAt
|
||||
lastUsed DateTime?
|
||||
|
||||
userId String
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
membershipId String
|
||||
member Member @relation(fields: [membershipId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@index([userId])
|
||||
company Company @relation(fields: [companyId], references: [id])
|
||||
companyId String
|
||||
|
||||
@@index([membershipId])
|
||||
@@index([keyId, active])
|
||||
@@index([userId, active])
|
||||
@@index([membershipId, active])
|
||||
@@index([companyId])
|
||||
}
|
||||
|
||||
@@ -2,13 +2,18 @@
|
||||
|
||||
import { dayjsExt } from "@/common/dayjs";
|
||||
import Tldr from "@/components/common/tldr";
|
||||
import { Allow } from "@/components/rbac/allow";
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { api } from "@/trpc/react";
|
||||
import { RiMore2Fill } from "@remixicon/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { toast } from "sonner";
|
||||
|
||||
import { ConfirmDialog } from "@/components/common/confirmDialog";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -25,6 +30,55 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { api } from "@/trpc/react";
|
||||
import { RiMore2Fill } from "@remixicon/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface DeleteDialogProps {
|
||||
keyId: string;
|
||||
open: boolean;
|
||||
setOpen: (val: boolean) => void;
|
||||
}
|
||||
|
||||
function DeleteKey({ keyId, open, setOpen }: DeleteDialogProps) {
|
||||
const router = useRouter();
|
||||
|
||||
const deleteMutation = api.apiKey.delete.useMutation({
|
||||
onSuccess: ({ message }) => {
|
||||
toast.success(message);
|
||||
router.refresh();
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
toast.error("An error occurred while creating the API key.");
|
||||
},
|
||||
});
|
||||
return (
|
||||
<AlertDialog open={open} onOpenChange={setOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Are you sure you want to delete this key? This action cannot be
|
||||
undone and you will loose the access if this API key is currently
|
||||
being used.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
onClick={() => deleteMutation.mutateAsync({ keyId })}
|
||||
>
|
||||
Continue
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
interface ApiKey {
|
||||
keyId: string;
|
||||
@@ -33,27 +87,7 @@ interface ApiKey {
|
||||
}
|
||||
|
||||
const ApiKeysTable = ({ keys }: { keys: ApiKey[] }) => {
|
||||
const router = useRouter();
|
||||
|
||||
const deleteMutation = api.apiKey.delete.useMutation({
|
||||
onSuccess: ({ message }) => {
|
||||
toast.success(message);
|
||||
},
|
||||
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
toast.error("An error occurred while creating the API key.");
|
||||
},
|
||||
|
||||
onSettled: () => {
|
||||
router.refresh();
|
||||
},
|
||||
});
|
||||
|
||||
const dialogTitle = "Are you sure?";
|
||||
const dialogBody =
|
||||
"Are you sure you want to delete this key? This action cannot be undone and you will loose the access if this API key is currently being used.";
|
||||
const dialogTrigger = <div className="text-rose-600">Delete key</div>;
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<Card className="mx-auto mt-3 w-[28rem] sm:w-[38rem] md:w-full">
|
||||
@@ -103,17 +137,23 @@ const ApiKeysTable = ({ keys }: { keys: ApiKey[] }) => {
|
||||
Rotate key
|
||||
</DropdownMenuItem>
|
||||
|
||||
<ConfirmDialog
|
||||
key={key.keyId}
|
||||
title={dialogTitle}
|
||||
body={dialogBody}
|
||||
trigger={dialogTrigger}
|
||||
onConfirm={() => {
|
||||
deleteMutation.mutate({ keyId: key.keyId });
|
||||
}}
|
||||
/>
|
||||
<Allow action="delete" subject="api-keys">
|
||||
{(allow) => (
|
||||
<DropdownMenuItem
|
||||
disabled={!allow}
|
||||
onSelect={() => setOpen(true)}
|
||||
>
|
||||
Delete key
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</Allow>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<DeleteKey
|
||||
open={open}
|
||||
setOpen={(val) => setOpen(val)}
|
||||
keyId={key.keyId}
|
||||
/>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import EmptyState from "@/components/common/empty-state";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { withServerComponentSession } from "@/server/auth";
|
||||
import { db } from "@/server/db";
|
||||
import { UnAuthorizedState } from "@/components/ui/un-authorized-state";
|
||||
import { serverAccessControl } from "@/lib/rbac/access-control";
|
||||
import { api } from "@/trpc/server";
|
||||
import { RiTerminalBoxFill } from "@remixicon/react";
|
||||
import type { Metadata } from "next";
|
||||
import { Fragment } from "react";
|
||||
@@ -12,31 +12,17 @@ export const metadata: Metadata = {
|
||||
title: "API Keys",
|
||||
};
|
||||
const ApiSettingsPage = async () => {
|
||||
const session = await withServerComponentSession();
|
||||
const { user } = session;
|
||||
const apiKeys = await db.apiKey.findMany({
|
||||
where: {
|
||||
userId: user.id,
|
||||
active: true,
|
||||
},
|
||||
const { allow } = await serverAccessControl();
|
||||
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
const data = await allow(api.apiKey.listAll.query(), ["api-keys", "read"]);
|
||||
|
||||
select: {
|
||||
id: true,
|
||||
keyId: true,
|
||||
createdAt: true,
|
||||
lastUsed: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log({ apiKeys });
|
||||
if (!data) {
|
||||
return <UnAuthorizedState />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{apiKeys.length === 0 ? (
|
||||
{data.apiKeys.length === 0 ? (
|
||||
<EmptyState
|
||||
bordered={false}
|
||||
icon={<RiTerminalBoxFill />}
|
||||
@@ -60,7 +46,7 @@ const ApiSettingsPage = async () => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ApiKeysTable keys={apiKeys} />
|
||||
<ApiKeysTable keys={data.apiKeys} />
|
||||
</div>
|
||||
)}
|
||||
</Fragment>
|
||||
|
||||
@@ -27,7 +27,7 @@ const EmptyState = ({
|
||||
children,
|
||||
}: EmptyStateProps) => {
|
||||
return (
|
||||
<section className="overflow-hidden">
|
||||
<div role="alert" className="overflow-hidden">
|
||||
<div className="container mx-auto px-4">
|
||||
<div
|
||||
className={cn(
|
||||
@@ -46,7 +46,7 @@ const EmptyState = ({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
+2
-2
@@ -10,8 +10,8 @@ export const createHash = async (key: string) => {
|
||||
.toString();
|
||||
};
|
||||
|
||||
export const createApiToken = async () => {
|
||||
return await randomBytes(32).toString("base64url");
|
||||
export const createApiToken = () => {
|
||||
return randomBytes(32).toString("base64url");
|
||||
};
|
||||
|
||||
export const createSecureHash = (key: string) => {
|
||||
|
||||
@@ -4,5 +4,6 @@ export const SUBJECTS = [
|
||||
"stakeholder",
|
||||
"roles",
|
||||
"documents",
|
||||
"api-keys",
|
||||
] as const;
|
||||
export type TSubjects = (typeof SUBJECTS)[number];
|
||||
|
||||
@@ -94,7 +94,12 @@ const verifyBearerToken = async (bearerToken: string | null | undefined) => {
|
||||
|
||||
const apiKey = await db.apiKey.findUnique({
|
||||
where: { keyId, active: true },
|
||||
select: { id: true, keyId: true, hashedToken: true, user: true },
|
||||
select: {
|
||||
id: true,
|
||||
keyId: true,
|
||||
hashedToken: true,
|
||||
member: { select: { user: true } },
|
||||
},
|
||||
});
|
||||
|
||||
if (!apiKey) {
|
||||
@@ -118,5 +123,5 @@ const verifyBearerToken = async (bearerToken: string | null | undefined) => {
|
||||
data: { lastUsed: new Date() },
|
||||
});
|
||||
|
||||
return apiKey.user;
|
||||
return apiKey.member.user;
|
||||
};
|
||||
|
||||
@@ -1,22 +1,58 @@
|
||||
import { generatePublicId } from "@/common/id";
|
||||
import { createApiToken, createSecureHash } from "@/lib/crypto";
|
||||
import { createTRPCRouter, withAuth } from "@/trpc/api/trpc";
|
||||
import { createTRPCRouter, withAccessControl } from "@/trpc/api/trpc";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import z from "zod";
|
||||
|
||||
export const apiKeyRouter = createTRPCRouter({
|
||||
create: withAuth.mutation(async ({ ctx }) => {
|
||||
const { db, session } = ctx;
|
||||
const user = session.user;
|
||||
listAll: withAccessControl
|
||||
.meta({ policies: { "api-keys": { allow: ["read"] } } })
|
||||
.query(async ({ ctx }) => {
|
||||
const {
|
||||
db,
|
||||
membership: { companyId, memberId },
|
||||
} = ctx;
|
||||
|
||||
const data = await db.$transaction(async (tx) => {
|
||||
const token = await createApiToken();
|
||||
const apiKeys = await db.apiKey.findMany({
|
||||
where: {
|
||||
active: true,
|
||||
companyId,
|
||||
membershipId: memberId,
|
||||
},
|
||||
|
||||
orderBy: {
|
||||
createdAt: "desc",
|
||||
},
|
||||
|
||||
select: {
|
||||
id: true,
|
||||
keyId: true,
|
||||
createdAt: true,
|
||||
lastUsed: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
apiKeys,
|
||||
};
|
||||
}),
|
||||
create: withAccessControl
|
||||
.meta({ policies: { "api-keys": { allow: ["create"] } } })
|
||||
.mutation(async ({ ctx }) => {
|
||||
const {
|
||||
db,
|
||||
membership: { companyId, memberId },
|
||||
} = ctx;
|
||||
|
||||
const token = createApiToken();
|
||||
const keyId = generatePublicId();
|
||||
const hashedToken = await createSecureHash(token);
|
||||
const hashedToken = createSecureHash(token);
|
||||
|
||||
const key = await tx.apiKey.create({
|
||||
const key = await db.apiKey.create({
|
||||
data: {
|
||||
keyId,
|
||||
userId: user.id,
|
||||
companyId,
|
||||
membershipId: memberId,
|
||||
hashedToken,
|
||||
},
|
||||
});
|
||||
@@ -26,24 +62,24 @@ export const apiKeyRouter = createTRPCRouter({
|
||||
keyId: key.keyId,
|
||||
createdAt: key.createdAt,
|
||||
};
|
||||
});
|
||||
}),
|
||||
|
||||
return data;
|
||||
}),
|
||||
|
||||
delete: withAuth
|
||||
delete: withAccessControl
|
||||
.input(z.object({ keyId: z.string() }))
|
||||
.meta({ policies: { "api-keys": { allow: ["delete"] } } })
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const {
|
||||
db,
|
||||
membership: { memberId, companyId },
|
||||
} = ctx;
|
||||
const { keyId } = input;
|
||||
try {
|
||||
const { db } = ctx;
|
||||
const { keyId } = input;
|
||||
|
||||
await db.$transaction(async (tx) => {
|
||||
await tx.apiKey.delete({
|
||||
where: {
|
||||
keyId,
|
||||
},
|
||||
});
|
||||
await db.apiKey.delete({
|
||||
where: {
|
||||
keyId,
|
||||
membershipId: memberId,
|
||||
companyId,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user