@delmaredigital/payload-better-auth 0.5.6 → 0.6.0
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 +5 -7
- package/dist/components/management/ApiKeysManagementClient.js +216 -286
- package/dist/components/management/views/ApiKeysView.js +6 -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 +27 -64
- 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
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PermissionDefinition } from '../../types/apiKey.js';
|
|
2
2
|
export type ApiKeysManagementClientProps = {
|
|
3
3
|
/** Optional pre-configured auth client with apiKey plugin */
|
|
4
4
|
authClient?: any;
|
|
5
5
|
/** Page title. Default: 'API Keys' */
|
|
6
6
|
title?: string;
|
|
7
|
-
/** Available
|
|
8
|
-
|
|
9
|
-
/** Default scopes to pre-select when creating a key */
|
|
10
|
-
defaultScopes?: string[];
|
|
7
|
+
/** Available permission definitions (collections + actions). Auto-generated if not provided. */
|
|
8
|
+
permissions?: PermissionDefinition[];
|
|
11
9
|
};
|
|
12
10
|
/**
|
|
13
11
|
* Client component for API keys management.
|
|
14
|
-
* Lists, creates, and deletes API keys with
|
|
12
|
+
* Lists, creates, and deletes API keys with permission selection (read/write per collection).
|
|
15
13
|
*/
|
|
16
|
-
export declare function ApiKeysManagementClient({ authClient: providedClient, title,
|
|
14
|
+
export declare function ApiKeysManagementClient({ authClient: providedClient, title, permissions, }?: ApiKeysManagementClientProps): import("react").JSX.Element;
|
|
17
15
|
export default ApiKeysManagementClient;
|
|
@@ -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 = [] } = {}) {
|
|
58
9
|
const [apiKeys, setApiKeys] = useState([]);
|
|
59
10
|
const [loading, setLoading] = useState(true);
|
|
60
11
|
const [error, setError] = useState(null);
|
|
@@ -63,32 +14,10 @@ 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 hasScopes = availableScopes.length > 0;
|
|
70
|
-
// Group scopes by collection
|
|
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 hasPermissions = permissions.length > 0;
|
|
92
21
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
93
22
|
const clientRef = useRef(null);
|
|
94
23
|
const getClient = async ()=>{
|
|
@@ -102,100 +31,140 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
102
31
|
});
|
|
103
32
|
return clientRef.current;
|
|
104
33
|
};
|
|
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([
|
|
34
|
+
// Toggle a specific action for a collection
|
|
35
|
+
function toggleAction(slug, action) {
|
|
36
|
+
setSelectedPermissions((prev)=>{
|
|
37
|
+
const current = prev[slug] ?? [];
|
|
38
|
+
if (current.includes(action)) {
|
|
39
|
+
// Remove action — if removing 'read' also remove 'write'
|
|
40
|
+
if (action === 'read') {
|
|
41
|
+
const filtered = current.filter((a)=>a !== 'read' && a !== 'write');
|
|
42
|
+
if (filtered.length === 0) {
|
|
43
|
+
const { [slug]: _, ...rest } = prev;
|
|
44
|
+
return rest;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
123
47
|
...prev,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
48
|
+
[slug]: filtered
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const filtered = current.filter((a)=>a !== action);
|
|
52
|
+
if (filtered.length === 0) {
|
|
53
|
+
const { [slug]: _, ...rest } = prev;
|
|
54
|
+
return rest;
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
...prev,
|
|
58
|
+
[slug]: filtered
|
|
59
|
+
};
|
|
135
60
|
} else {
|
|
136
|
-
|
|
61
|
+
// Add action — if adding 'write' also add 'read'
|
|
62
|
+
if (action === 'write') {
|
|
63
|
+
return {
|
|
64
|
+
...prev,
|
|
65
|
+
[slug]: [
|
|
66
|
+
...new Set([
|
|
67
|
+
...current,
|
|
68
|
+
'read',
|
|
69
|
+
'write'
|
|
70
|
+
])
|
|
71
|
+
]
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
...prev,
|
|
76
|
+
[slug]: [
|
|
77
|
+
...current,
|
|
78
|
+
action
|
|
79
|
+
]
|
|
80
|
+
};
|
|
137
81
|
}
|
|
138
|
-
return next;
|
|
139
82
|
});
|
|
140
83
|
}
|
|
141
|
-
// Bulk toggle all
|
|
142
|
-
function toggleAllOfType(
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
84
|
+
// Bulk toggle all of an action type
|
|
85
|
+
function toggleAllOfType(action) {
|
|
86
|
+
const allHave = permissions.every((p)=>(selectedPermissions[p.slug] ?? []).includes(action));
|
|
87
|
+
if (allHave) {
|
|
88
|
+
// Remove this action from all — if removing 'read' also remove 'write'
|
|
89
|
+
setSelectedPermissions((prev)=>{
|
|
90
|
+
const next = {};
|
|
91
|
+
for (const [slug, actions] of Object.entries(prev)){
|
|
92
|
+
const filtered = action === 'read' ? actions.filter((a)=>a !== 'read' && a !== 'write') : actions.filter((a)=>a !== action);
|
|
93
|
+
if (filtered.length > 0) next[slug] = filtered;
|
|
94
|
+
}
|
|
95
|
+
return next;
|
|
96
|
+
});
|
|
147
97
|
} else {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
98
|
+
// Add this action to all — if 'write' also add 'read'
|
|
99
|
+
setSelectedPermissions((prev)=>{
|
|
100
|
+
const next = {
|
|
101
|
+
...prev
|
|
102
|
+
};
|
|
103
|
+
for (const p of permissions){
|
|
104
|
+
const current = next[p.slug] ?? [];
|
|
105
|
+
if (action === 'write') {
|
|
106
|
+
next[p.slug] = [
|
|
107
|
+
...new Set([
|
|
108
|
+
...current,
|
|
109
|
+
'read',
|
|
110
|
+
'write'
|
|
111
|
+
])
|
|
112
|
+
];
|
|
113
|
+
} else {
|
|
114
|
+
next[p.slug] = [
|
|
115
|
+
...new Set([
|
|
116
|
+
...current,
|
|
117
|
+
action
|
|
118
|
+
])
|
|
119
|
+
];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return next;
|
|
123
|
+
});
|
|
154
124
|
}
|
|
155
125
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
return scopesByType[type].length > 0 && scopesByType[type].every((id)=>selectedScopes.includes(id));
|
|
126
|
+
function isAllOfTypeSelected(action) {
|
|
127
|
+
return permissions.length > 0 && permissions.every((p)=>(selectedPermissions[p.slug] ?? []).includes(action));
|
|
159
128
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const selectedCount = typeScopes.filter((id)=>selectedScopes.includes(id)).length;
|
|
164
|
-
return selectedCount > 0 && selectedCount < typeScopes.length;
|
|
129
|
+
function isSomeOfTypeSelected(action) {
|
|
130
|
+
const count = permissions.filter((p)=>(selectedPermissions[p.slug] ?? []).includes(action)).length;
|
|
131
|
+
return count > 0 && count < permissions.length;
|
|
165
132
|
}
|
|
166
|
-
// Clear all selections
|
|
167
133
|
function clearAll() {
|
|
168
|
-
|
|
134
|
+
setSelectedPermissions({});
|
|
169
135
|
}
|
|
170
|
-
// Select all scopes
|
|
171
136
|
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;
|
|
137
|
+
const next = {};
|
|
138
|
+
for (const p of permissions){
|
|
139
|
+
next[p.slug] = [
|
|
140
|
+
'read',
|
|
141
|
+
'write'
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
setSelectedPermissions(next);
|
|
186
145
|
}
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
case 'write':
|
|
193
|
-
return 'Write';
|
|
194
|
-
case 'delete':
|
|
195
|
-
return 'Delete';
|
|
196
|
-
default:
|
|
197
|
-
return 'Access';
|
|
146
|
+
// Count total selected actions
|
|
147
|
+
const selectedCount = useMemo(()=>{
|
|
148
|
+
let count = 0;
|
|
149
|
+
for (const actions of Object.values(selectedPermissions)){
|
|
150
|
+
count += actions.length;
|
|
198
151
|
}
|
|
152
|
+
return count;
|
|
153
|
+
}, [
|
|
154
|
+
selectedPermissions
|
|
155
|
+
]);
|
|
156
|
+
// Format permissions for display
|
|
157
|
+
function formatPermissions(perms) {
|
|
158
|
+
if (!perms) return [];
|
|
159
|
+
const labels = [];
|
|
160
|
+
for (const [slug, actions] of Object.entries(perms)){
|
|
161
|
+
const def = permissions.find((p)=>p.slug === slug);
|
|
162
|
+
const label = def?.label ?? slug;
|
|
163
|
+
for (const action of actions){
|
|
164
|
+
labels.push(`${label}: ${action}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return labels;
|
|
199
168
|
}
|
|
200
169
|
useEffect(()=>{
|
|
201
170
|
fetchApiKeys();
|
|
@@ -226,16 +195,15 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
226
195
|
setNewlyCreatedKey(null);
|
|
227
196
|
try {
|
|
228
197
|
const client = await getClient();
|
|
229
|
-
// Send scopes to server - server will convert to permissions
|
|
230
198
|
const createOptions = {
|
|
231
199
|
name: newKeyName
|
|
232
200
|
};
|
|
233
201
|
if (newKeyExpiry) {
|
|
234
|
-
createOptions.expiresIn = parseInt(newKeyExpiry) * 24 * 60 * 60;
|
|
202
|
+
createOptions.expiresIn = parseInt(newKeyExpiry) * 24 * 60 * 60;
|
|
235
203
|
}
|
|
236
|
-
//
|
|
237
|
-
if (
|
|
238
|
-
createOptions.
|
|
204
|
+
// Send permissions directly in BA's native format
|
|
205
|
+
if (hasPermissions && selectedCount > 0) {
|
|
206
|
+
createOptions.permissions = selectedPermissions;
|
|
239
207
|
}
|
|
240
208
|
const result = await client.apiKey.create(createOptions);
|
|
241
209
|
if (result.error) {
|
|
@@ -245,7 +213,7 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
245
213
|
setShowCreateForm(false);
|
|
246
214
|
setNewKeyName('');
|
|
247
215
|
setNewKeyExpiry('');
|
|
248
|
-
|
|
216
|
+
setSelectedPermissions({});
|
|
249
217
|
fetchApiKeys();
|
|
250
218
|
}
|
|
251
219
|
} catch {
|
|
@@ -480,7 +448,7 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
480
448
|
})
|
|
481
449
|
]
|
|
482
450
|
}),
|
|
483
|
-
|
|
451
|
+
hasPermissions && /*#__PURE__*/ _jsxs("div", {
|
|
484
452
|
style: {
|
|
485
453
|
marginBottom: 'var(--base)'
|
|
486
454
|
},
|
|
@@ -514,12 +482,6 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
514
482
|
indeterminate: isSomeOfTypeSelected('write'),
|
|
515
483
|
onClick: ()=>toggleAllOfType('write')
|
|
516
484
|
}),
|
|
517
|
-
/*#__PURE__*/ _jsx(BulkButton, {
|
|
518
|
-
label: "All Delete",
|
|
519
|
-
active: isAllOfTypeSelected('delete'),
|
|
520
|
-
indeterminate: isSomeOfTypeSelected('delete'),
|
|
521
|
-
onClick: ()=>toggleAllOfType('delete')
|
|
522
|
-
}),
|
|
523
485
|
/*#__PURE__*/ _jsx("div", {
|
|
524
486
|
style: {
|
|
525
487
|
flex: 1
|
|
@@ -557,7 +519,7 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
557
519
|
})
|
|
558
520
|
]
|
|
559
521
|
}),
|
|
560
|
-
/*#__PURE__*/
|
|
522
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
561
523
|
style: {
|
|
562
524
|
background: 'var(--theme-input-bg)',
|
|
563
525
|
border: '1px solid var(--theme-elevation-150)',
|
|
@@ -565,113 +527,103 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
565
527
|
maxHeight: '400px',
|
|
566
528
|
overflowY: 'auto'
|
|
567
529
|
},
|
|
568
|
-
children:
|
|
569
|
-
|
|
570
|
-
const isExpanded = expandedGroups.has(group.collection);
|
|
571
|
-
return /*#__PURE__*/ _jsxs("div", {
|
|
530
|
+
children: [
|
|
531
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
572
532
|
style: {
|
|
573
|
-
|
|
533
|
+
display: 'grid',
|
|
534
|
+
gridTemplateColumns: '1fr 60px 60px',
|
|
535
|
+
gap: 'calc(var(--base) * 0.5)',
|
|
536
|
+
padding: 'calc(var(--base) * 0.5) calc(var(--base) * 0.75)',
|
|
537
|
+
borderBottom: '1px solid var(--theme-elevation-150)',
|
|
538
|
+
fontSize: '11px',
|
|
539
|
+
fontWeight: 600,
|
|
540
|
+
color: 'var(--theme-elevation-600)',
|
|
541
|
+
textTransform: 'uppercase',
|
|
542
|
+
letterSpacing: '0.5px'
|
|
574
543
|
},
|
|
575
544
|
children: [
|
|
576
|
-
/*#__PURE__*/
|
|
545
|
+
/*#__PURE__*/ _jsx("span", {
|
|
546
|
+
children: "Collection"
|
|
547
|
+
}),
|
|
548
|
+
/*#__PURE__*/ _jsx("span", {
|
|
577
549
|
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'
|
|
550
|
+
textAlign: 'center'
|
|
584
551
|
},
|
|
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
|
-
]
|
|
552
|
+
children: "Read"
|
|
629
553
|
}),
|
|
630
|
-
|
|
554
|
+
/*#__PURE__*/ _jsx("span", {
|
|
631
555
|
style: {
|
|
632
|
-
|
|
633
|
-
paddingLeft: 'calc(var(--base) * 2.5)',
|
|
634
|
-
display: 'flex',
|
|
635
|
-
flexDirection: 'column',
|
|
636
|
-
gap: 'calc(var(--base) * 0.25)'
|
|
556
|
+
textAlign: 'center'
|
|
637
557
|
},
|
|
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))
|
|
558
|
+
children: "Write"
|
|
663
559
|
})
|
|
664
560
|
]
|
|
665
|
-
},
|
|
666
|
-
|
|
561
|
+
}),
|
|
562
|
+
permissions.map((perm)=>{
|
|
563
|
+
const actions = selectedPermissions[perm.slug] ?? [];
|
|
564
|
+
const hasRead = actions.includes('read');
|
|
565
|
+
const hasWrite = actions.includes('write');
|
|
566
|
+
return /*#__PURE__*/ _jsxs("div", {
|
|
567
|
+
style: {
|
|
568
|
+
display: 'grid',
|
|
569
|
+
gridTemplateColumns: '1fr 60px 60px',
|
|
570
|
+
gap: 'calc(var(--base) * 0.5)',
|
|
571
|
+
padding: 'calc(var(--base) * 0.5) calc(var(--base) * 0.75)',
|
|
572
|
+
borderBottom: '1px solid var(--theme-elevation-100)',
|
|
573
|
+
alignItems: 'center',
|
|
574
|
+
background: hasRead || hasWrite ? 'var(--theme-elevation-50)' : 'transparent'
|
|
575
|
+
},
|
|
576
|
+
children: [
|
|
577
|
+
/*#__PURE__*/ _jsx("span", {
|
|
578
|
+
style: {
|
|
579
|
+
color: 'var(--theme-text)',
|
|
580
|
+
fontSize: 'var(--font-size-small)',
|
|
581
|
+
fontWeight: 500
|
|
582
|
+
},
|
|
583
|
+
children: perm.label
|
|
584
|
+
}),
|
|
585
|
+
/*#__PURE__*/ _jsx("label", {
|
|
586
|
+
style: {
|
|
587
|
+
display: 'flex',
|
|
588
|
+
justifyContent: 'center',
|
|
589
|
+
cursor: 'pointer'
|
|
590
|
+
},
|
|
591
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
592
|
+
type: "checkbox",
|
|
593
|
+
checked: hasRead,
|
|
594
|
+
onChange: ()=>toggleAction(perm.slug, 'read'),
|
|
595
|
+
style: {
|
|
596
|
+
cursor: 'pointer'
|
|
597
|
+
}
|
|
598
|
+
})
|
|
599
|
+
}),
|
|
600
|
+
/*#__PURE__*/ _jsx("label", {
|
|
601
|
+
style: {
|
|
602
|
+
display: 'flex',
|
|
603
|
+
justifyContent: 'center',
|
|
604
|
+
cursor: 'pointer'
|
|
605
|
+
},
|
|
606
|
+
children: /*#__PURE__*/ _jsx("input", {
|
|
607
|
+
type: "checkbox",
|
|
608
|
+
checked: hasWrite,
|
|
609
|
+
onChange: ()=>toggleAction(perm.slug, 'write'),
|
|
610
|
+
style: {
|
|
611
|
+
cursor: 'pointer'
|
|
612
|
+
}
|
|
613
|
+
})
|
|
614
|
+
})
|
|
615
|
+
]
|
|
616
|
+
}, perm.slug);
|
|
617
|
+
})
|
|
618
|
+
]
|
|
667
619
|
}),
|
|
668
620
|
/*#__PURE__*/ _jsx("div", {
|
|
669
621
|
style: {
|
|
670
622
|
marginTop: 'calc(var(--base) * 0.5)',
|
|
671
623
|
fontSize: '11px',
|
|
672
|
-
color:
|
|
624
|
+
color: selectedCount === 0 ? 'var(--theme-warning-500)' : 'var(--theme-elevation-600)'
|
|
673
625
|
},
|
|
674
|
-
children:
|
|
626
|
+
children: selectedCount === 0 ? 'No permissions selected. Key will have no access.' : `${selectedCount} permission${selectedCount === 1 ? '' : 's'} selected`
|
|
675
627
|
})
|
|
676
628
|
]
|
|
677
629
|
}),
|
|
@@ -793,14 +745,14 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
793
745
|
})
|
|
794
746
|
]
|
|
795
747
|
}),
|
|
796
|
-
key.
|
|
748
|
+
key.permissions && Object.keys(key.permissions).length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
797
749
|
style: {
|
|
798
750
|
display: 'flex',
|
|
799
751
|
flexWrap: 'wrap',
|
|
800
752
|
gap: 'calc(var(--base) * 0.25)',
|
|
801
753
|
marginTop: 'calc(var(--base) * 0.5)'
|
|
802
754
|
},
|
|
803
|
-
children: key.
|
|
755
|
+
children: formatPermissions(key.permissions).map((label)=>/*#__PURE__*/ _jsx("span", {
|
|
804
756
|
style: {
|
|
805
757
|
padding: '2px 6px',
|
|
806
758
|
background: 'var(--theme-elevation-100)',
|
|
@@ -808,8 +760,8 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
808
760
|
fontSize: '11px',
|
|
809
761
|
color: 'var(--theme-elevation-700)'
|
|
810
762
|
},
|
|
811
|
-
children:
|
|
812
|
-
},
|
|
763
|
+
children: label
|
|
764
|
+
}, label))
|
|
813
765
|
})
|
|
814
766
|
]
|
|
815
767
|
}),
|
|
@@ -854,26 +806,4 @@ import { createAuthClient } from 'better-auth/react';
|
|
|
854
806
|
children: label
|
|
855
807
|
});
|
|
856
808
|
}
|
|
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
809
|
export default ApiKeysManagementClient;
|