@kyro-cms/admin 0.10.5 → 0.10.7
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/index.cjs +38 -15119
- package/dist/index.css +1 -1698
- package/dist/index.js +38 -15033
- package/package.json +1 -1
- package/src/components/ApiKeysManager.tsx +33 -126
- package/src/components/AutoForm.tsx +130 -1188
- package/src/components/PluginsManager.tsx +13 -141
- package/src/components/SettingsPage.tsx +0 -1
- package/src/components/UserMenu.tsx +17 -1
- package/src/components/autoform/AutoFormApiView.tsx +96 -0
- package/src/components/autoform/AutoFormEditView.tsx +91 -0
- package/src/components/autoform/AutoFormHeader.tsx +488 -0
- package/src/components/autoform/AutoFormVersionView.tsx +283 -0
- package/src/hooks/useAutoFormState.ts +3 -1
- package/src/lib/autoform-store.ts +8 -0
- package/dist/index.cjs.map +0 -1
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
package/package.json
CHANGED
|
@@ -3,9 +3,9 @@ import { apiPost } from "../lib/api";
|
|
|
3
3
|
import { useResourceManager } from "../lib/useResourceManager";
|
|
4
4
|
import { useUIStore, toast } from "../lib/stores";
|
|
5
5
|
import {
|
|
6
|
-
Key, Plus, Trash2,
|
|
6
|
+
Key, Plus, Trash2, Clock, CheckCircle2,
|
|
7
7
|
Shield, Zap, AlertTriangle, Info, Terminal,
|
|
8
|
-
Code,
|
|
8
|
+
Code,
|
|
9
9
|
} from "./ui/icons";
|
|
10
10
|
import { PageHeader } from "./ui/PageHeader";
|
|
11
11
|
import { Badge } from "./ui/Badge";
|
|
@@ -62,9 +62,7 @@ export function ApiKeysManager() {
|
|
|
62
62
|
const [newKeyName, setNewKeyName] = useState("");
|
|
63
63
|
const [newKeyPermissions, setNewKeyPermissions] = useState<string[]>(["*"]);
|
|
64
64
|
const [newKeyExpires, setNewKeyExpires] = useState("");
|
|
65
|
-
const [copiedId, setCopiedId] = useState<string | null>(null);
|
|
66
65
|
const [createError, setCreateError] = useState("");
|
|
67
|
-
const [rotatingId, setRotatingId] = useState<string | null>(null);
|
|
68
66
|
const { confirm: kyroConfirm } = useUIStore();
|
|
69
67
|
|
|
70
68
|
const handleCreateKey = async () => {
|
|
@@ -81,32 +79,6 @@ export function ApiKeysManager() {
|
|
|
81
79
|
} catch { setCreateError("Failed to create API key"); }
|
|
82
80
|
};
|
|
83
81
|
|
|
84
|
-
const handleRotateKey = (key: ApiKeyItem) => {
|
|
85
|
-
kyroConfirm({
|
|
86
|
-
title: "Rotate API Key",
|
|
87
|
-
message: `Are you sure you want to regenerate the key "${key.name}"? The current key will stop working immediately and this action cannot be undone.`,
|
|
88
|
-
variant: "danger",
|
|
89
|
-
onConfirm: async () => {
|
|
90
|
-
setRotatingId(key.id);
|
|
91
|
-
try {
|
|
92
|
-
const rotated = await apiPost<ApiKeyItem>(`/api/keys/${key.id}/rotate`);
|
|
93
|
-
setNewKey(rotated);
|
|
94
|
-
toast.success("API key rotated");
|
|
95
|
-
} catch {
|
|
96
|
-
toast.error("Failed to rotate key. Please try again.");
|
|
97
|
-
} finally {
|
|
98
|
-
setRotatingId(null);
|
|
99
|
-
}
|
|
100
|
-
},
|
|
101
|
-
});
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const copyToClipboard = (key: string, id: string) => {
|
|
105
|
-
navigator.clipboard.writeText(key);
|
|
106
|
-
setCopiedId(id);
|
|
107
|
-
setTimeout(() => setCopiedId(null), 2000);
|
|
108
|
-
};
|
|
109
|
-
|
|
110
82
|
return (
|
|
111
83
|
<div className="w-full space-y-8 animate-in fade-in slide-in-from-bottom-4 duration-700 pb-32">
|
|
112
84
|
|
|
@@ -169,13 +141,6 @@ export function ApiKeysManager() {
|
|
|
169
141
|
<span> -H <span className="text-green-500">"Authorization: ApiKey kyro_xxx"</span></span>
|
|
170
142
|
</div>
|
|
171
143
|
</pre>
|
|
172
|
-
<button
|
|
173
|
-
type="button"
|
|
174
|
-
onClick={() => navigator.clipboard.writeText('curl -X GET https://api.yoursite.com/v1/posts -H "Authorization: ApiKey YOUR_KEY"')}
|
|
175
|
-
className="absolute top-3 right-3 p-2 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-lg opacity-0 group-hover/code:opacity-100 transition-opacity hover:text-[var(--kyro-primary)] shadow-sm"
|
|
176
|
-
>
|
|
177
|
-
<Copy className="w-3.5 h-3.5" />
|
|
178
|
-
</button>
|
|
179
144
|
</div>
|
|
180
145
|
</div>
|
|
181
146
|
</div>
|
|
@@ -220,13 +185,6 @@ export function ApiKeysManager() {
|
|
|
220
185
|
<span>{"}"});</span>
|
|
221
186
|
</div>
|
|
222
187
|
</pre>
|
|
223
|
-
<button
|
|
224
|
-
type="button"
|
|
225
|
-
onClick={() => navigator.clipboard.writeText('await fetch(url, { headers: { "Authorization": "ApiKey YOUR_KEY" } })')}
|
|
226
|
-
className="absolute top-3 right-3 p-2 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-lg opacity-0 group-hover/code:opacity-100 transition-opacity hover:text-[var(--kyro-primary)] shadow-sm"
|
|
227
|
-
>
|
|
228
|
-
<Copy className="w-3.5 h-3.5" />
|
|
229
|
-
</button>
|
|
230
188
|
</div>
|
|
231
189
|
</div>
|
|
232
190
|
</div>
|
|
@@ -281,18 +239,8 @@ export function ApiKeysManager() {
|
|
|
281
239
|
This is the <span className="text-green-500 font-bold uppercase tracking-tight">only time</span> the full key will be shown. Please store it securely.
|
|
282
240
|
</p>
|
|
283
241
|
|
|
284
|
-
<div className="flex flex
|
|
285
|
-
<
|
|
286
|
-
<span className="opacity-80 select-all">{newKey.key}</span>
|
|
287
|
-
</div>
|
|
288
|
-
<button
|
|
289
|
-
type="button"
|
|
290
|
-
onClick={() => copyToClipboard(newKey.key!, newKey.id)}
|
|
291
|
-
className="kyro-btn kyro-btn-primary flex items-center justify-center gap-2 px-8 py-4 rounded-2xl font-bold hover:scale-[1.02] active:scale-[0.98] transition-all shadow-lg shadow-[var(--kyro-primary)]/20"
|
|
292
|
-
>
|
|
293
|
-
{copiedId === newKey.id ? <CheckCircle2 className="w-5 h-5" /> : <Copy className="w-5 h-5" />}
|
|
294
|
-
<span>{copiedId === newKey.id ? "Copied" : "Copy Key"}</span>
|
|
295
|
-
</button>
|
|
242
|
+
<div className="flex-1 flex items-center gap-3 p-4 bg-[var(--kyro-bg)] border border-[var(--kyro-border)] rounded-2xl font-mono text-sm break-all group relative">
|
|
243
|
+
<span className="opacity-80 select-all">{newKey.key}</span>
|
|
296
244
|
</div>
|
|
297
245
|
|
|
298
246
|
<button
|
|
@@ -345,87 +293,46 @@ export function ApiKeysManager() {
|
|
|
345
293
|
{keys.map((key) => (
|
|
346
294
|
<div
|
|
347
295
|
key={key.id}
|
|
348
|
-
className="group relative overflow-hidden bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-
|
|
296
|
+
className="group relative overflow-hidden bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-2xl p-3 hover:border-[var(--kyro-primary)]/50 transition-all duration-300"
|
|
349
297
|
>
|
|
350
|
-
<div className="flex
|
|
351
|
-
<div className="flex-1">
|
|
352
|
-
<div className="
|
|
353
|
-
<
|
|
354
|
-
<Key className="w-5 h-5 text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-primary)] transition-colors" />
|
|
355
|
-
</div>
|
|
356
|
-
<div>
|
|
357
|
-
<h3 className="text-lg font-bold group-hover:text-[var(--kyro-primary)] transition-colors">{key.name}</h3>
|
|
358
|
-
<div className="flex items-center gap-2 mt-0.5">
|
|
359
|
-
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
|
360
|
-
<span className="text-[10px] font-bold uppercase tracking-wider text-green-500">Live</span>
|
|
361
|
-
</div>
|
|
362
|
-
</div>
|
|
298
|
+
<div className="flex items-center justify-between gap-3">
|
|
299
|
+
<div className="flex items-center gap-3 min-w-0 flex-1">
|
|
300
|
+
<div className="p-1.5 bg-[var(--kyro-surface-accent)] rounded-lg group-hover:bg-[var(--kyro-primary)]/10 transition-colors shrink-0">
|
|
301
|
+
<Key className="w-3.5 h-3.5 text-[var(--kyro-text-secondary)] group-hover:text-[var(--kyro-primary)] transition-colors" />
|
|
363
302
|
</div>
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
<span className="
|
|
368
|
-
<div className="font-mono text-sm opacity-60 tracking-tighter">
|
|
369
|
-
{key.keyPrefix}<span className="opacity-20">••••••••••••••••</span>
|
|
370
|
-
</div>
|
|
303
|
+
<div className="min-w-0 flex-1">
|
|
304
|
+
<div className="flex items-center gap-2">
|
|
305
|
+
<h3 className="text-sm font-bold truncate group-hover:text-[var(--kyro-primary)] transition-colors">{key.name}</h3>
|
|
306
|
+
<span className="w-1.5 h-1.5 rounded-full bg-green-500 shrink-0" />
|
|
371
307
|
</div>
|
|
372
|
-
<div className="
|
|
373
|
-
<span className="
|
|
374
|
-
<
|
|
308
|
+
<div className="flex items-center gap-3 text-[11px] text-[var(--kyro-text-secondary)]">
|
|
309
|
+
<span className="font-mono opacity-60">{key.keyPrefix}••••••••••••••••</span>
|
|
310
|
+
<span className="opacity-30">·</span>
|
|
311
|
+
<span className="flex items-center gap-1">
|
|
375
312
|
{key.permissions?.includes("*") ? (
|
|
376
|
-
|
|
313
|
+
"Full Access"
|
|
377
314
|
) : (
|
|
378
|
-
key.permissions?.slice(0, 2).
|
|
379
|
-
<Badge key={p} variant="outline" className="text-[8px] font-bold px-1.5 opacity-60">{p}</Badge>
|
|
380
|
-
))
|
|
381
|
-
)}
|
|
382
|
-
{key.permissions && key.permissions.length > 2 && (
|
|
383
|
-
<span className="text-[8px] font-bold opacity-30">+{key.permissions.length - 2} more</span>
|
|
315
|
+
key.permissions?.slice(0, 2).join(", ")
|
|
384
316
|
)}
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
<span className="
|
|
389
|
-
<div className="text-[10px] font-bold opacity-60 flex items-center gap-1.5">
|
|
317
|
+
{key.permissions && key.permissions.length > 2 && <span className="opacity-40">+{key.permissions.length - 2}</span>}
|
|
318
|
+
</span>
|
|
319
|
+
<span className="opacity-30">·</span>
|
|
320
|
+
<span className="flex items-center gap-1">
|
|
390
321
|
<Clock className="w-3 h-3" />
|
|
391
|
-
{key.lastUsed ? new Date(key.lastUsed).toLocaleDateString() : "Never
|
|
392
|
-
</
|
|
322
|
+
{key.lastUsed ? new Date(key.lastUsed).toLocaleDateString() : "Never"}
|
|
323
|
+
</span>
|
|
393
324
|
</div>
|
|
394
325
|
</div>
|
|
395
326
|
</div>
|
|
396
327
|
|
|
397
|
-
<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
<span className="text-xs font-bold">{copiedId === key.id ? "Copied" : "Copy"}</span>
|
|
406
|
-
</button>
|
|
407
|
-
|
|
408
|
-
<div className="w-px h-6 bg-[var(--kyro-border)] mx-1 hidden lg:block" />
|
|
409
|
-
|
|
410
|
-
<button
|
|
411
|
-
type="button"
|
|
412
|
-
onClick={() => handleRotateKey(key)}
|
|
413
|
-
disabled={rotatingId === key.id}
|
|
414
|
-
className="p-3 bg-[var(--kyro-surface)] border border-[var(--kyro-border)] rounded-xl hover:border-[var(--kyro-primary)] transition-all group/rotate"
|
|
415
|
-
title="Rotate Key"
|
|
416
|
-
>
|
|
417
|
-
<RefreshCw className={`w-4 h-4 text-[var(--kyro-text-secondary)] group-hover/rotate:text-[var(--kyro-primary)] ${rotatingId === key.id ? "animate-spin" : ""}`} />
|
|
418
|
-
</button>
|
|
419
|
-
|
|
420
|
-
<button
|
|
421
|
-
type="button"
|
|
422
|
-
onClick={() => remove(key.id, "API Key")}
|
|
423
|
-
className="p-3 bg-red-500/5 border border-red-500/10 rounded-xl hover:bg-red-500/10 hover:border-red-500/30 transition-all group/delete"
|
|
424
|
-
title="Revoke Access"
|
|
425
|
-
>
|
|
426
|
-
<Trash2 className="w-4 h-4 text-red-500/50 group-hover/delete:text-red-500" />
|
|
427
|
-
</button>
|
|
428
|
-
</div>
|
|
328
|
+
<button
|
|
329
|
+
type="button"
|
|
330
|
+
onClick={() => remove(key.id, "API Key")}
|
|
331
|
+
className="p-2 bg-red-500/5 border border-red-500/10 rounded-lg hover:bg-red-500/10 hover:border-red-500/30 transition-all opacity-0 group-hover:opacity-100 shrink-0"
|
|
332
|
+
title="Revoke Access"
|
|
333
|
+
>
|
|
334
|
+
<Trash2 className="w-3.5 h-3.5 text-red-500/50 group-hover/delete:text-red-500" />
|
|
335
|
+
</button>
|
|
429
336
|
</div>
|
|
430
337
|
</div>
|
|
431
338
|
))}
|