@delmaredigital/payload-better-auth 0.5.6 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/management/ApiKeysManagementClient.d.ts +16 -7
- package/dist/components/management/ApiKeysManagementClient.js +293 -286
- package/dist/components/management/views/ApiKeysView.js +55 -9
- package/dist/index.d.ts +5 -5
- package/dist/index.js +5 -5
- package/dist/plugin/index.d.ts +8 -8
- package/dist/plugin/index.js +131 -63
- package/dist/types/apiKey.d.ts +19 -47
- package/dist/types/apiKey.js +10 -5
- package/dist/utils/apiKeyAccess.d.ts +47 -95
- package/dist/utils/apiKeyAccess.js +128 -284
- package/dist/utils/generatePermissions.d.ts +11 -0
- package/dist/utils/generatePermissions.js +30 -0
- package/package.json +1 -1
- package/dist/utils/generateScopes.d.ts +0 -20
- package/dist/utils/generateScopes.js +0 -110
|
@@ -2,59 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useMemo, useRef } from 'react';
|
|
4
4
|
import { createAuthClient } from 'better-auth/react';
|
|
5
|
-
/**
|
|
6
|
-
* Group scopes by collection for the UI.
|
|
7
|
-
* Scopes like "posts:read", "posts:write" get grouped under "Posts"
|
|
8
|
-
*/ function groupScopesByCollection(scopes) {
|
|
9
|
-
const groups = new Map();
|
|
10
|
-
for (const scope of scopes){
|
|
11
|
-
// Parse scope ID like "posts:read" -> collection="posts", type="read"
|
|
12
|
-
const colonIndex = scope.id.indexOf(':');
|
|
13
|
-
let collection;
|
|
14
|
-
let type;
|
|
15
|
-
if (colonIndex > 0) {
|
|
16
|
-
collection = scope.id.substring(0, colonIndex);
|
|
17
|
-
const typeStr = scope.id.substring(colonIndex + 1);
|
|
18
|
-
type = [
|
|
19
|
-
'read',
|
|
20
|
-
'write',
|
|
21
|
-
'delete'
|
|
22
|
-
].includes(typeStr) ? typeStr : 'other';
|
|
23
|
-
} else {
|
|
24
|
-
// No colon - treat as standalone scope
|
|
25
|
-
collection = scope.id;
|
|
26
|
-
type = 'other';
|
|
27
|
-
}
|
|
28
|
-
if (!groups.has(collection)) {
|
|
29
|
-
// Create label from collection slug (posts -> Posts)
|
|
30
|
-
const label = collection.charAt(0).toUpperCase() + collection.slice(1).replace(/-/g, ' ');
|
|
31
|
-
groups.set(collection, {
|
|
32
|
-
collection,
|
|
33
|
-
label,
|
|
34
|
-
scopes: []
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
groups.get(collection).scopes.push({
|
|
38
|
-
type,
|
|
39
|
-
scope
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
// Sort groups alphabetically, sort scopes within group by type order
|
|
43
|
-
const typeOrder = {
|
|
44
|
-
read: 0,
|
|
45
|
-
write: 1,
|
|
46
|
-
delete: 2,
|
|
47
|
-
other: 3
|
|
48
|
-
};
|
|
49
|
-
return Array.from(groups.values()).sort((a, b)=>a.label.localeCompare(b.label)).map((group)=>({
|
|
50
|
-
...group,
|
|
51
|
-
scopes: group.scopes.sort((a, b)=>typeOrder[a.type] - typeOrder[b.type])
|
|
52
|
-
}));
|
|
53
|
-
}
|
|
54
5
|
/**
|
|
55
6
|
* Client component for API keys management.
|
|
56
|
-
* Lists, creates, and deletes API keys with
|
|
57
|
-
*/ export function ApiKeysManagementClient({ authClient: providedClient, title = 'API Keys',
|
|
7
|
+
* Lists, creates, and deletes API keys with permission selection (read/write per collection).
|
|
8
|
+
*/ export function ApiKeysManagementClient({ authClient: providedClient, title = 'API Keys', permissions = [], organizations = [] } = {}) {
|
|
58
9
|
const [apiKeys, setApiKeys] = useState([]);
|
|
59
10
|
const [loading, setLoading] = useState(true);
|
|
60
11
|
const [error, setError] = useState(null);
|
|
@@ -63,32 +14,12 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
63
14
|
const [showCreateForm, setShowCreateForm] = useState(false);
|
|
64
15
|
const [newKeyName, setNewKeyName] = useState('');
|
|
65
16
|
const [newKeyExpiry, setNewKeyExpiry] = useState('');
|
|
66
|
-
|
|
17
|
+
// Selected permissions: { posts: ['read', 'write'], pages: ['read'] }
|
|
18
|
+
const [selectedPermissions, setSelectedPermissions] = useState({});
|
|
67
19
|
const [newlyCreatedKey, setNewlyCreatedKey] = useState(null);
|
|
68
|
-
const [
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
const scopeGroups = useMemo(()=>groupScopesByCollection(availableScopes), [
|
|
72
|
-
availableScopes
|
|
73
|
-
]);
|
|
74
|
-
// Get all scope IDs by type for bulk actions
|
|
75
|
-
const scopesByType = useMemo(()=>{
|
|
76
|
-
const result = {
|
|
77
|
-
read: [],
|
|
78
|
-
write: [],
|
|
79
|
-
delete: []
|
|
80
|
-
};
|
|
81
|
-
for (const group of scopeGroups){
|
|
82
|
-
for (const { type, scope } of group.scopes){
|
|
83
|
-
if (type === 'read' || type === 'write' || type === 'delete') {
|
|
84
|
-
result[type].push(scope.id);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
return result;
|
|
89
|
-
}, [
|
|
90
|
-
scopeGroups
|
|
91
|
-
]);
|
|
20
|
+
const [selectedOrganizationId, setSelectedOrganizationId] = useState('');
|
|
21
|
+
const hasPermissions = permissions.length > 0;
|
|
22
|
+
const hasOrganizations = organizations.length > 0;
|
|
92
23
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
24
|
const clientRef = useRef(null);
|
|
94
25
|
const getClient = async ()=>{
|
|
@@ -102,100 +33,140 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
102
33
|
});
|
|
103
34
|
return clientRef.current;
|
|
104
35
|
};
|
|
105
|
-
// Toggle a
|
|
106
|
-
function
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
setSelectedScopes((prev)=>prev.filter((id)=>!groupScopeIds.includes(id)));
|
|
119
|
-
} else {
|
|
120
|
-
// Select all in group
|
|
121
|
-
setSelectedScopes((prev)=>[
|
|
122
|
-
...new Set([
|
|
36
|
+
// Toggle a specific action for a collection
|
|
37
|
+
function toggleAction(slug, action) {
|
|
38
|
+
setSelectedPermissions((prev)=>{
|
|
39
|
+
const current = prev[slug] ?? [];
|
|
40
|
+
if (current.includes(action)) {
|
|
41
|
+
// Remove action — if removing 'read' also remove 'write'
|
|
42
|
+
if (action === 'read') {
|
|
43
|
+
const filtered = current.filter((a)=>a !== 'read' && a !== 'write');
|
|
44
|
+
if (filtered.length === 0) {
|
|
45
|
+
const { [slug]: _, ...rest } = prev;
|
|
46
|
+
return rest;
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
123
49
|
...prev,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
50
|
+
[slug]: filtered
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const filtered = current.filter((a)=>a !== action);
|
|
54
|
+
if (filtered.length === 0) {
|
|
55
|
+
const { [slug]: _, ...rest } = prev;
|
|
56
|
+
return rest;
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
...prev,
|
|
60
|
+
[slug]: filtered
|
|
61
|
+
};
|
|
135
62
|
} else {
|
|
136
|
-
|
|
63
|
+
// Add action — if adding 'write' also add 'read'
|
|
64
|
+
if (action === 'write') {
|
|
65
|
+
return {
|
|
66
|
+
...prev,
|
|
67
|
+
[slug]: [
|
|
68
|
+
...new Set([
|
|
69
|
+
...current,
|
|
70
|
+
'read',
|
|
71
|
+
'write'
|
|
72
|
+
])
|
|
73
|
+
]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
...prev,
|
|
78
|
+
[slug]: [
|
|
79
|
+
...current,
|
|
80
|
+
action
|
|
81
|
+
]
|
|
82
|
+
};
|
|
137
83
|
}
|
|
138
|
-
return next;
|
|
139
84
|
});
|
|
140
85
|
}
|
|
141
|
-
// Bulk toggle all
|
|
142
|
-
function toggleAllOfType(
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
86
|
+
// Bulk toggle all of an action type
|
|
87
|
+
function toggleAllOfType(action) {
|
|
88
|
+
const allHave = permissions.every((p)=>(selectedPermissions[p.slug] ?? []).includes(action));
|
|
89
|
+
if (allHave) {
|
|
90
|
+
// Remove this action from all — if removing 'read' also remove 'write'
|
|
91
|
+
setSelectedPermissions((prev)=>{
|
|
92
|
+
const next = {};
|
|
93
|
+
for (const [slug, actions] of Object.entries(prev)){
|
|
94
|
+
const filtered = action === 'read' ? actions.filter((a)=>a !== 'read' && a !== 'write') : actions.filter((a)=>a !== action);
|
|
95
|
+
if (filtered.length > 0) next[slug] = filtered;
|
|
96
|
+
}
|
|
97
|
+
return next;
|
|
98
|
+
});
|
|
147
99
|
} else {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
100
|
+
// Add this action to all — if 'write' also add 'read'
|
|
101
|
+
setSelectedPermissions((prev)=>{
|
|
102
|
+
const next = {
|
|
103
|
+
...prev
|
|
104
|
+
};
|
|
105
|
+
for (const p of permissions){
|
|
106
|
+
const current = next[p.slug] ?? [];
|
|
107
|
+
if (action === 'write') {
|
|
108
|
+
next[p.slug] = [
|
|
109
|
+
...new Set([
|
|
110
|
+
...current,
|
|
111
|
+
'read',
|
|
112
|
+
'write'
|
|
113
|
+
])
|
|
114
|
+
];
|
|
115
|
+
} else {
|
|
116
|
+
next[p.slug] = [
|
|
117
|
+
...new Set([
|
|
118
|
+
...current,
|
|
119
|
+
action
|
|
120
|
+
])
|
|
121
|
+
];
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return next;
|
|
125
|
+
});
|
|
154
126
|
}
|
|
155
127
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return scopesByType[type].length > 0 && scopesByType[type].every((id)=>selectedScopes.includes(id));
|
|
128
|
+
function isAllOfTypeSelected(action) {
|
|
129
|
+
return permissions.length > 0 && permissions.every((p)=>(selectedPermissions[p.slug] ?? []).includes(action));
|
|
159
130
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const selectedCount = typeScopes.filter((id)=>selectedScopes.includes(id)).length;
|
|
164
|
-
return selectedCount > 0 && selectedCount < typeScopes.length;
|
|
131
|
+
function isSomeOfTypeSelected(action) {
|
|
132
|
+
const count = permissions.filter((p)=>(selectedPermissions[p.slug] ?? []).includes(action)).length;
|
|
133
|
+
return count > 0 && count < permissions.length;
|
|
165
134
|
}
|
|
166
|
-
// Clear all selections
|
|
167
135
|
function clearAll() {
|
|
168
|
-
|
|
136
|
+
setSelectedPermissions({});
|
|
169
137
|
}
|
|
170
|
-
// Select all scopes
|
|
171
138
|
function selectAll() {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
return 'some';
|
|
181
|
-
}
|
|
182
|
-
// Get scope label by ID
|
|
183
|
-
function getScopeLabel(scopeId) {
|
|
184
|
-
const scope = availableScopes.find((s)=>s.id === scopeId);
|
|
185
|
-
return scope?.label ?? scopeId;
|
|
139
|
+
const next = {};
|
|
140
|
+
for (const p of permissions){
|
|
141
|
+
next[p.slug] = [
|
|
142
|
+
'read',
|
|
143
|
+
'write'
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
setSelectedPermissions(next);
|
|
186
147
|
}
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
case 'write':
|
|
193
|
-
return 'Write';
|
|
194
|
-
case 'delete':
|
|
195
|
-
return 'Delete';
|
|
196
|
-
default:
|
|
197
|
-
return 'Access';
|
|
148
|
+
// Count total selected actions
|
|
149
|
+
const selectedCount = useMemo(()=>{
|
|
150
|
+
let count = 0;
|
|
151
|
+
for (const actions of Object.values(selectedPermissions)){
|
|
152
|
+
count += actions.length;
|
|
198
153
|
}
|
|
154
|
+
return count;
|
|
155
|
+
}, [
|
|
156
|
+
selectedPermissions
|
|
157
|
+
]);
|
|
158
|
+
// Format permissions for display
|
|
159
|
+
function formatPermissions(perms) {
|
|
160
|
+
if (!perms) return [];
|
|
161
|
+
const labels = [];
|
|
162
|
+
for (const [slug, actions] of Object.entries(perms)){
|
|
163
|
+
const def = permissions.find((p)=>p.slug === slug);
|
|
164
|
+
const label = def?.label ?? slug;
|
|
165
|
+
for (const action of actions){
|
|
166
|
+
labels.push(`${label}: ${action}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return labels;
|
|
199
170
|
}
|
|
200
171
|
useEffect(()=>{
|
|
201
172
|
fetchApiKeys();
|
|
@@ -226,16 +197,19 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
226
197
|
setNewlyCreatedKey(null);
|
|
227
198
|
try {
|
|
228
199
|
const client = await getClient();
|
|
229
|
-
// Send scopes to server - server will convert to permissions
|
|
230
200
|
const createOptions = {
|
|
231
201
|
name: newKeyName
|
|
232
202
|
};
|
|
233
203
|
if (newKeyExpiry) {
|
|
234
|
-
createOptions.expiresIn = parseInt(newKeyExpiry) * 24 * 60 * 60;
|
|
204
|
+
createOptions.expiresIn = parseInt(newKeyExpiry) * 24 * 60 * 60;
|
|
235
205
|
}
|
|
236
|
-
//
|
|
237
|
-
if (
|
|
238
|
-
createOptions.
|
|
206
|
+
// Send permissions directly in BA's native format
|
|
207
|
+
if (hasPermissions && selectedCount > 0) {
|
|
208
|
+
createOptions.permissions = selectedPermissions;
|
|
209
|
+
}
|
|
210
|
+
// Bind to organization if selected
|
|
211
|
+
if (selectedOrganizationId) {
|
|
212
|
+
createOptions.organizationId = selectedOrganizationId;
|
|
239
213
|
}
|
|
240
214
|
const result = await client.apiKey.create(createOptions);
|
|
241
215
|
if (result.error) {
|
|
@@ -245,7 +219,8 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
245
219
|
setShowCreateForm(false);
|
|
246
220
|
setNewKeyName('');
|
|
247
221
|
setNewKeyExpiry('');
|
|
248
|
-
|
|
222
|
+
setSelectedPermissions({});
|
|
223
|
+
setSelectedOrganizationId('');
|
|
249
224
|
fetchApiKeys();
|
|
250
225
|
}
|
|
251
226
|
} catch {
|
|
@@ -480,7 +455,54 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
480
455
|
})
|
|
481
456
|
]
|
|
482
457
|
}),
|
|
483
|
-
|
|
458
|
+
hasOrganizations && /*#__PURE__*/ _jsxs("div", {
|
|
459
|
+
style: {
|
|
460
|
+
marginBottom: 'var(--base)'
|
|
461
|
+
},
|
|
462
|
+
children: [
|
|
463
|
+
/*#__PURE__*/ _jsx("label", {
|
|
464
|
+
style: {
|
|
465
|
+
display: 'block',
|
|
466
|
+
color: 'var(--theme-text)',
|
|
467
|
+
fontSize: 'var(--font-size-small)',
|
|
468
|
+
marginBottom: 'calc(var(--base) * 0.25)'
|
|
469
|
+
},
|
|
470
|
+
children: "Organization (optional)"
|
|
471
|
+
}),
|
|
472
|
+
/*#__PURE__*/ _jsxs("select", {
|
|
473
|
+
value: selectedOrganizationId,
|
|
474
|
+
onChange: (e)=>setSelectedOrganizationId(e.target.value),
|
|
475
|
+
style: {
|
|
476
|
+
width: '100%',
|
|
477
|
+
padding: 'calc(var(--base) * 0.5)',
|
|
478
|
+
background: 'var(--theme-input-bg)',
|
|
479
|
+
border: '1px solid var(--theme-elevation-150)',
|
|
480
|
+
borderRadius: 'var(--style-radius-s)',
|
|
481
|
+
color: 'var(--theme-text)',
|
|
482
|
+
boxSizing: 'border-box'
|
|
483
|
+
},
|
|
484
|
+
children: [
|
|
485
|
+
/*#__PURE__*/ _jsx("option", {
|
|
486
|
+
value: "",
|
|
487
|
+
children: "No organization (global key)"
|
|
488
|
+
}),
|
|
489
|
+
organizations.map((org)=>/*#__PURE__*/ _jsx("option", {
|
|
490
|
+
value: String(org.id),
|
|
491
|
+
children: org.name
|
|
492
|
+
}, String(org.id)))
|
|
493
|
+
]
|
|
494
|
+
}),
|
|
495
|
+
/*#__PURE__*/ _jsx("div", {
|
|
496
|
+
style: {
|
|
497
|
+
marginTop: 'calc(var(--base) * 0.25)',
|
|
498
|
+
fontSize: '11px',
|
|
499
|
+
color: 'var(--theme-elevation-600)'
|
|
500
|
+
},
|
|
501
|
+
children: selectedOrganizationId ? 'API key will only have access to this organization\'s data.' : 'Without an organization, the key will not have org-scoped access.'
|
|
502
|
+
})
|
|
503
|
+
]
|
|
504
|
+
}),
|
|
505
|
+
hasPermissions && /*#__PURE__*/ _jsxs("div", {
|
|
484
506
|
style: {
|
|
485
507
|
marginBottom: 'var(--base)'
|
|
486
508
|
},
|
|
@@ -514,12 +536,6 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
514
536
|
indeterminate: isSomeOfTypeSelected('write'),
|
|
515
537
|
onClick: ()=>toggleAllOfType('write')
|
|
516
538
|
}),
|
|
517
|
-
/*#__PURE__*/ _jsx(BulkButton, {
|
|
518
|
-
label: "All Delete",
|
|
519
|
-
active: isAllOfTypeSelected('delete'),
|
|
520
|
-
indeterminate: isSomeOfTypeSelected('delete'),
|
|
521
|
-
onClick: ()=>toggleAllOfType('delete')
|
|
522
|
-
}),
|
|
523
539
|
/*#__PURE__*/ _jsx("div", {
|
|
524
540
|
style: {
|
|
525
541
|
flex: 1
|
|
@@ -557,7 +573,7 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
557
573
|
})
|
|
558
574
|
]
|
|
559
575
|
}),
|
|
560
|
-
/*#__PURE__*/
|
|
576
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
561
577
|
style: {
|
|
562
578
|
background: 'var(--theme-input-bg)',
|
|
563
579
|
border: '1px solid var(--theme-elevation-150)',
|
|
@@ -565,113 +581,103 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
565
581
|
maxHeight: '400px',
|
|
566
582
|
overflowY: 'auto'
|
|
567
583
|
},
|
|
568
|
-
children:
|
|
569
|
-
|
|
570
|
-
const isExpanded = expandedGroups.has(group.collection);
|
|
571
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
584
|
+
children: [
|
|
585
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
572
586
|
style: {
|
|
573
|
-
|
|
587
|
+
display: 'grid',
|
|
588
|
+
gridTemplateColumns: '1fr 60px 60px',
|
|
589
|
+
gap: 'calc(var(--base) * 0.5)',
|
|
590
|
+
padding: 'calc(var(--base) * 0.5) calc(var(--base) * 0.75)',
|
|
591
|
+
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
592
|
+
fontSize: '11px',
|
|
593
|
+
fontWeight: 600,
|
|
594
|
+
color: 'var(--theme-elevation-600)',
|
|
595
|
+
textTransform: 'uppercase',
|
|
596
|
+
letterSpacing: '0.5px'
|
|
574
597
|
},
|
|
575
598
|
children: [
|
|
576
|
-
/*#__PURE__*/
|
|
599
|
+
/*#__PURE__*/ _jsx("span", {
|
|
600
|
+
children: "Collection"
|
|
601
|
+
}),
|
|
602
|
+
/*#__PURE__*/ _jsx("span", {
|
|
577
603
|
style: {
|
|
578
|
-
|
|
579
|
-
alignItems: 'center',
|
|
580
|
-
gap: 'calc(var(--base) * 0.5)',
|
|
581
|
-
padding: 'calc(var(--base) * 0.5) calc(var(--base) * 0.75)',
|
|
582
|
-
cursor: 'pointer',
|
|
583
|
-
background: groupState !== 'none' ? 'var(--theme-elevation-50)' : 'transparent'
|
|
604
|
+
textAlign: 'center'
|
|
584
605
|
},
|
|
585
|
-
|
|
586
|
-
children: [
|
|
587
|
-
/*#__PURE__*/ _jsx("span", {
|
|
588
|
-
style: {
|
|
589
|
-
color: 'var(--theme-elevation-500)',
|
|
590
|
-
fontSize: '10px',
|
|
591
|
-
width: '12px',
|
|
592
|
-
transition: 'transform 0.15s',
|
|
593
|
-
transform: isExpanded ? 'rotate(90deg)' : 'rotate(0deg)'
|
|
594
|
-
},
|
|
595
|
-
children: "▶"
|
|
596
|
-
}),
|
|
597
|
-
/*#__PURE__*/ _jsx(IndeterminateCheckbox, {
|
|
598
|
-
checked: groupState === 'all',
|
|
599
|
-
indeterminate: groupState === 'some',
|
|
600
|
-
onChange: (e)=>{
|
|
601
|
-
e.stopPropagation();
|
|
602
|
-
toggleGroup(group);
|
|
603
|
-
}
|
|
604
|
-
}),
|
|
605
|
-
/*#__PURE__*/ _jsx("span", {
|
|
606
|
-
style: {
|
|
607
|
-
color: 'var(--theme-text)',
|
|
608
|
-
fontSize: 'var(--font-size-small)',
|
|
609
|
-
fontWeight: 500,
|
|
610
|
-
flex: 1
|
|
611
|
-
},
|
|
612
|
-
children: group.label
|
|
613
|
-
}),
|
|
614
|
-
groupState !== 'none' && /*#__PURE__*/ _jsxs("span", {
|
|
615
|
-
style: {
|
|
616
|
-
padding: '2px 6px',
|
|
617
|
-
background: 'var(--theme-elevation-200)',
|
|
618
|
-
borderRadius: '10px',
|
|
619
|
-
fontSize: '10px',
|
|
620
|
-
color: 'var(--theme-elevation-700)'
|
|
621
|
-
},
|
|
622
|
-
children: [
|
|
623
|
-
group.scopes.filter((s)=>selectedScopes.includes(s.scope.id)).length,
|
|
624
|
-
"/",
|
|
625
|
-
group.scopes.length
|
|
626
|
-
]
|
|
627
|
-
})
|
|
628
|
-
]
|
|
606
|
+
children: "Read"
|
|
629
607
|
}),
|
|
630
|
-
|
|
608
|
+
/*#__PURE__*/ _jsx("span", {
|
|
631
609
|
style: {
|
|
632
|
-
|
|
633
|
-
paddingLeft: 'calc(var(--base) * 2.5)',
|
|
634
|
-
display: 'flex',
|
|
635
|
-
flexDirection: 'column',
|
|
636
|
-
gap: 'calc(var(--base) * 0.25)'
|
|
610
|
+
textAlign: 'center'
|
|
637
611
|
},
|
|
638
|
-
children:
|
|
639
|
-
style: {
|
|
640
|
-
display: 'flex',
|
|
641
|
-
alignItems: 'center',
|
|
642
|
-
gap: 'calc(var(--base) * 0.5)',
|
|
643
|
-
cursor: 'pointer',
|
|
644
|
-
padding: 'calc(var(--base) * 0.25)',
|
|
645
|
-
borderRadius: 'var(--style-radius-s)',
|
|
646
|
-
background: selectedScopes.includes(scope.id) ? 'var(--theme-elevation-100)' : 'transparent'
|
|
647
|
-
},
|
|
648
|
-
children: [
|
|
649
|
-
/*#__PURE__*/ _jsx("input", {
|
|
650
|
-
type: "checkbox",
|
|
651
|
-
checked: selectedScopes.includes(scope.id),
|
|
652
|
-
onChange: ()=>toggleScope(scope.id)
|
|
653
|
-
}),
|
|
654
|
-
/*#__PURE__*/ _jsx("span", {
|
|
655
|
-
style: {
|
|
656
|
-
color: 'var(--theme-text)',
|
|
657
|
-
fontSize: 'var(--font-size-small)'
|
|
658
|
-
},
|
|
659
|
-
children: getTypeLabel(type)
|
|
660
|
-
})
|
|
661
|
-
]
|
|
662
|
-
}, scope.id))
|
|
612
|
+
children: "Write"
|
|
663
613
|
})
|
|
664
614
|
]
|
|
665
|
-
},
|
|
666
|
-
|
|
615
|
+
}),
|
|
616
|
+
permissions.map((perm)=>{
|
|
617
|
+
const actions = selectedPermissions[perm.slug] ?? [];
|
|
618
|
+
const hasRead = actions.includes('read');
|
|
619
|
+
const hasWrite = actions.includes('write');
|
|
620
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
621
|
+
style: {
|
|
622
|
+
display: 'grid',
|
|
623
|
+
gridTemplateColumns: '1fr 60px 60px',
|
|
624
|
+
gap: 'calc(var(--base) * 0.5)',
|
|
625
|
+
padding: 'calc(var(--base) * 0.5) calc(var(--base) * 0.75)',
|
|
626
|
+
borderBottom: '1px solid var(--theme-elevation-100)',
|
|
627
|
+
alignItems: 'center',
|
|
628
|
+
background: hasRead || hasWrite ? 'var(--theme-elevation-50)' : 'transparent'
|
|
629
|
+
},
|
|
630
|
+
children: [
|
|
631
|
+
/*#__PURE__*/ _jsx("span", {
|
|
632
|
+
style: {
|
|
633
|
+
color: 'var(--theme-text)',
|
|
634
|
+
fontSize: 'var(--font-size-small)',
|
|
635
|
+
fontWeight: 500
|
|
636
|
+
},
|
|
637
|
+
children: perm.label
|
|
638
|
+
}),
|
|
639
|
+
/*#__PURE__*/ _jsx("label", {
|
|
640
|
+
style: {
|
|
641
|
+
display: 'flex',
|
|
642
|
+
justifyContent: 'center',
|
|
643
|
+
cursor: 'pointer'
|
|
644
|
+
},
|
|
645
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
646
|
+
type: "checkbox",
|
|
647
|
+
checked: hasRead,
|
|
648
|
+
onChange: ()=>toggleAction(perm.slug, 'read'),
|
|
649
|
+
style: {
|
|
650
|
+
cursor: 'pointer'
|
|
651
|
+
}
|
|
652
|
+
})
|
|
653
|
+
}),
|
|
654
|
+
/*#__PURE__*/ _jsx("label", {
|
|
655
|
+
style: {
|
|
656
|
+
display: 'flex',
|
|
657
|
+
justifyContent: 'center',
|
|
658
|
+
cursor: 'pointer'
|
|
659
|
+
},
|
|
660
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
661
|
+
type: "checkbox",
|
|
662
|
+
checked: hasWrite,
|
|
663
|
+
onChange: ()=>toggleAction(perm.slug, 'write'),
|
|
664
|
+
style: {
|
|
665
|
+
cursor: 'pointer'
|
|
666
|
+
}
|
|
667
|
+
})
|
|
668
|
+
})
|
|
669
|
+
]
|
|
670
|
+
}, perm.slug);
|
|
671
|
+
})
|
|
672
|
+
]
|
|
667
673
|
}),
|
|
668
674
|
/*#__PURE__*/ _jsx("div", {
|
|
669
675
|
style: {
|
|
670
676
|
marginTop: 'calc(var(--base) * 0.5)',
|
|
671
677
|
fontSize: '11px',
|
|
672
|
-
color:
|
|
678
|
+
color: selectedCount === 0 ? 'var(--theme-warning-500)' : 'var(--theme-elevation-600)'
|
|
673
679
|
},
|
|
674
|
-
children:
|
|
680
|
+
children: selectedCount === 0 ? 'No permissions selected. Key will have no access.' : `${selectedCount} permission${selectedCount === 1 ? '' : 's'} selected`
|
|
675
681
|
})
|
|
676
682
|
]
|
|
677
683
|
}),
|
|
@@ -793,14 +799,37 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
793
799
|
})
|
|
794
800
|
]
|
|
795
801
|
}),
|
|
796
|
-
key.metadata?.
|
|
802
|
+
Boolean(key.metadata?.organizationId) && /*#__PURE__*/ _jsx("div", {
|
|
803
|
+
style: {
|
|
804
|
+
marginTop: 'calc(var(--base) * 0.5)'
|
|
805
|
+
},
|
|
806
|
+
children: /*#__PURE__*/ _jsxs("span", {
|
|
807
|
+
style: {
|
|
808
|
+
padding: '2px 6px',
|
|
809
|
+
background: 'var(--theme-elevation-150)',
|
|
810
|
+
borderRadius: 'var(--style-radius-s)',
|
|
811
|
+
fontSize: '11px',
|
|
812
|
+
color: 'var(--theme-elevation-700)',
|
|
813
|
+
fontWeight: 500
|
|
814
|
+
},
|
|
815
|
+
children: [
|
|
816
|
+
"Org: ",
|
|
817
|
+
(()=>{
|
|
818
|
+
const orgId = String(key.metadata?.organizationId ?? '');
|
|
819
|
+
const org = organizations.find((o)=>String(o.id) === orgId);
|
|
820
|
+
return org?.name ?? orgId;
|
|
821
|
+
})()
|
|
822
|
+
]
|
|
823
|
+
})
|
|
824
|
+
}),
|
|
825
|
+
key.permissions && Object.keys(key.permissions).length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
797
826
|
style: {
|
|
798
827
|
display: 'flex',
|
|
799
828
|
flexWrap: 'wrap',
|
|
800
829
|
gap: 'calc(var(--base) * 0.25)',
|
|
801
830
|
marginTop: 'calc(var(--base) * 0.5)'
|
|
802
831
|
},
|
|
803
|
-
children: key.
|
|
832
|
+
children: formatPermissions(key.permissions).map((label)=>/*#__PURE__*/ _jsx("span", {
|
|
804
833
|
style: {
|
|
805
834
|
padding: '2px 6px',
|
|
806
835
|
background: 'var(--theme-elevation-100)',
|
|
@@ -808,8 +837,8 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
808
837
|
fontSize: '11px',
|
|
809
838
|
color: 'var(--theme-elevation-700)'
|
|
810
839
|
},
|
|
811
|
-
children:
|
|
812
|
-
},
|
|
840
|
+
children: label
|
|
841
|
+
}, label))
|
|
813
842
|
})
|
|
814
843
|
]
|
|
815
844
|
}),
|
|
@@ -854,26 +883,4 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
854
883
|
children: label
|
|
855
884
|
});
|
|
856
885
|
}
|
|
857
|
-
/**
|
|
858
|
-
* Checkbox that supports indeterminate state
|
|
859
|
-
*/ function IndeterminateCheckbox({ checked, indeterminate, onChange }) {
|
|
860
|
-
const ref = useRef(null);
|
|
861
|
-
useEffect(()=>{
|
|
862
|
-
if (ref.current) {
|
|
863
|
-
ref.current.indeterminate = indeterminate;
|
|
864
|
-
}
|
|
865
|
-
}, [
|
|
866
|
-
indeterminate
|
|
867
|
-
]);
|
|
868
|
-
return /*#__PURE__*/ _jsx("input", {
|
|
869
|
-
ref: ref,
|
|
870
|
-
type: "checkbox",
|
|
871
|
-
checked: checked,
|
|
872
|
-
onChange: ()=>{},
|
|
873
|
-
onClick: onChange,
|
|
874
|
-
style: {
|
|
875
|
-
cursor: 'pointer'
|
|
876
|
-
}
|
|
877
|
-
});
|
|
878
|
-
}
|
|
879
886
|
export default ApiKeysManagementClient;
|