@rebasepro/studio 0.13.1-canary.gf57a27e → 0.14.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/{ApiExplorer-BIN9hE7r.js → ApiExplorer-CDOlUQzk.js} +9 -3
- package/dist/ApiExplorer-CDOlUQzk.js.map +1 -0
- package/dist/{ApiKeysView-dA-OvlcH.js → ApiKeysView-C_UoUPuR.js} +2 -1
- package/dist/{ApiKeysView-dA-OvlcH.js.map → ApiKeysView-C_UoUPuR.js.map} +1 -1
- package/dist/{LogsExplorer-BsHaHIMK.js → LogsExplorer-Ctk2f3MM.js} +2 -1
- package/dist/LogsExplorer-Ctk2f3MM.js.map +1 -0
- package/dist/{RLSEditor-DuzofJ8t.js → RLSEditor-CV8R1G3W.js} +52 -18
- package/dist/RLSEditor-CV8R1G3W.js.map +1 -0
- package/dist/components/RLSEditor/saveSecurityRules.d.ts +19 -0
- package/dist/index.es.js +4 -4
- package/package.json +9 -9
- package/src/components/ApiExplorer/TryItPanel.tsx +7 -2
- package/src/components/ApiKeys/ApiKeysView.tsx +1 -0
- package/src/components/LogsExplorer/LogsExplorer.tsx +1 -0
- package/src/components/RLSEditor/PolicyEditor.tsx +1 -0
- package/src/components/RLSEditor/RLSEditor.tsx +20 -19
- package/src/components/RLSEditor/saveSecurityRules.ts +48 -0
- package/src/utils/parseSpec.test.ts +10 -2
- package/dist/ApiExplorer-BIN9hE7r.js.map +0 -1
- package/dist/LogsExplorer-BsHaHIMK.js.map +0 -1
- package/dist/RLSEditor-DuzofJ8t.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as AuthSimulationSelector } from "./AuthSimulationSelector-Bougd521.js";
|
|
2
2
|
import { useApiBase, useApiConfig, useAuthController, useRebaseContext } from "@rebasepro/app";
|
|
3
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { Alert, ArrowRightFromLineIcon, BookOpenIcon, Button, Chip, CircularProgress, IconButton, LoaderIcon, PlayIcon, PlusIcon, SearchBar, SendIcon, SlidersHorizontalIcon, TextField, Typography, UploadIcon, XIcon, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
|
|
5
5
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
//#region src/components/ApiExplorer/parseSpec.ts
|
|
@@ -555,6 +555,7 @@ function TryItPanel({ endpoint, apiUrl, getAuthToken, user, basePath = "" }) {
|
|
|
555
555
|
/* @__PURE__ */ jsx(TextField, {
|
|
556
556
|
multiline: true,
|
|
557
557
|
minRows: 10,
|
|
558
|
+
"aria-label": "Request Body",
|
|
558
559
|
value: body,
|
|
559
560
|
onChange: (e) => {
|
|
560
561
|
setBody(e.target.value);
|
|
@@ -624,16 +625,18 @@ function TryItPanel({ endpoint, apiUrl, getAuthToken, user, basePath = "" }) {
|
|
|
624
625
|
});
|
|
625
626
|
}
|
|
626
627
|
function ParamSection({ title, params, values, onChange }) {
|
|
628
|
+
const paramIdBase = React.useId();
|
|
627
629
|
return /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx(Typography, {
|
|
628
630
|
variant: "caption",
|
|
629
631
|
className: "text-text-secondary dark:text-text-secondary-dark text-xs font-semibold uppercase tracking-wider mb-2 block",
|
|
630
632
|
children: title
|
|
631
633
|
}), /* @__PURE__ */ jsx("div", {
|
|
632
634
|
className: "space-y-2",
|
|
633
|
-
children: params.map((p) => /* @__PURE__ */ jsxs("div", {
|
|
635
|
+
children: params.map((p, index) => /* @__PURE__ */ jsxs("div", {
|
|
634
636
|
className: "flex items-center gap-3",
|
|
635
637
|
children: [/* @__PURE__ */ jsxs("label", {
|
|
636
638
|
className: "w-32 shrink-0",
|
|
639
|
+
htmlFor: `${paramIdBase}-${index}-param`,
|
|
637
640
|
children: [/* @__PURE__ */ jsx("code", {
|
|
638
641
|
className: "text-xs font-mono font-semibold",
|
|
639
642
|
children: p.name
|
|
@@ -642,6 +645,7 @@ function ParamSection({ title, params, values, onChange }) {
|
|
|
642
645
|
children: "*"
|
|
643
646
|
})]
|
|
644
647
|
}), /* @__PURE__ */ jsx(TextField, {
|
|
648
|
+
id: `${paramIdBase}-${index}-param`,
|
|
645
649
|
size: "small",
|
|
646
650
|
placeholder: p.description ?? p.name,
|
|
647
651
|
value: values[p.name] ?? "",
|
|
@@ -677,6 +681,7 @@ function CustomKeyValueSection({ title, values, onChange, onAdd, onRemove }) {
|
|
|
677
681
|
children: [
|
|
678
682
|
/* @__PURE__ */ jsx(TextField, {
|
|
679
683
|
size: "small",
|
|
684
|
+
"aria-label": `Header ${i + 1} name`,
|
|
680
685
|
placeholder: "Header name",
|
|
681
686
|
value: v.key,
|
|
682
687
|
onChange: (e) => onChange(i, e.target.value, v.value),
|
|
@@ -685,6 +690,7 @@ function CustomKeyValueSection({ title, values, onChange, onAdd, onRemove }) {
|
|
|
685
690
|
}),
|
|
686
691
|
/* @__PURE__ */ jsx(TextField, {
|
|
687
692
|
size: "small",
|
|
693
|
+
"aria-label": `Header ${i + 1} value`,
|
|
688
694
|
placeholder: "Value",
|
|
689
695
|
value: v.value,
|
|
690
696
|
onChange: (e) => onChange(i, v.key, e.target.value),
|
|
@@ -968,4 +974,4 @@ ApiExplorer.displayName = "ApiExplorer";
|
|
|
968
974
|
//#endregion
|
|
969
975
|
export { ApiExplorer };
|
|
970
976
|
|
|
971
|
-
//# sourceMappingURL=ApiExplorer-
|
|
977
|
+
//# sourceMappingURL=ApiExplorer-CDOlUQzk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ApiExplorer-CDOlUQzk.js","names":[],"sources":["../src/components/ApiExplorer/parseSpec.ts","../src/components/ApiExplorer/EndpointDetail.tsx","../src/components/ApiExplorer/TryItPanel.tsx","../src/components/ApiExplorer/ApiExplorer.tsx"],"sourcesContent":["import type { OpenApiSpec, ParsedEndpoint, EndpointGroup } from \"./types\";\n\n/**\n * Parse an OpenAPI 3.x spec into grouped, sorted endpoints for the sidebar.\n */\nexport function parseOpenApiSpec(spec: OpenApiSpec): {\n groups: EndpointGroup[];\n allEndpoints: ParsedEndpoint[];\n} {\n const allEndpoints: ParsedEndpoint[] = [];\n const tagMap = new Map<string, ParsedEndpoint[]>();\n\n // Build tag description lookup\n const tagDescriptions = new Map<string, string>();\n for (const t of spec.tags ?? []) {\n tagDescriptions.set(t.name, t.description ?? \"\");\n }\n\n for (const [path, methods] of Object.entries(spec.paths ?? {})) {\n for (const [method, op] of Object.entries(methods)) {\n if ([\"get\", \"post\", \"put\", \"patch\", \"delete\"].indexOf(method) === -1) continue;\n\n const tags = op.tags?.length ? op.tags : [\"Other\"];\n const shortPath = path.replace(/^\\/api\\/data/, \"\");\n\n const endpoint: ParsedEndpoint = {\n id: `${method}:${path}`,\n method,\n path,\n shortPath: shortPath || \"/\",\n summary: op.summary ?? \"\",\n description: op.description ?? \"\",\n tags,\n parameters: op.parameters ?? [],\n requestBody: op.requestBody,\n responses: op.responses ?? {},\n security: op.security,\n operationId: op.operationId\n };\n\n allEndpoints.push(endpoint);\n for (const tag of tags) {\n if (!tagMap.has(tag)) tagMap.set(tag, []);\n tagMap.get(tag)!.push(endpoint);\n }\n }\n }\n\n // Method sort order\n const ORDER: Record<string, number> = { get: 0,\npost: 1,\nput: 2,\npatch: 3,\ndelete: 4 };\n\n const groups: EndpointGroup[] = [];\n // Sort tags: use spec.tags order if available, else alphabetical\n const tagOrder = (spec.tags ?? []).map((t) => t.name);\n const sortedTags = [...tagMap.keys()].sort((a, b) => {\n const ai = tagOrder.indexOf(a);\n const bi = tagOrder.indexOf(b);\n if (ai !== -1 && bi !== -1) return ai - bi;\n if (ai !== -1) return -1;\n if (bi !== -1) return 1;\n return a.localeCompare(b);\n });\n\n for (const tag of sortedTags) {\n const endpoints = tagMap.get(tag)!;\n endpoints.sort((a, b) => {\n const pa = a.path.localeCompare(b.path);\n if (pa !== 0) return pa;\n return (ORDER[a.method] ?? 99) - (ORDER[b.method] ?? 99);\n });\n groups.push({\n tag,\n description: tagDescriptions.get(tag),\n endpoints\n });\n }\n\n return { groups,\nallEndpoints };\n}\n\n/**\n * Resolve a $ref string (e.g. \"#/components/schemas/Author\") to a schema name.\n */\nexport function resolveRefName(ref: string): string {\n const parts = ref.split(\"/\");\n return parts[parts.length - 1];\n}\n\n/**\n * Resolve a $ref to its actual schema from the spec.\n */\nexport function resolveRef(spec: OpenApiSpec, ref: string): unknown {\n const parts = ref.replace(\"#/\", \"\").split(\"/\");\n let current: unknown = spec;\n for (const part of parts) {\n current = (current as Record<string, unknown>)?.[part];\n }\n return current ?? {};\n}\n","import React from \"react\";\nimport {\n ArrowRightFromLineIcon,\n Chip,\n cls,\n defaultBorderMixin,\n iconSize,\n SlidersHorizontalIcon,\n Typography,\n UploadIcon\n} from \"@rebasepro/ui\";\nimport type { ParsedEndpoint, OpenApiSpec, OpenApiSchema } from \"./types\";\nimport { resolveRef, resolveRefName } from \"./parseSpec\";\n\n/**\n * Renders the documentation view for a single API endpoint:\n * parameters, request body schema, response schemas.\n */\nexport function EndpointDetail({ endpoint, spec }: { endpoint: ParsedEndpoint; spec: OpenApiSpec }) {\n return (\n <div className=\"p-6 space-y-8 max-w-4xl\">\n {/* Summary / Description */}\n {(endpoint.summary || endpoint.description) && (\n <div>\n {endpoint.summary && (\n <Typography variant=\"h6\" className=\"font-semibold mb-1\">\n {endpoint.summary}\n </Typography>\n )}\n {endpoint.description && (\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n {endpoint.description}\n </Typography>\n )}\n </div>\n )}\n\n {/* Parameters */}\n {endpoint.parameters.length > 0 && (\n <section>\n <SectionHeading icon={<SlidersHorizontalIcon size={iconSize.small} className=\"text-text-secondary dark:text-text-secondary-dark\" />} title=\"Parameters\"/>\n <div className={cls(\"rounded-lg border overflow-hidden\", defaultBorderMixin)}>\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"bg-surface-100 dark:bg-surface-900 text-left\">\n <th className=\"px-4 py-2 font-medium text-text-secondary dark:text-text-secondary-dark\">\n Name\n </th>\n <th className=\"px-4 py-2 font-medium text-text-secondary dark:text-text-secondary-dark\">\n In\n </th>\n <th className=\"px-4 py-2 font-medium text-text-secondary dark:text-text-secondary-dark\">\n Type\n </th>\n <th className=\"px-4 py-2 font-medium text-text-secondary dark:text-text-secondary-dark\">\n Description\n </th>\n </tr>\n </thead>\n <tbody>\n {endpoint.parameters.map((p, i) => (\n <tr\n key={p.name + i}\n className={cls(\"border-t\", defaultBorderMixin)}\n >\n <td className=\"px-4 py-2.5\">\n <code className=\"text-xs font-mono font-semibold\">{p.name}</code>\n {p.required && <span className=\"text-red-500 ml-1 text-xs\">*</span>}\n </td>\n <td className=\"px-4 py-2.5\">\n <Chip\n size=\"smallest\"\n colorScheme={p.in === \"path\" ? \"orangeDarker\" : \"cyanDarker\"}\n >\n {p.in}\n </Chip>\n </td>\n <td className=\"px-4 py-2.5 text-xs font-mono text-text-secondary dark:text-text-secondary-dark\">\n {schemaTypeLabel(p.schema)}\n </td>\n <td className=\"px-4 py-2.5 text-xs text-text-secondary dark:text-text-secondary-dark\">\n {p.description ?? \"—\"}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </section>\n )}\n\n {/* Request Body */}\n {endpoint.requestBody && (\n <section>\n <SectionHeading icon={<UploadIcon size={iconSize.small} className=\"text-text-secondary dark:text-text-secondary-dark\" />} title=\"Request Body\"/>\n {Object.entries(endpoint.requestBody.content ?? {}).map(([contentType, media]) => (\n <div key={contentType}>\n <Chip size=\"smallest\" colorScheme=\"blueDarker\" className=\"mb-3\">\n {contentType}\n </Chip>\n {media.schema && <SchemaBlock schema={media.schema} spec={spec} depth={0}/>}\n </div>\n ))}\n </section>\n )}\n\n {/* Responses */}\n <section>\n <SectionHeading icon={<ArrowRightFromLineIcon size={iconSize.small} className=\"text-text-secondary dark:text-text-secondary-dark\" />} title=\"Responses\"/>\n <div className=\"space-y-3\">\n {Object.entries(endpoint.responses).map(([code, res]) => (\n <div\n key={code}\n className={cls(\"rounded-lg border overflow-hidden\", defaultBorderMixin)}\n >\n <div\n className={cls(\n \"flex items-center gap-3 px-4 py-2.5\",\n \"bg-surface-50 dark:bg-surface-900/50\"\n )}\n >\n <StatusBadge code={code}/>\n <Typography\n variant=\"body2\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs\"\n >\n {res.description}\n </Typography>\n </div>\n {res.content &&\n Object.entries(res.content).map(\n ([ct, media]) =>\n media.schema && (\n <div\n key={ct}\n className={cls(\"px-4 py-3 border-t\", defaultBorderMixin)}\n >\n <SchemaBlock schema={media.schema} spec={spec} depth={0}/>\n </div>\n )\n )}\n </div>\n ))}\n </div>\n </section>\n </div>\n );\n}\n\n/* ── Schema Block ─────────────────────────────────────────────────── */\n\nfunction SchemaBlock({ schema, spec, depth }: { schema: OpenApiSchema; spec: OpenApiSpec; depth: number }) {\n // Resolve $ref\n if (schema.$ref) {\n const name = resolveRefName(schema.$ref);\n const resolved = resolveRef(spec, schema.$ref) as OpenApiSchema;\n return (\n <div>\n <Typography\n variant=\"caption\"\n className=\"text-primary dark:text-primary-dark font-mono text-xs mb-2 block\"\n >\n {name}\n </Typography>\n <SchemaBlock schema={resolved} spec={spec} depth={depth}/>\n </div>\n );\n }\n\n // Object with properties\n if (schema.properties) {\n const required = new Set(schema.required ?? []);\n return (\n <div\n className={cls(\n \"rounded-lg overflow-hidden\",\n depth > 0 && `border ml-4 mt-1 ${defaultBorderMixin}`\n )}\n >\n <table className=\"w-full text-xs\">\n <tbody>\n {Object.entries(schema.properties).map(([key, prop]) => (\n <tr\n key={key}\n className={cls(\"border-t first:border-t-0\", defaultBorderMixin)}\n >\n <td className=\"px-3 py-2 align-top w-36\">\n <code className=\"font-mono font-semibold text-text-primary dark:text-text-primary-dark\">\n {key}\n </code>\n {required.has(key) && <span className=\"text-red-500 ml-0.5\">*</span>}\n {prop.readOnly && (\n <span className=\"ml-1.5 text-[9px] text-text-secondary dark:text-text-secondary-dark italic\">\n read-only\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 align-top w-28\">\n <span className=\"font-mono text-text-secondary dark:text-text-secondary-dark\">\n {schemaTypeLabel(prop)}\n </span>\n </td>\n <td className=\"px-3 py-2 align-top text-text-secondary dark:text-text-secondary-dark\">\n {prop.description ?? \"\"}\n {prop.enum && (\n <div className=\"flex flex-wrap gap-1 mt-1\">\n {prop.enum.map((v) => (\n <span\n key={String(v)}\n className=\"px-1.5 py-0.5 rounded bg-surface-200 dark:bg-surface-800 text-[10px] font-mono\"\n >\n {String(v)}\n </span>\n ))}\n </div>\n )}\n {prop.properties && <SchemaBlock schema={prop} spec={spec} depth={depth + 1}/>}\n {prop.$ref && <SchemaBlock schema={prop} spec={spec} depth={depth + 1}/>}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n }\n\n // Array\n if (schema.type === \"array\" && schema.items) {\n return (\n <div>\n <span className=\"font-mono text-xs text-text-secondary dark:text-text-secondary-dark\">Array of:</span>\n <SchemaBlock schema={schema.items} spec={spec} depth={depth + 1}/>\n </div>\n );\n }\n\n // Primitive\n return (\n <span className=\"font-mono text-xs text-text-secondary dark:text-text-secondary-dark\">\n {schemaTypeLabel(schema)}\n </span>\n );\n}\n\n/* ── Helpers ──────────────────────────────────────────────────────── */\n\nfunction schemaTypeLabel(schema?: OpenApiSchema): string {\n if (!schema) return \"any\";\n if (schema.$ref) return resolveRefName(schema.$ref);\n if (schema.type === \"array\") return `${schemaTypeLabel(schema.items)}[]`;\n if (schema.format) return `${schema.type} (${schema.format})`;\n return schema.type ?? \"object\";\n}\n\nfunction SectionHeading({ icon, title }: { icon: React.ReactNode; title: string }) {\n return (\n <div className=\"flex items-center gap-2 mb-3\">\n {icon}\n <Typography variant=\"subtitle2\" className=\"font-semibold text-sm\">\n {title}\n </Typography>\n </div>\n );\n}\n\nfunction StatusBadge({ code }: { code: string }) {\n const n = parseInt(code, 10);\n const color =\n n < 300\n ? \"text-emerald-600 dark:text-emerald-400\"\n : n < 400\n ? \"text-blue-600 dark:text-blue-400\"\n : n < 500\n ? \"text-amber-600 dark:text-amber-400\"\n : \"text-red-600 dark:text-red-400\";\n\n return <span className={cls(\"text-xs font-bold font-mono\", color)}>{code}</span>;\n}\n","import React, { useState, useCallback, useMemo, useEffect } from \"react\";\nimport {\n Button,\n cls,\n defaultBorderMixin,\n IconButton,\n iconSize,\n LoaderIcon,\n PlusIcon,\n SendIcon,\n TextField,\n Typography,\n XIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseContext, UserSelectPopover, SelectableUser } from \"@rebasepro/app\";\nimport { AuthSimulationSelector } from \"../AuthSimulationSelector\";\nimport type { ParsedEndpoint } from \"./types\";\nimport type { User } from \"@rebasepro/types\";\n\ninterface TryItPanelProps {\n endpoint: ParsedEndpoint;\n apiUrl: string;\n getAuthToken: () => Promise<string | null | undefined>;\n user: User | null;\n basePath?: string;\n}\n\n/**\n * Interactive \"Try It\" panel that lets the user execute API requests\n * using their current JWT token directly from the Studio.\n */\nexport function TryItPanel({ endpoint, apiUrl, getAuthToken, user, basePath = \"\" }: TryItPanelProps) {\n const storageKey = `rebase_apiexplorer_${endpoint.method}_${endpoint.path}`;\n\n const [pathParams, setPathParams] = useState<Record<string, string>>(() => {\n try { const v = localStorage.getItem(`${storageKey}_path`); if (v) return JSON.parse(v); } catch { /* ignore */ }\n return {};\n });\n const [queryParams, setQueryParams] = useState<Record<string, string>>(() => {\n try { const v = localStorage.getItem(`${storageKey}_query`); if (v) return JSON.parse(v); } catch { /* ignore */ }\n return {};\n });\n const [customHeaders, setCustomHeaders] = useState<Array<{ key: string; value: string }>>(() => {\n try { const v = localStorage.getItem(`${storageKey}_headers`); if (v) return JSON.parse(v); } catch { /* ignore */ }\n return [{ key: \"rebase-branch\",\nvalue: \"\" }];\n });\n const [body, setBody] = useState(() => {\n try { const v = localStorage.getItem(`${storageKey}_body`); if (v) return JSON.parse(v); } catch { /* ignore */ }\n return buildBodyTemplate(endpoint);\n });\n const [response, setResponse] = useState<{ status: number; statusText: string; body: string; time: number } | null>(\n null\n );\n const [loading, setLoading] = useState(false);\n const [authMode, setAuthMode] = useState<\"jwt\" | \"none\">(\"jwt\");\n const [validationError, setValidationError] = useState<string | null>(null);\n\n const rebaseContext = useRebaseContext();\n const currentUser = rebaseContext.authController?.user;\n\n const users = useMemo((): SelectableUser[] => {\n const managed: SelectableUser[] = [];\n if (currentUser) {\n managed.push({\n uid: currentUser.uid,\n displayName: currentUser.displayName,\n email: currentUser.email,\n photoURL: currentUser.photoURL,\n roles: currentUser.roles\n });\n }\n return managed;\n }, [currentUser]);\n\n const currentSelectableUser = useMemo((): SelectableUser | null => {\n if (!currentUser) return null;\n return {\n uid: currentUser.uid,\n displayName: currentUser.displayName,\n email: currentUser.email,\n photoURL: currentUser.photoURL,\n roles: currentUser.roles\n };\n }, [currentUser]);\n\n const [selectedUser, setSelectedUser] = useState<SelectableUser | null>(null);\n\n useEffect(() => {\n localStorage.setItem(`${storageKey}_path`, JSON.stringify(pathParams));\n localStorage.setItem(`${storageKey}_query`, JSON.stringify(queryParams));\n localStorage.setItem(`${storageKey}_headers`, JSON.stringify(customHeaders));\n localStorage.setItem(`${storageKey}_body`, JSON.stringify(body));\n }, [storageKey, pathParams, queryParams, customHeaders, body]);\n\n // Build the final URL\n const resolvedUrl = useMemo(() => {\n const base = basePath.startsWith(\"/\") ? basePath : `/${basePath}`;\n const cleanBase = base === \"/\" ? \"\" : base;\n let url = `${apiUrl.replace(/\\/+$/, \"\")}${cleanBase}${endpoint.path}`;\n // Replace path params\n for (const [key, val] of Object.entries(pathParams)) {\n url = url.replace(`{${key}}`, encodeURIComponent(val));\n }\n // Append query params\n const qp = Object.entries(queryParams).filter(([, v]) => v.trim() !== \"\");\n if (qp.length > 0) {\n url += \"?\" + qp.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join(\"&\");\n }\n return url;\n }, [apiUrl, basePath, endpoint.path, pathParams, queryParams]);\n\n const pathParamDefs = endpoint.parameters.filter((p) => p.in === \"path\");\n const queryParamDefs = endpoint.parameters.filter((p) => p.in === \"query\");\n const hasBody = [\"post\", \"put\", \"patch\"].includes(endpoint.method);\n\n const execute = useCallback(async () => {\n setValidationError(null);\n if (hasBody && body.trim()) {\n try {\n JSON.parse(body);\n } catch (err: unknown) {\n setValidationError(`Invalid JSON: ${err instanceof Error ? err.message : String(err)}`);\n return;\n }\n }\n\n setLoading(true);\n setResponse(null);\n const start = performance.now();\n\n try {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n\n for (const h of customHeaders) {\n if (h.key.trim()) headers[h.key.trim()] = h.value;\n }\n\n if (authMode === \"jwt\") {\n const token = await getAuthToken();\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n if (selectedUser && selectedUser.uid !== currentUser?.uid) {\n headers[\"x-rebase-impersonate\"] = selectedUser.uid;\n }\n }\n\n const res = await fetch(resolvedUrl, {\n method: endpoint.method.toUpperCase(),\n headers,\n body: hasBody && body.trim() ? body : undefined\n });\n\n const elapsed = Math.round(performance.now() - start);\n let text: string;\n\n const rawText = await res.text();\n try {\n const json = JSON.parse(rawText);\n text = JSON.stringify(json, null, 2);\n } catch {\n text = rawText;\n }\n\n setResponse({ status: res.status,\nstatusText: res.statusText,\nbody: text,\ntime: elapsed });\n } catch (err: unknown) {\n setResponse({\n status: 0,\n statusText: \"Network Error\",\n body: err instanceof Error ? err.message : \"Request failed\",\n time: Math.round(performance.now() - start)\n });\n } finally {\n setLoading(false);\n }\n }, [resolvedUrl, endpoint.method, hasBody, body, authMode, getAuthToken, customHeaders, selectedUser, currentUser?.uid]);\n\n return (\n <div className=\"flex flex-col h-full\">\n <div className=\"p-5 space-y-5 overflow-y-auto flex-1\">\n {/* Auth Mode */}\n <AuthSimulationSelector\n authMode={authMode}\n setAuthMode={setAuthMode}\n selectedUser={selectedUser}\n setSelectedUser={setSelectedUser}\n users={users}\n loading={false}\n currentUser={currentSelectableUser}\n />\n\n {/* Path Params */}\n {pathParamDefs.length > 0 && (\n <ParamSection\n title=\"Path Parameters\"\n params={pathParamDefs}\n values={pathParams}\n onChange={(k, v) => setPathParams((prev) => ({ ...prev,\n[k]: v }))}\n />\n )}\n\n {/* Query Params */}\n {queryParamDefs.length > 0 && (\n <ParamSection\n title=\"Query Parameters\"\n params={queryParamDefs}\n values={queryParams}\n onChange={(k, v) => setQueryParams((prev) => ({ ...prev,\n[k]: v }))}\n />\n )}\n\n {/* Custom Headers */}\n <CustomKeyValueSection\n title=\"Custom Headers\"\n values={customHeaders}\n onChange={(i, k, v) => {\n const next = [...customHeaders];\n next[i] = { key: k,\nvalue: v };\n setCustomHeaders(next);\n }}\n onAdd={() => setCustomHeaders((prev) => [...prev, { key: \"\",\nvalue: \"\" }])}\n onRemove={(i) => {\n const next = [...customHeaders];\n next.splice(i, 1);\n setCustomHeaders(next);\n }}\n />\n\n {/* Request Body */}\n {hasBody && (\n <div>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs font-semibold uppercase tracking-wider mb-2 block\"\n >\n Request Body\n </Typography>\n <TextField\n multiline\n minRows={10}\n aria-label=\"Request Body\"\n value={body}\n onChange={(e) => { setBody(e.target.value); setValidationError(null); }}\n spellCheck={false}\n error={!!validationError}\n className=\"w-full\"\n inputClassName=\"font-mono text-xs p-3 resize-y\"\n />\n {validationError && (\n <Typography variant=\"caption\" className=\"text-red-500 mt-1 block text-xs\">\n {validationError}\n </Typography>\n )}\n </div>\n )}\n\n {/* URL Preview */}\n <div className=\"rounded-lg bg-surface-100 dark:bg-surface-900 p-3\">\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-[10px] uppercase tracking-wider block mb-1\"\n >\n Request URL\n </Typography>\n <code className=\"text-xs font-mono text-text-primary dark:text-text-primary-dark break-all\">\n {resolvedUrl}\n </code>\n </div>\n\n {/* Execute Button */}\n <Button variant=\"filled\" onClick={execute} disabled={loading} className=\"w-full\">\n {loading ? (\n <span className=\"flex items-center gap-2\">\n <LoaderIcon size={iconSize.small} className=\"animate-spin\" />\n Sending…\n </span>\n ) : (\n <span className=\"flex items-center gap-2\">\n <SendIcon size={iconSize.small} />\n Send Request\n </span>\n )}\n </Button>\n\n {/* Response */}\n {response && (\n <div className={cls(\"rounded-lg border overflow-hidden\", defaultBorderMixin)}>\n <div\n className={cls(\n \"flex items-center justify-between px-4 py-2.5\",\n \"bg-surface-50 dark:bg-surface-900/50\"\n )}\n >\n <div className=\"flex items-center gap-3\">\n <ResponseBadge status={response.status}/>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs\"\n >\n {response.statusText}\n </Typography>\n </div>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs font-mono\"\n >\n {response.time}ms\n </Typography>\n </div>\n <pre\n className={cls(\n \"p-4 text-xs font-mono overflow-auto max-h-96\",\n \"bg-surface-950 text-emerald-400\",\n \"dark:bg-surface-900 dark:text-emerald-400\"\n )}\n >\n {response.body}\n </pre>\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/* ── Param Section ────────────────────────────────────────────────── */\n\nfunction ParamSection({\n title,\n params,\n values,\n onChange\n}: {\n title: string;\n params: ParsedEndpoint[\"parameters\"];\n values: Record<string, string>;\n onChange: (_key: string, _value: string) => void;\n}) {\n const paramIdBase = React.useId();\n return (\n <div>\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs font-semibold uppercase tracking-wider mb-2 block\"\n >\n {title}\n </Typography>\n <div className=\"space-y-2\">\n {params.map((p, index) => (\n <div key={p.name} className=\"flex items-center gap-3\">\n <label className=\"w-32 shrink-0\" htmlFor={`${paramIdBase}-${index}-param`}>\n <code className=\"text-xs font-mono font-semibold\">{p.name}</code>\n {p.required && <span className=\"text-red-500 ml-0.5 text-xs\">*</span>}\n </label>\n <TextField\n id={`${paramIdBase}-${index}-param`}\n size=\"small\"\n placeholder={p.description ?? p.name}\n value={values[p.name] ?? \"\"}\n onChange={(e) => onChange(p.name, e.target.value)}\n className=\"flex-1\"\n inputClassName=\"font-mono text-xs\"\n />\n </div>\n ))}\n </div>\n </div>\n );\n}\n\nfunction CustomKeyValueSection({\n title,\n values,\n onChange,\n onAdd,\n onRemove\n}: {\n title: string;\n values: Array<{ key: string; value: string }>;\n onChange: (index: number, key: string, value: string) => void;\n onAdd: () => void;\n onRemove: (index: number) => void;\n}) {\n return (\n <div>\n <div className=\"flex items-center justify-between mb-2\">\n <Typography\n variant=\"caption\"\n className=\"text-text-secondary dark:text-text-secondary-dark text-xs font-semibold uppercase tracking-wider block\"\n >\n {title}\n </Typography>\n <Button\n variant=\"text\"\n size=\"small\"\n color=\"primary\"\n onClick={onAdd}\n className=\"text-xs p-0 min-h-0\"\n >\n <PlusIcon size={iconSize.small} className=\"mr-1\" /> Add Header\n </Button>\n </div>\n <div className=\"space-y-2\">\n {values.map((v, i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <TextField\n size=\"small\"\n aria-label={`Header ${i + 1} name`}\n placeholder=\"Header name\"\n value={v.key}\n onChange={(e) => onChange(i, e.target.value, v.value)}\n className=\"w-1/3\"\n inputClassName=\"font-mono text-xs\"\n />\n <TextField\n size=\"small\"\n aria-label={`Header ${i + 1} value`}\n placeholder=\"Value\"\n value={v.value}\n onChange={(e) => onChange(i, v.key, e.target.value)}\n className=\"flex-1\"\n inputClassName=\"font-mono text-xs\"\n />\n <IconButton\n size=\"small\"\n onClick={() => onRemove(i)}\n className=\"text-text-secondary hover:text-red-500 shrink-0\"\n title=\"Remove\"\n >\n <XIcon size={iconSize.small} />\n </IconButton>\n </div>\n ))}\n {values.length === 0 && (\n <Typography variant=\"caption\" className=\"text-text-secondary/50 italic text-xs\">\n No custom headers added.\n </Typography>\n )}\n </div>\n </div>\n );\n}\n\n/* ── Helpers ──────────────────────────────────────────────────────── */\n\nfunction ResponseBadge({ status }: { status: number }) {\n let colorClass = \"text-red-500\";\n if (status >= 200 && status < 300) colorClass = \"text-emerald-500\";\n else if (status >= 300 && status < 400) colorClass = \"text-blue-500\";\n else if (status >= 400 && status < 500) colorClass = \"text-amber-500\";\n\n return (\n <span className={cls(\"text-xs font-bold font-mono\", colorClass)}>\n {status || \"ERR\"}\n </span>\n );\n}\n\nfunction buildBodyTemplate(endpoint: ParsedEndpoint): string {\n if (!endpoint.requestBody?.content) return \"{\\n \\n}\";\n const json = endpoint.requestBody.content[\"application/json\"];\n if (!json?.schema?.properties) return \"{\\n \\n}\";\n\n const props = json.schema.properties;\n const lines: string[] = [\"{\"];\n const keys = Object.keys(props);\n keys.forEach((key, i) => {\n const prop = props[key];\n if (prop.readOnly) return;\n const comma = i < keys.length - 1 ? \",\" : \"\";\n const val = defaultValue(prop);\n lines.push(` \"${key}\": ${val}${comma}`);\n });\n lines.push(\"}\");\n return lines.join(\"\\n\");\n}\n\nfunction defaultValue(schema: { type?: string; format?: string; enum?: (string | number)[] }): string {\n if (schema.enum) return JSON.stringify(schema.enum[0]);\n switch (schema.type) {\n case \"string\":\n return schema.format === \"date-time\" ? `\"${new Date().toISOString()}\"` : '\"\"';\n case \"number\":\n case \"integer\":\n return \"0\";\n case \"boolean\":\n return \"false\";\n case \"array\":\n return \"[]\";\n default:\n return \"null\";\n }\n}\n","import React, { useState, useEffect, useMemo } from \"react\";\nimport { useApiBase, useApiConfig, useAuthController } from \"@rebasepro/app\";\nimport {\n CircularProgress,\n Typography,\n Alert,\n cls,\n Button,\n Chip,\n defaultBorderMixin,\n SearchBar,\n iconSize\n} from \"@rebasepro/ui\";\nimport { BookOpenIcon, PlayIcon } from \"@rebasepro/ui\";\nimport { EndpointDetail } from \"./EndpointDetail\";\nimport { TryItPanel } from \"./TryItPanel\";\nimport type { OpenApiSpec, ParsedEndpoint } from \"./types\";\nimport { parseOpenApiSpec } from \"./parseSpec\";\n\n/**\n * Custom-built API Explorer for Rebase Studio.\n * No external dependencies — renders the OpenAPI spec natively\n * with deep integration into the Rebase auth system.\n */\nexport function ApiExplorer() {\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n const authController = useAuthController();\n const apiUrl = apiConfig?.apiUrl;\n\n const [spec, setSpec] = useState<OpenApiSpec | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const [selectedEndpoint, setSelectedEndpoint] = useState<ParsedEndpoint | null>(null);\n const [sidebarFilter, setSidebarFilter] = useState(\"\");\n const [tryItOpen, setTryItOpen] = useState(false);\n\n // Fetch OpenAPI spec\n useEffect(() => {\n if (!apiUrl) return;\n let cancelled = false;\n const specUrl = `${apiBase}/docs`;\n\n (async () => {\n try {\n // The spec endpoint itself is public on a stock backend, but\n // `apiUrl` may route through an authenticated proxy (the\n // console's Studio embed) — carry the token like every other\n // request in this view, and like LogsExplorer does.\n const getAuthToken = apiConfig?.getAuthToken ?? authController.getAuthToken;\n const token = getAuthToken ? await getAuthToken() : null;\n const res = await fetch(specUrl, token ? { headers: { Authorization: `Bearer ${token}` } } : undefined);\n if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);\n const data = await res.json();\n if (!cancelled) {\n setSpec(data);\n setLoading(false);\n }\n } catch (err: unknown) {\n if (!cancelled) {\n setError(err instanceof Error ? err.message : \"Failed to load API spec\");\n setLoading(false);\n }\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [apiUrl, apiBase, apiConfig, authController]);\n\n // Parse spec into grouped endpoints\n const { groups, allEndpoints } = useMemo(() => {\n if (!spec) return { groups: [],\nallEndpoints: [] };\n return parseOpenApiSpec(spec);\n }, [spec]);\n\n // Filter\n const filteredGroups = useMemo(() => {\n if (!sidebarFilter.trim()) return groups;\n const q = sidebarFilter.toLowerCase();\n return groups\n .map((g) => ({\n ...g,\n endpoints: g.endpoints.filter(\n (e) =>\n e.path.toLowerCase().includes(q) ||\n e.summary.toLowerCase().includes(q) ||\n e.method.toLowerCase().includes(q)\n )\n }))\n .filter((g) => g.endpoints.length > 0);\n }, [groups, sidebarFilter]);\n\n // Auto-select first endpoint\n useEffect(() => {\n if (!selectedEndpoint && allEndpoints.length > 0) {\n setSelectedEndpoint(allEndpoints[0]);\n }\n }, [allEndpoints, selectedEndpoint]);\n\n // ── States ───────────────────────────────────────────────────────\n if (!apiUrl) {\n return (\n <div className=\"flex items-center justify-center h-full w-full p-8\">\n <Alert color=\"warning\">\n <Typography variant=\"body2\">\n No API URL configured. Ensure your app provides an{\" \"}\n <code className=\"font-mono text-xs\">apiUrl</code>.\n </Typography>\n </Alert>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full w-full gap-4\">\n <CircularProgress size=\"medium\"/>\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark animate-pulse\">\n Loading API specification…\n </Typography>\n </div>\n );\n }\n\n if (error || !spec) {\n return (\n <div className=\"flex items-center justify-center h-full w-full p-8\">\n <Alert color=\"error\">\n <Typography variant=\"body2\">{error ?? \"Unknown error\"}</Typography>\n </Alert>\n </div>\n );\n }\n\n const METHOD_COLORS: Record<string, string> = {\n get: \"text-blue-600 dark:text-blue-400\",\n post: \"text-emerald-600 dark:text-emerald-400\",\n put: \"text-amber-600 dark:text-amber-400\",\n patch: \"text-orange-600 dark:text-orange-400\",\n delete: \"text-red-600 dark:text-red-400\"\n };\n\n return (\n <div className=\"flex h-full w-full overflow-hidden\">\n {/* ── Sidebar ──────────────────────────────────────── */}\n <div\n className={cls(\n \"w-72 min-w-[272px] flex flex-col h-full overflow-hidden border-r\",\n defaultBorderMixin,\n \"bg-surface-50 dark:bg-surface-900\"\n )}\n >\n {/* Header */}\n <div className=\"p-4 space-y-3\">\n <div className=\"flex items-center gap-2\">\n <BookOpenIcon size={iconSize.medium} className=\"text-primary dark:text-primary-dark\" />\n <Typography variant=\"subtitle2\" className=\"font-semibold\">\n {spec.info?.title ?? \"API Reference\"}\n </Typography>\n </div>\n {spec.info?.version && (\n <Chip size=\"smallest\" colorScheme=\"cyanDarker\">\n v{spec.info.version}\n </Chip>\n )}\n {/* Search */}\n <div className=\"mb-2\">\n <SearchBar\n placeholder=\"Filter endpoints…\"\n size=\"small\"\n onTextSearch={(val) => setSidebarFilter(val ?? \"\")}\n />\n </div>\n\n {/* Auth status removed to avoid redundancy with the AuthSimulationSelector */}\n </div>\n\n {/* Endpoint list */}\n <div className=\"flex-1 overflow-y-auto px-2 pb-4\">\n {filteredGroups.map((group) => (\n <div key={group.tag} className=\"mb-3\">\n <Typography\n variant=\"caption\"\n className=\"px-2 py-1.5 text-text-secondary dark:text-text-secondary-dark uppercase tracking-wider font-semibold text-[10px]\"\n >\n {group.tag}\n </Typography>\n {group.endpoints.map((ep) => {\n const isSelected = selectedEndpoint?.id === ep.id;\n return (\n <Button\n key={ep.id}\n variant=\"text\"\n color=\"neutral\"\n fullWidth\n onClick={() => {\n setSelectedEndpoint(ep);\n setTryItOpen(false);\n }}\n className={cls(\n \"!justify-between !px-2.5 !py-1.5 !text-left !text-sm\",\n isSelected\n ? \"bg-surface-200 dark:bg-surface-800 font-medium\"\n : \"text-text-primary dark:text-text-primary-dark\"\n )}\n >\n <span className=\"truncate text-[13px] opacity-90\">{ep.summary || ep.shortPath}</span>\n <span\n className={cls(\n \"text-[10px] font-bold uppercase shrink-0\",\n METHOD_COLORS[ep.method] ?? \"text-text-secondary\"\n )}\n >\n {ep.method}\n </span>\n </Button>\n );\n })}\n </div>\n ))}\n {filteredGroups.length === 0 && (\n <Typography\n variant=\"body2\"\n className=\"text-center text-text-secondary dark:text-text-secondary-dark py-8\"\n >\n No endpoints match\n </Typography>\n )}\n </div>\n </div>\n\n {/* ── Main content ─────────────────────────────────── */}\n <div className=\"flex-1 flex flex-col h-full overflow-hidden\">\n {selectedEndpoint ? (\n <>\n {/* Top bar */}\n <div\n className={cls(\n \"flex items-center justify-between px-5 py-3 gap-4 shrink-0 border-b z-10\",\n defaultBorderMixin,\n \"bg-surface-50/80 dark:bg-surface-950/80 backdrop-blur-md sticky top-0\"\n )}\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <span\n className={cls(\n \"text-xs font-bold uppercase\",\n METHOD_COLORS[selectedEndpoint.method] ?? \"\"\n )}\n >\n {selectedEndpoint.method}\n </span>\n <code className=\"text-sm font-mono text-text-primary dark:text-text-primary-dark truncate\">\n {selectedEndpoint.path}\n </code>\n </div>\n <Button\n variant={tryItOpen ? \"filled\" : \"outlined\"}\n size=\"small\"\n onClick={() => setTryItOpen((v) => !v)}\n >\n <PlayIcon size={iconSize.small} className=\"mr-1\" />\n Try It\n </Button>\n </div>\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto\">\n {tryItOpen ? (\n <TryItPanel\n key={selectedEndpoint.operationId || selectedEndpoint.path}\n endpoint={selectedEndpoint}\n apiUrl={apiUrl}\n getAuthToken={apiConfig?.getAuthToken ?? authController.getAuthToken}\n user={authController.user}\n basePath={spec?.servers?.[0]?.url || \"\"}\n />\n ) : (\n <EndpointDetail endpoint={selectedEndpoint} spec={spec}/>\n )}\n </div>\n </>\n ) : (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" className=\"text-text-secondary dark:text-text-secondary-dark\">\n Select an endpoint from the sidebar\n </Typography>\n </div>\n )}\n </div>\n </div>\n );\n}\n\nApiExplorer.displayName = \"ApiExplorer\";\n\n"],"mappings":"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"}
|
|
@@ -508,6 +508,7 @@ function CreateApiKeyDialog({ onClose, onCreated }) {
|
|
|
508
508
|
children: [
|
|
509
509
|
/* @__PURE__ */ jsx(TextField, {
|
|
510
510
|
size: "small",
|
|
511
|
+
"aria-label": `Permission ${idx + 1} collection`,
|
|
511
512
|
value: row.collection,
|
|
512
513
|
onChange: (e) => updateRow(idx, "collection", e.target.value),
|
|
513
514
|
placeholder: "Collection slug or *",
|
|
@@ -724,4 +725,4 @@ function SecretDisplayDialog({ keyWithSecret, onClose }) {
|
|
|
724
725
|
//#endregion
|
|
725
726
|
export { ApiKeysView };
|
|
726
727
|
|
|
727
|
-
//# sourceMappingURL=ApiKeysView-
|
|
728
|
+
//# sourceMappingURL=ApiKeysView-C_UoUPuR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKeysView-dA-OvlcH.js","names":[],"sources":["../src/components/ApiKeys/ApiKeysView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Button,\n Card,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n KeyRoundIcon,\n RefreshCwIcon,\n Tooltip,\n Typography,\n TextField,\n Select,\n SelectItem,\n Checkbox,\n CopyIcon,\n PlusIcon as AddIcon,\n Trash2Icon as DeleteIcon,\n AlertCircleIcon,\n CheckCircleIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController } from \"@rebasepro/app\";\nimport type { RebaseClient } from \"@rebasepro/types\";\n\n/* ═══════════════════════════════════════════════════════════════\n Types — mirrors server api-key-types.ts\n ═══════════════════════════════════════════════════════════════ */\n\ninterface ApiKeyPermission {\n collection: string;\n operations: (\"read\" | \"write\" | \"delete\")[];\n}\n\ninterface ApiKeyMasked {\n id: string;\n name: string;\n key_prefix: string;\n permissions: ApiKeyPermission[];\n rate_limit: number | null;\n created_by: string;\n created_at: string;\n updated_at: string;\n last_used_at: string | null;\n expires_at: string | null;\n revoked_at: string | null;\n}\n\ninterface ApiKeyWithSecret extends ApiKeyMasked {\n key: string;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Helpers\n ═══════════════════════════════════════════════════════════════ */\n\nfunction formatRelative(iso: string | null | undefined): string {\n if (!iso) return \"—\";\n const d = new Date(iso);\n const now = Date.now();\n const diff = d.getTime() - now;\n const abs = Math.abs(diff);\n if (abs < 60000) return diff > 0 ? \"in <1m\" : \"<1m ago\";\n if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }\n if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }\n return d.toLocaleDateString();\n}\n\nfunction permissionSummary(perms: ApiKeyPermission[]): string {\n if (perms.length === 0) return \"No permissions\";\n const wildcard = perms.find(p => p.collection === \"*\");\n if (wildcard) {\n return `All collections (${wildcard.operations.join(\", \")})`;\n }\n if (perms.length === 1) {\n return `${perms[0].collection} (${perms[0].operations.join(\", \")})`;\n }\n return `${perms.length} collections`;\n}\n\nfunction isExpired(key: ApiKeyMasked): boolean {\n return !!(key.expires_at && new Date(key.expires_at) < new Date());\n}\n\nfunction keyStatus(key: ApiKeyMasked): { label: string; color: string } {\n if (key.revoked_at) return { label: \"Revoked\", color: \"text-red-500\" };\n if (isExpired(key)) return { label: \"Expired\", color: \"text-amber-500\" };\n return { label: \"Active\", color: \"text-emerald-500\" };\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Main Component\n ═══════════════════════════════════════════════════════════════ */\n\nexport function ApiKeysView() {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [keys, setKeys] = useState<ApiKeyMasked[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [showCreate, setShowCreate] = useState(false);\n const [showSecret, setShowSecret] = useState<ApiKeyWithSecret | null>(null);\n const [revoking, setRevoking] = useState<string | null>(null);\n const [confirmRevoke, setConfirmRevoke] = useState<ApiKeyMasked | null>(null);\n\n const clientRef = useRef(client);\n clientRef.current = client;\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const loadKeys = useCallback(async () => {\n const c = clientRef.current;\n if (!c?.apiKeys) { setLoading(false); return; }\n try {\n const res = await c.apiKeys.listKeys();\n setKeys(res.keys);\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => { loadKeys(); }, [loadKeys]);\n\n const handleRevoke = async (id: string) => {\n const c = clientRef.current;\n if (!c?.apiKeys) return;\n setRevoking(id);\n try {\n await c.apiKeys.revokeKey(id);\n snackbarRef.current.open({ type: \"success\", message: \"API key revoked\" });\n await loadKeys();\n if (selectedId === id) setSelectedId(null);\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setRevoking(null); }\n };\n\n const handleCreated = (keyWithSecret: ApiKeyWithSecret) => {\n setShowCreate(false);\n setShowSecret(keyWithSecret);\n loadKeys();\n };\n\n const selectedKey = keys.find(k => k.id === selectedId);\n const activeKeys = keys.filter(k => !k.revoked_at && !isExpired(k));\n const inactiveKeys = keys.filter(k => k.revoked_at || isExpired(k));\n\n if (loading) return <div className=\"flex items-center justify-center h-full\"><CircularProgress/></div>;\n\n return (\n <>\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Key List ── */}\n <div className={cls(\"flex flex-col w-[340px] min-w-[280px] border-r h-full\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <KeyRoundIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">API Keys</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{activeKeys.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadKeys} title=\"Refresh\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n <Button size=\"small\" color=\"primary\" onClick={() => setShowCreate(true)} startIcon={<AddIcon size={iconSize.smallest}/>}>\n New\n </Button>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {activeKeys.length === 0 && inactiveKeys.length === 0 && (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center p-6\">\n <KeyRoundIcon size={iconSize.medium} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"secondary\">No API keys yet</Typography>\n <Typography variant=\"caption\" color=\"disabled\">Create a key to enable machine-to-machine authentication</Typography>\n </div>\n )}\n {activeKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n {inactiveKeys.length > 0 && (\n <>\n <div className=\"px-2 pt-3 pb-1\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px] uppercase tracking-wider font-medium\">Revoked / Expired</Typography>\n </div>\n {inactiveKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n </>\n )}\n </div>\n </div>\n\n {/* ── Detail Panel ── */}\n <div className=\"flex-1 flex flex-col min-w-0 h-full overflow-hidden\">\n {!selectedKey ? (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" color=\"disabled\">Select an API key to view details</Typography>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-3 min-w-0\">\n <KeyRoundIcon size={iconSize.small} className=\"text-primary shrink-0\"/>\n <div className=\"min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedKey.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"font-mono text-[11px]\">{selectedKey.key_prefix}•••</Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n {!selectedKey.revoked_at && (\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setConfirmRevoke(selectedKey)}\n disabled={revoking === selectedKey.id}\n startIcon={revoking === selectedKey.id ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n >\n Revoke\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"px-5 py-4 bg-surface-50 dark:bg-surface-900/50\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3\">\n <StatCard label=\"Status\" value={keyStatus(selectedKey).label} className={keyStatus(selectedKey).color}/>\n <StatCard label=\"Created\" value={formatRelative(selectedKey.created_at)}/>\n <StatCard label=\"Last Used\" value={formatRelative(selectedKey.last_used_at)}/>\n <StatCard label=\"Expires\" value={selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : \"Never\"}/>\n </div>\n <div className=\"grid grid-cols-2 gap-3 mt-3\">\n <StatCard label=\"Rate Limit\" value={selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : \"Default (1000/15min)\"}/>\n <StatCard label=\"Created By\" value={selectedKey.created_by} mono/>\n </div>\n </div>\n\n {/* Permissions */}\n <div className={cls(\"flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"font-semibold text-[13px]\">Permissions</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">\n {selectedKey.permissions.length}\n </Chip>\n </div>\n <div className=\"flex-1 overflow-y-auto px-5 py-3\">\n {selectedKey.permissions.length === 0 ? (\n <Typography variant=\"body2\" color=\"disabled\">No permissions configured</Typography>\n ) : (\n <div className=\"space-y-2\">\n {selectedKey.permissions.map((perm, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-3 px-3 py-2 rounded-lg border\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium flex-1\">\n {perm.collection === \"*\" ? \"* (all collections)\" : perm.collection}\n </Typography>\n <div className=\"flex items-center gap-1\">\n {perm.operations.map(op => (\n <Chip key={op} size=\"smallest\" className={cls(\n op === \"read\" && \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n op === \"write\" && \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300\",\n op === \"delete\" && \"bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300\"\n )}>{op}</Chip>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Revoke Confirmation Dialog */}\n <Dialog\n open={confirmRevoke !== null}\n onOpenChange={(open) => {\n if (!open && !revoking) setConfirmRevoke(null);\n }}\n >\n <DialogTitle hidden>Revoke Confirmation</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n Revoke "{confirmRevoke?.name}"?\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n Requests authenticated with this key will stop working immediately. This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => setConfirmRevoke(null)}\n disabled={revoking !== null}\n >\n Cancel\n </Button>\n <Button\n color=\"error\"\n disabled={revoking !== null}\n startIcon={revoking !== null ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n onClick={async () => {\n if (!confirmRevoke) return;\n await handleRevoke(confirmRevoke.id);\n setConfirmRevoke(null);\n }}\n >\n Revoke\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Create Dialog */}\n {showCreate && (\n <CreateApiKeyDialog\n onClose={() => setShowCreate(false)}\n onCreated={handleCreated}\n />\n )}\n\n {/* Secret Display Dialog */}\n {showSecret && (\n <SecretDisplayDialog\n keyWithSecret={showSecret}\n onClose={() => setShowSecret(null)}\n />\n )}\n </>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n List item\n ═══════════════════════════════════════════════════════════════ */\n\nfunction KeyListItem({ apiKey, selected, onClick }: { apiKey: ApiKeyMasked; selected: boolean; onClick: () => void }) {\n const status = keyStatus(apiKey);\n return (\n <div\n onClick={onClick}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selected\n ? \"bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950\"\n )}\n >\n <div className={cls(\"w-2 h-2 rounded-full shrink-0\",\n status.label === \"Active\" ? \"bg-emerald-400\" :\n status.label === \"Expired\" ? \"bg-amber-400\" : \"bg-red-400\"\n )}/>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{apiKey.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px] font-mono\">{apiKey.key_prefix}•••</Typography>\n </div>\n <div className=\"shrink-0\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px]\">{permissionSummary(apiKey.permissions)}</Typography>\n </div>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Stat card\n ═══════════════════════════════════════════════════════════════ */\n\nfunction StatCard({ label, value, mono, className }: { label: string; value: string; mono?: boolean; className?: string }) {\n return (\n <div className={cls(\"px-3 py-2 rounded-lg border bg-white dark:bg-surface-900\", defaultBorderMixin)}>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[10px] uppercase tracking-wider font-medium\">{label}</Typography>\n <Typography variant=\"body2\" className={cls(\n \"mt-0.5 font-semibold text-[13px]\",\n mono && \"font-mono\",\n className\n )}>{value}</Typography>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Create API Key Dialog\n ═══════════════════════════════════════════════════════════════ */\n\ninterface PermissionRow {\n collection: string;\n read: boolean;\n write: boolean;\n delete: boolean;\n}\n\nfunction CreateApiKeyDialog({ onClose, onCreated }: { onClose: () => void; onCreated: (key: ApiKeyWithSecret) => void }) {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [name, setName] = useState(\"\");\n const [permissions, setPermissions] = useState<PermissionRow[]>([{ collection: \"*\", read: true, write: false, delete: false }]);\n const [rateLimit, setRateLimit] = useState(\"\");\n const [expiresIn, setExpiresIn] = useState(\"never\");\n const [creating, setCreating] = useState(false);\n\n const addRow = () => setPermissions([...permissions, { collection: \"\", read: true, write: false, delete: false }]);\n const removeRow = (idx: number) => setPermissions(permissions.filter((_, i) => i !== idx));\n\n const updateRow = (idx: number, field: keyof PermissionRow, value: string | boolean) => {\n setPermissions(permissions.map((row, i) => i === idx ? { ...row, [field]: value } : row));\n };\n\n const handleCreate = async () => {\n if (!client?.apiKeys || !name.trim()) return;\n const apiPerms: ApiKeyPermission[] = permissions\n .filter(r => r.collection.trim())\n .map(r => ({\n collection: r.collection.trim(),\n operations: [\n ...(r.read ? [\"read\" as const] : []),\n ...(r.write ? [\"write\" as const] : []),\n ...(r.delete ? [\"delete\" as const] : [])\n ]\n }))\n .filter(p => p.operations.length > 0);\n\n if (apiPerms.length === 0) {\n snackbar.open({ type: \"error\", message: \"At least one permission is required\" });\n return;\n }\n\n let expires_at: string | null = null;\n if (expiresIn === \"7d\") expires_at = new Date(Date.now() + 7 * 86400000).toISOString();\n else if (expiresIn === \"30d\") expires_at = new Date(Date.now() + 30 * 86400000).toISOString();\n else if (expiresIn === \"90d\") expires_at = new Date(Date.now() + 90 * 86400000).toISOString();\n else if (expiresIn === \"1y\") expires_at = new Date(Date.now() + 365 * 86400000).toISOString();\n\n setCreating(true);\n try {\n const res = await client.apiKeys.createKey({\n name: name.trim(),\n permissions: apiPerms,\n rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,\n expires_at\n });\n onCreated(res.key);\n } catch (e: unknown) {\n snackbar.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setCreating(false); }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"lg\">\n <DialogTitle>Create API Key</DialogTitle>\n <DialogContent className=\"space-y-4\">\n <TextField\n label=\"Name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Analytics Pipeline\"\n size=\"small\"\n autoFocus\n />\n\n <div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[11px] uppercase tracking-wider font-medium mb-2 block\">\n Permissions\n </Typography>\n <div className=\"space-y-2\">\n {permissions.map((row, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-2 p-2 rounded-lg border\", defaultBorderMixin)}>\n <TextField\n size=\"small\"\n value={row.collection}\n onChange={(e) => updateRow(idx, \"collection\", e.target.value)}\n placeholder=\"Collection slug or *\"\n className=\"flex-1\"\n />\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.read} onCheckedChange={(v) => updateRow(idx, \"read\", !!v)}/>\n <span className=\"text-emerald-600 dark:text-emerald-400\">read</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.write} onCheckedChange={(v) => updateRow(idx, \"write\", !!v)}/>\n <span className=\"text-blue-600 dark:text-blue-400\">write</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.delete} onCheckedChange={(v) => updateRow(idx, \"delete\", !!v)}/>\n <span className=\"text-red-600 dark:text-red-400\">delete</span>\n </label>\n {permissions.length > 1 && (\n <IconButton size=\"small\" onClick={() => removeRow(idx)}>\n <DeleteIcon size={iconSize.smallest}/>\n </IconButton>\n )}\n </div>\n ))}\n </div>\n <Button size=\"small\" variant=\"text\" onClick={addRow} className=\"mt-1\" startIcon={<AddIcon size={iconSize.smallest}/>}>\n Add collection\n </Button>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <Select label=\"Expires\" value={expiresIn} onValueChange={setExpiresIn} size=\"small\" renderValue={(v) =>\n v === \"never\" ? \"Never\" :\n v === \"7d\" ? \"7 days\" :\n v === \"30d\" ? \"30 days\" :\n v === \"90d\" ? \"90 days\" : \"1 year\"\n }>\n <SelectItem value=\"never\">Never</SelectItem>\n <SelectItem value=\"7d\">7 days</SelectItem>\n <SelectItem value=\"30d\">30 days</SelectItem>\n <SelectItem value=\"90d\">90 days</SelectItem>\n <SelectItem value=\"1y\">1 year</SelectItem>\n </Select>\n </div>\n <div className=\"flex-1\">\n <TextField\n label=\"Rate Limit (per 15 min)\"\n value={rateLimit}\n onChange={(e) => setRateLimit(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"Default: 1000\"\n size=\"small\"\n />\n </div>\n </div>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={onClose}>Cancel</Button>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={creating || !name.trim()}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : undefined}\n >\n Create Key\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Secret Display Dialog — shown exactly once after creation\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SecretDisplayDialog({ keyWithSecret, onClose }: { keyWithSecret: ApiKeyWithSecret; onClose: () => void }) {\n const snackbar = useSnackbarController();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(keyWithSecret.key);\n setCopied(true);\n snackbar.open({ type: \"success\", message: \"API key copied to clipboard\" });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n snackbar.open({ type: \"error\", message: \"Failed to copy\" });\n }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"md\">\n <DialogTitle>\n <div className=\"flex items-center gap-2\">\n <CheckCircleIcon size={iconSize.small} className=\"text-emerald-500\"/>\n API Key Created\n </div>\n </DialogTitle>\n <DialogContent>\n <div className=\"p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4\">\n <div className=\"flex items-center gap-2 mb-1\">\n <AlertCircleIcon size={iconSize.smallest} className=\"text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"font-semibold text-amber-700 dark:text-amber-400\">\n Copy your key now — it won't be shown again\n </Typography>\n </div>\n <Typography variant=\"caption\" className=\"text-amber-600 dark:text-amber-300\">\n This is the only time the full API key will be displayed. Store it securely.\n </Typography>\n </div>\n\n <div className={cls(\"flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900\", defaultBorderMixin)}>\n <code className=\"flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all\">\n {keyWithSecret.key}\n </code>\n <Tooltip title={copied ? \"Copied!\" : \"Copy\"}>\n <IconButton size=\"small\" onClick={handleCopy}>\n {copied\n ? <CheckCircleIcon size={iconSize.smallest} className=\"text-emerald-500\"/>\n : <CopyIcon size={iconSize.smallest}/>\n }\n </IconButton>\n </Tooltip>\n </div>\n\n <div className=\"mt-4 space-y-1\">\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Name:</strong> {keyWithSecret.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Permissions:</strong> {permissionSummary(keyWithSecret.permissions)}\n </Typography>\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={onClose}>Done</Button>\n </DialogActions>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApiKeysView-C_UoUPuR.js","names":[],"sources":["../src/components/ApiKeys/ApiKeysView.tsx"],"sourcesContent":["\nimport React, { useState, useEffect, useRef, useCallback } from \"react\";\nimport {\n Button,\n Card,\n Chip,\n CircularProgress,\n cls,\n defaultBorderMixin,\n Dialog,\n DialogActions,\n DialogContent,\n DialogTitle,\n IconButton,\n iconSize,\n KeyRoundIcon,\n RefreshCwIcon,\n Tooltip,\n Typography,\n TextField,\n Select,\n SelectItem,\n Checkbox,\n CopyIcon,\n PlusIcon as AddIcon,\n Trash2Icon as DeleteIcon,\n AlertCircleIcon,\n CheckCircleIcon\n} from \"@rebasepro/ui\";\nimport { useRebaseClient, useSnackbarController } from \"@rebasepro/app\";\nimport type { RebaseClient } from \"@rebasepro/types\";\n\n/* ═══════════════════════════════════════════════════════════════\n Types — mirrors server api-key-types.ts\n ═══════════════════════════════════════════════════════════════ */\n\ninterface ApiKeyPermission {\n collection: string;\n operations: (\"read\" | \"write\" | \"delete\")[];\n}\n\ninterface ApiKeyMasked {\n id: string;\n name: string;\n key_prefix: string;\n permissions: ApiKeyPermission[];\n rate_limit: number | null;\n created_by: string;\n created_at: string;\n updated_at: string;\n last_used_at: string | null;\n expires_at: string | null;\n revoked_at: string | null;\n}\n\ninterface ApiKeyWithSecret extends ApiKeyMasked {\n key: string;\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Helpers\n ═══════════════════════════════════════════════════════════════ */\n\nfunction formatRelative(iso: string | null | undefined): string {\n if (!iso) return \"—\";\n const d = new Date(iso);\n const now = Date.now();\n const diff = d.getTime() - now;\n const abs = Math.abs(diff);\n if (abs < 60000) return diff > 0 ? \"in <1m\" : \"<1m ago\";\n if (abs < 3600000) { const m = Math.round(abs / 60000); return diff > 0 ? `in ${m}m` : `${m}m ago`; }\n if (abs < 86400000) { const h = Math.round(abs / 3600000); return diff > 0 ? `in ${h}h` : `${h}h ago`; }\n return d.toLocaleDateString();\n}\n\nfunction permissionSummary(perms: ApiKeyPermission[]): string {\n if (perms.length === 0) return \"No permissions\";\n const wildcard = perms.find(p => p.collection === \"*\");\n if (wildcard) {\n return `All collections (${wildcard.operations.join(\", \")})`;\n }\n if (perms.length === 1) {\n return `${perms[0].collection} (${perms[0].operations.join(\", \")})`;\n }\n return `${perms.length} collections`;\n}\n\nfunction isExpired(key: ApiKeyMasked): boolean {\n return !!(key.expires_at && new Date(key.expires_at) < new Date());\n}\n\nfunction keyStatus(key: ApiKeyMasked): { label: string; color: string } {\n if (key.revoked_at) return { label: \"Revoked\", color: \"text-red-500\" };\n if (isExpired(key)) return { label: \"Expired\", color: \"text-amber-500\" };\n return { label: \"Active\", color: \"text-emerald-500\" };\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Main Component\n ═══════════════════════════════════════════════════════════════ */\n\nexport function ApiKeysView() {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [keys, setKeys] = useState<ApiKeyMasked[]>([]);\n const [loading, setLoading] = useState(true);\n const [selectedId, setSelectedId] = useState<string | null>(null);\n const [showCreate, setShowCreate] = useState(false);\n const [showSecret, setShowSecret] = useState<ApiKeyWithSecret | null>(null);\n const [revoking, setRevoking] = useState<string | null>(null);\n const [confirmRevoke, setConfirmRevoke] = useState<ApiKeyMasked | null>(null);\n\n const clientRef = useRef(client);\n clientRef.current = client;\n const snackbarRef = useRef(snackbar);\n snackbarRef.current = snackbar;\n\n const loadKeys = useCallback(async () => {\n const c = clientRef.current;\n if (!c?.apiKeys) { setLoading(false); return; }\n try {\n const res = await c.apiKeys.listKeys();\n setKeys(res.keys);\n } catch (e: unknown) {\n snackbarRef.current.open({\n type: \"error\",\n message: e instanceof Error ? e.message : String(e)\n });\n } finally {\n setLoading(false);\n }\n }, []);\n\n useEffect(() => { loadKeys(); }, [loadKeys]);\n\n const handleRevoke = async (id: string) => {\n const c = clientRef.current;\n if (!c?.apiKeys) return;\n setRevoking(id);\n try {\n await c.apiKeys.revokeKey(id);\n snackbarRef.current.open({ type: \"success\", message: \"API key revoked\" });\n await loadKeys();\n if (selectedId === id) setSelectedId(null);\n } catch (e: unknown) {\n snackbarRef.current.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setRevoking(null); }\n };\n\n const handleCreated = (keyWithSecret: ApiKeyWithSecret) => {\n setShowCreate(false);\n setShowSecret(keyWithSecret);\n loadKeys();\n };\n\n const selectedKey = keys.find(k => k.id === selectedId);\n const activeKeys = keys.filter(k => !k.revoked_at && !isExpired(k));\n const inactiveKeys = keys.filter(k => k.revoked_at || isExpired(k));\n\n if (loading) return <div className=\"flex items-center justify-center h-full\"><CircularProgress/></div>;\n\n return (\n <>\n <div className=\"flex h-full w-full overflow-hidden bg-white dark:bg-surface-950\">\n {/* ── Key List ── */}\n <div className={cls(\"flex flex-col w-[340px] min-w-[280px] border-r h-full\", defaultBorderMixin)}>\n <div className={cls(\"flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-2\">\n <KeyRoundIcon size={iconSize.smallest} className=\"text-primary\"/>\n <Typography variant=\"subtitle2\" className=\"font-semibold\">API Keys</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">{activeKeys.length}</Chip>\n </div>\n <div className=\"flex items-center gap-1\">\n <IconButton size=\"small\" onClick={loadKeys} title=\"Refresh\"><RefreshCwIcon size={iconSize.smallest}/></IconButton>\n <Button size=\"small\" color=\"primary\" onClick={() => setShowCreate(true)} startIcon={<AddIcon size={iconSize.smallest}/>}>\n New\n </Button>\n </div>\n </div>\n <div className=\"flex-1 overflow-y-auto p-2 space-y-1\">\n {activeKeys.length === 0 && inactiveKeys.length === 0 && (\n <div className=\"flex flex-col items-center justify-center h-full gap-3 text-center p-6\">\n <KeyRoundIcon size={iconSize.medium} className=\"text-surface-300 dark:text-surface-600\"/>\n <Typography variant=\"body2\" color=\"secondary\">No API keys yet</Typography>\n <Typography variant=\"caption\" color=\"disabled\">Create a key to enable machine-to-machine authentication</Typography>\n </div>\n )}\n {activeKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n {inactiveKeys.length > 0 && (\n <>\n <div className=\"px-2 pt-3 pb-1\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px] uppercase tracking-wider font-medium\">Revoked / Expired</Typography>\n </div>\n {inactiveKeys.map(key => (\n <KeyListItem key={key.id} apiKey={key} selected={selectedId === key.id} onClick={() => setSelectedId(key.id)}/>\n ))}\n </>\n )}\n </div>\n </div>\n\n {/* ── Detail Panel ── */}\n <div className=\"flex-1 flex flex-col min-w-0 h-full overflow-hidden\">\n {!selectedKey ? (\n <div className=\"flex items-center justify-center h-full\">\n <Typography variant=\"body2\" color=\"disabled\">Select an API key to view details</Typography>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className={cls(\"flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]\", defaultBorderMixin)}>\n <div className=\"flex items-center gap-3 min-w-0\">\n <KeyRoundIcon size={iconSize.small} className=\"text-primary shrink-0\"/>\n <div className=\"min-w-0\">\n <Typography variant=\"subtitle1\" className=\"font-semibold truncate\">{selectedKey.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"font-mono text-[11px]\">{selectedKey.key_prefix}•••</Typography>\n </div>\n </div>\n <div className=\"flex items-center gap-2 shrink-0\">\n {!selectedKey.revoked_at && (\n <Button\n size=\"small\"\n color=\"error\"\n variant=\"outlined\"\n onClick={() => setConfirmRevoke(selectedKey)}\n disabled={revoking === selectedKey.id}\n startIcon={revoking === selectedKey.id ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n >\n Revoke\n </Button>\n )}\n </div>\n </div>\n\n {/* Stats */}\n <div className=\"px-5 py-4 bg-surface-50 dark:bg-surface-900/50\">\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-3\">\n <StatCard label=\"Status\" value={keyStatus(selectedKey).label} className={keyStatus(selectedKey).color}/>\n <StatCard label=\"Created\" value={formatRelative(selectedKey.created_at)}/>\n <StatCard label=\"Last Used\" value={formatRelative(selectedKey.last_used_at)}/>\n <StatCard label=\"Expires\" value={selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : \"Never\"}/>\n </div>\n <div className=\"grid grid-cols-2 gap-3 mt-3\">\n <StatCard label=\"Rate Limit\" value={selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : \"Default (1000/15min)\"}/>\n <StatCard label=\"Created By\" value={selectedKey.created_by} mono/>\n </div>\n </div>\n\n {/* Permissions */}\n <div className={cls(\"flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950\", defaultBorderMixin)}>\n <Typography variant=\"subtitle2\" className=\"font-semibold text-[13px]\">Permissions</Typography>\n <Chip size=\"smallest\" className=\"bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300\">\n {selectedKey.permissions.length}\n </Chip>\n </div>\n <div className=\"flex-1 overflow-y-auto px-5 py-3\">\n {selectedKey.permissions.length === 0 ? (\n <Typography variant=\"body2\" color=\"disabled\">No permissions configured</Typography>\n ) : (\n <div className=\"space-y-2\">\n {selectedKey.permissions.map((perm, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-3 px-3 py-2 rounded-lg border\", defaultBorderMixin)}>\n <Typography variant=\"body2\" className=\"font-mono text-[13px] font-medium flex-1\">\n {perm.collection === \"*\" ? \"* (all collections)\" : perm.collection}\n </Typography>\n <div className=\"flex items-center gap-1\">\n {perm.operations.map(op => (\n <Chip key={op} size=\"smallest\" className={cls(\n op === \"read\" && \"bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300\",\n op === \"write\" && \"bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300\",\n op === \"delete\" && \"bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300\"\n )}>{op}</Chip>\n ))}\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </>\n )}\n </div>\n </div>\n\n {/* Revoke Confirmation Dialog */}\n <Dialog\n open={confirmRevoke !== null}\n onOpenChange={(open) => {\n if (!open && !revoking) setConfirmRevoke(null);\n }}\n >\n <DialogTitle hidden>Revoke Confirmation</DialogTitle>\n <DialogContent>\n <Typography variant=\"subtitle1\" className=\"font-semibold mb-2\">\n Revoke "{confirmRevoke?.name}"?\n </Typography>\n <Typography variant=\"body2\" color=\"secondary\">\n Requests authenticated with this key will stop working immediately. This action cannot be undone.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"text\"\n onClick={() => setConfirmRevoke(null)}\n disabled={revoking !== null}\n >\n Cancel\n </Button>\n <Button\n color=\"error\"\n disabled={revoking !== null}\n startIcon={revoking !== null ? <CircularProgress size=\"smallest\"/> : <DeleteIcon size={iconSize.smallest}/>}\n onClick={async () => {\n if (!confirmRevoke) return;\n await handleRevoke(confirmRevoke.id);\n setConfirmRevoke(null);\n }}\n >\n Revoke\n </Button>\n </DialogActions>\n </Dialog>\n\n {/* Create Dialog */}\n {showCreate && (\n <CreateApiKeyDialog\n onClose={() => setShowCreate(false)}\n onCreated={handleCreated}\n />\n )}\n\n {/* Secret Display Dialog */}\n {showSecret && (\n <SecretDisplayDialog\n keyWithSecret={showSecret}\n onClose={() => setShowSecret(null)}\n />\n )}\n </>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n List item\n ═══════════════════════════════════════════════════════════════ */\n\nfunction KeyListItem({ apiKey, selected, onClick }: { apiKey: ApiKeyMasked; selected: boolean; onClick: () => void }) {\n const status = keyStatus(apiKey);\n return (\n <div\n onClick={onClick}\n className={cls(\n \"flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all\",\n selected\n ? \"bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30\"\n : \"hover:bg-surface-100 dark:hover:bg-surface-950\"\n )}\n >\n <div className={cls(\"w-2 h-2 rounded-full shrink-0\",\n status.label === \"Active\" ? \"bg-emerald-400\" :\n status.label === \"Expired\" ? \"bg-amber-400\" : \"bg-red-400\"\n )}/>\n <div className=\"flex-1 min-w-0\">\n <Typography variant=\"body2\" className=\"truncate font-medium text-[13px]\">{apiKey.name}</Typography>\n <Typography variant=\"caption\" color=\"secondary\" className=\"truncate text-[11px] font-mono\">{apiKey.key_prefix}•••</Typography>\n </div>\n <div className=\"shrink-0\">\n <Typography variant=\"caption\" color=\"disabled\" className=\"text-[10px]\">{permissionSummary(apiKey.permissions)}</Typography>\n </div>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Stat card\n ═══════════════════════════════════════════════════════════════ */\n\nfunction StatCard({ label, value, mono, className }: { label: string; value: string; mono?: boolean; className?: string }) {\n return (\n <div className={cls(\"px-3 py-2 rounded-lg border bg-white dark:bg-surface-900\", defaultBorderMixin)}>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[10px] uppercase tracking-wider font-medium\">{label}</Typography>\n <Typography variant=\"body2\" className={cls(\n \"mt-0.5 font-semibold text-[13px]\",\n mono && \"font-mono\",\n className\n )}>{value}</Typography>\n </div>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Create API Key Dialog\n ═══════════════════════════════════════════════════════════════ */\n\ninterface PermissionRow {\n collection: string;\n read: boolean;\n write: boolean;\n delete: boolean;\n}\n\nfunction CreateApiKeyDialog({ onClose, onCreated }: { onClose: () => void; onCreated: (key: ApiKeyWithSecret) => void }) {\n const client = useRebaseClient<RebaseClient>();\n const snackbar = useSnackbarController();\n const [name, setName] = useState(\"\");\n const [permissions, setPermissions] = useState<PermissionRow[]>([{ collection: \"*\", read: true, write: false, delete: false }]);\n const [rateLimit, setRateLimit] = useState(\"\");\n const [expiresIn, setExpiresIn] = useState(\"never\");\n const [creating, setCreating] = useState(false);\n\n const addRow = () => setPermissions([...permissions, { collection: \"\", read: true, write: false, delete: false }]);\n const removeRow = (idx: number) => setPermissions(permissions.filter((_, i) => i !== idx));\n\n const updateRow = (idx: number, field: keyof PermissionRow, value: string | boolean) => {\n setPermissions(permissions.map((row, i) => i === idx ? { ...row, [field]: value } : row));\n };\n\n const handleCreate = async () => {\n if (!client?.apiKeys || !name.trim()) return;\n const apiPerms: ApiKeyPermission[] = permissions\n .filter(r => r.collection.trim())\n .map(r => ({\n collection: r.collection.trim(),\n operations: [\n ...(r.read ? [\"read\" as const] : []),\n ...(r.write ? [\"write\" as const] : []),\n ...(r.delete ? [\"delete\" as const] : [])\n ]\n }))\n .filter(p => p.operations.length > 0);\n\n if (apiPerms.length === 0) {\n snackbar.open({ type: \"error\", message: \"At least one permission is required\" });\n return;\n }\n\n let expires_at: string | null = null;\n if (expiresIn === \"7d\") expires_at = new Date(Date.now() + 7 * 86400000).toISOString();\n else if (expiresIn === \"30d\") expires_at = new Date(Date.now() + 30 * 86400000).toISOString();\n else if (expiresIn === \"90d\") expires_at = new Date(Date.now() + 90 * 86400000).toISOString();\n else if (expiresIn === \"1y\") expires_at = new Date(Date.now() + 365 * 86400000).toISOString();\n\n setCreating(true);\n try {\n const res = await client.apiKeys.createKey({\n name: name.trim(),\n permissions: apiPerms,\n rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,\n expires_at\n });\n onCreated(res.key);\n } catch (e: unknown) {\n snackbar.open({ type: \"error\", message: e instanceof Error ? e.message : String(e) });\n } finally { setCreating(false); }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"lg\">\n <DialogTitle>Create API Key</DialogTitle>\n <DialogContent className=\"space-y-4\">\n <TextField\n label=\"Name\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder=\"e.g. Analytics Pipeline\"\n size=\"small\"\n autoFocus\n />\n\n <div>\n <Typography variant=\"caption\" color=\"secondary\" className=\"text-[11px] uppercase tracking-wider font-medium mb-2 block\">\n Permissions\n </Typography>\n <div className=\"space-y-2\">\n {permissions.map((row, idx) => (\n <div key={idx} className={cls(\"flex items-center gap-2 p-2 rounded-lg border\", defaultBorderMixin)}>\n <TextField\n size=\"small\"\n aria-label={`Permission ${idx + 1} collection`}\n value={row.collection}\n onChange={(e) => updateRow(idx, \"collection\", e.target.value)}\n placeholder=\"Collection slug or *\"\n className=\"flex-1\"\n />\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.read} onCheckedChange={(v) => updateRow(idx, \"read\", !!v)}/>\n <span className=\"text-emerald-600 dark:text-emerald-400\">read</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.write} onCheckedChange={(v) => updateRow(idx, \"write\", !!v)}/>\n <span className=\"text-blue-600 dark:text-blue-400\">write</span>\n </label>\n <label className=\"flex items-center gap-1 text-xs cursor-pointer\">\n <Checkbox size=\"small\" checked={row.delete} onCheckedChange={(v) => updateRow(idx, \"delete\", !!v)}/>\n <span className=\"text-red-600 dark:text-red-400\">delete</span>\n </label>\n {permissions.length > 1 && (\n <IconButton size=\"small\" onClick={() => removeRow(idx)}>\n <DeleteIcon size={iconSize.smallest}/>\n </IconButton>\n )}\n </div>\n ))}\n </div>\n <Button size=\"small\" variant=\"text\" onClick={addRow} className=\"mt-1\" startIcon={<AddIcon size={iconSize.smallest}/>}>\n Add collection\n </Button>\n </div>\n\n <div className=\"flex gap-4\">\n <div className=\"flex-1\">\n <Select label=\"Expires\" value={expiresIn} onValueChange={setExpiresIn} size=\"small\" renderValue={(v) =>\n v === \"never\" ? \"Never\" :\n v === \"7d\" ? \"7 days\" :\n v === \"30d\" ? \"30 days\" :\n v === \"90d\" ? \"90 days\" : \"1 year\"\n }>\n <SelectItem value=\"never\">Never</SelectItem>\n <SelectItem value=\"7d\">7 days</SelectItem>\n <SelectItem value=\"30d\">30 days</SelectItem>\n <SelectItem value=\"90d\">90 days</SelectItem>\n <SelectItem value=\"1y\">1 year</SelectItem>\n </Select>\n </div>\n <div className=\"flex-1\">\n <TextField\n label=\"Rate Limit (per 15 min)\"\n value={rateLimit}\n onChange={(e) => setRateLimit(e.target.value.replace(/\\D/g, \"\"))}\n placeholder=\"Default: 1000\"\n size=\"small\"\n />\n </div>\n </div>\n </DialogContent>\n <DialogActions>\n <Button variant=\"text\" onClick={onClose}>Cancel</Button>\n <Button\n color=\"primary\"\n onClick={handleCreate}\n disabled={creating || !name.trim()}\n startIcon={creating ? <CircularProgress size=\"smallest\"/> : undefined}\n >\n Create Key\n </Button>\n </DialogActions>\n </Dialog>\n );\n}\n\n/* ═══════════════════════════════════════════════════════════════\n Secret Display Dialog — shown exactly once after creation\n ═══════════════════════════════════════════════════════════════ */\n\nfunction SecretDisplayDialog({ keyWithSecret, onClose }: { keyWithSecret: ApiKeyWithSecret; onClose: () => void }) {\n const snackbar = useSnackbarController();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(keyWithSecret.key);\n setCopied(true);\n snackbar.open({ type: \"success\", message: \"API key copied to clipboard\" });\n setTimeout(() => setCopied(false), 2000);\n } catch {\n snackbar.open({ type: \"error\", message: \"Failed to copy\" });\n }\n };\n\n return (\n <Dialog open onOpenChange={(open) => { if (!open) onClose(); }} maxWidth=\"md\">\n <DialogTitle>\n <div className=\"flex items-center gap-2\">\n <CheckCircleIcon size={iconSize.small} className=\"text-emerald-500\"/>\n API Key Created\n </div>\n </DialogTitle>\n <DialogContent>\n <div className=\"p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4\">\n <div className=\"flex items-center gap-2 mb-1\">\n <AlertCircleIcon size={iconSize.smallest} className=\"text-amber-600 dark:text-amber-400\"/>\n <Typography variant=\"caption\" className=\"font-semibold text-amber-700 dark:text-amber-400\">\n Copy your key now — it won't be shown again\n </Typography>\n </div>\n <Typography variant=\"caption\" className=\"text-amber-600 dark:text-amber-300\">\n This is the only time the full API key will be displayed. Store it securely.\n </Typography>\n </div>\n\n <div className={cls(\"flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900\", defaultBorderMixin)}>\n <code className=\"flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all\">\n {keyWithSecret.key}\n </code>\n <Tooltip title={copied ? \"Copied!\" : \"Copy\"}>\n <IconButton size=\"small\" onClick={handleCopy}>\n {copied\n ? <CheckCircleIcon size={iconSize.smallest} className=\"text-emerald-500\"/>\n : <CopyIcon size={iconSize.smallest}/>\n }\n </IconButton>\n </Tooltip>\n </div>\n\n <div className=\"mt-4 space-y-1\">\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Name:</strong> {keyWithSecret.name}\n </Typography>\n <Typography variant=\"caption\" color=\"secondary\">\n <strong>Permissions:</strong> {permissionSummary(keyWithSecret.permissions)}\n </Typography>\n </div>\n </DialogContent>\n <DialogActions>\n <Button color=\"primary\" onClick={onClose}>Done</Button>\n </DialogActions>\n </Dialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -196,6 +196,7 @@ function LogsExplorer() {
|
|
|
196
196
|
}),
|
|
197
197
|
/* @__PURE__ */ jsx(TextField, {
|
|
198
198
|
size: "small",
|
|
199
|
+
"aria-label": "Search logs",
|
|
199
200
|
placeholder: "Search logs...",
|
|
200
201
|
value: searchInput,
|
|
201
202
|
onChange: (e) => setSearchInput(e.target.value),
|
|
@@ -297,4 +298,4 @@ function LogsExplorer() {
|
|
|
297
298
|
//#endregion
|
|
298
299
|
export { LogsExplorer };
|
|
299
300
|
|
|
300
|
-
//# sourceMappingURL=LogsExplorer-
|
|
301
|
+
//# sourceMappingURL=LogsExplorer-Ctk2f3MM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LogsExplorer-Ctk2f3MM.js","names":[],"sources":["../src/components/LogsExplorer/LogsExplorer.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { ArrowDownToLineIcon, Checkbox, cls, defaultBorderMixin, Label, Select, SelectItem, TextField, Typography } from \"@rebasepro/ui\";\nimport { useApiBase, useApiConfig } from \"@rebasepro/app\";\n\ninterface LogEntry {\n id: string;\n timestamp: string;\n level: \"debug\" | \"info\" | \"warn\" | \"error\";\n source: \"api\" | \"auth\" | \"storage\" | \"realtime\" | \"system\";\n message: string;\n metadata?: Record<string, unknown>;\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n debug: \"text-surface-500\",\n info: \"text-blue-600 dark:text-blue-500\",\n warn: \"text-amber-600 dark:text-amber-500\",\n error: \"text-red-600 dark:text-red-500\"\n};\n\nconst SOURCE_COLORS: Record<string, string> = {\n api: \"text-sky-600 dark:text-sky-400\",\n auth: \"text-purple-600 dark:text-purple-400\",\n storage: \"text-green-600 dark:text-green-500\",\n realtime: \"text-orange-600 dark:text-orange-400\",\n system: \"text-surface-600 dark:text-surface-400\"\n};\n\n// Ids are `log_<n>` with a monotonic counter, so numeric comparison tells\n// old entries from new ones across polls.\nconst idNum = (entry: LogEntry): number => Number(entry.id.slice(\"log_\".length));\n\n// The window is contiguous and ordered, so identical ends mean identical content.\nconst sameLogs = (a: LogEntry[], b: LogEntry[]): boolean =>\n a.length === b.length &&\n a[0]?.id === b[0]?.id &&\n a[a.length - 1]?.id === b[b.length - 1]?.id;\n\n// How close to the bottom edge still counts as \"at the bottom\".\nconst STICK_THRESHOLD = 40;\n\nexport function LogsExplorer() {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n const [level, setLevel] = useState<string>(\"all\");\n const [source, setSource] = useState<string>(\"all\");\n const [searchInput, setSearchInput] = useState(\"\");\n const [search, setSearch] = useState(\"\");\n const [autoScroll, setAutoScroll] = useState(true);\n const [error, setError] = useState<string | null>(null);\n // Entries that arrived while the user was scrolled away from the bottom.\n const [newCount, setNewCount] = useState(0);\n const [atBottom, setAtBottom] = useState(true);\n const containerRef = useRef<HTMLDivElement>(null);\n // Whether the view is stuck to the bottom right now. A ref, not state:\n // the scroll handler and the fetch loop both read it synchronously.\n const stickRef = useRef(true);\n const lastMaxIdRef = useRef<number | null>(null);\n const apiConfig = useApiConfig();\n const apiBase = useApiBase();\n\n // Debounce the search box so typing doesn't refetch per keystroke.\n useEffect(() => {\n const t = setTimeout(() => setSearch(searchInput), 300);\n return () => clearTimeout(t);\n }, [searchInput]);\n\n const fetchLogs = useCallback(async () => {\n if (!apiConfig?.apiUrl) {\n setError(\"No API URL configured — cannot load logs.\");\n return;\n }\n try {\n const params = new URLSearchParams();\n if (level && level !== \"all\") params.set(\"level\", level);\n if (source && source !== \"all\") params.set(\"source\", source);\n if (search) params.set(\"search\", search);\n params.set(\"limit\", \"200\");\n\n // Logs are admin-only, so the request must carry the auth token. The\n // URL is absolute: a relative one would resolve against the frontend\n // origin, which serves index.html rather than the API.\n const headers: Record<string, string> = {};\n const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const resp = await fetch(`${apiBase}/logs?${params}`, { headers });\n if (!resp.ok) {\n setError(resp.status === 401 || resp.status === 403\n ? \"Not authorised to read logs — an admin role is required.\"\n : `Could not load logs (HTTP ${resp.status}).`);\n return;\n }\n const data: { entries?: LogEntry[] } = await resp.json();\n // The API returns newest-first; the view tails like a terminal, so\n // flip to chronological order (newest at the bottom).\n const entries = (data.entries || []).slice().reverse();\n\n const prevMax = lastMaxIdRef.current;\n if (prevMax != null && !stickRef.current) {\n const fresh = entries.filter(e => idNum(e) > prevMax).length;\n if (fresh > 0) setNewCount(c => c + fresh);\n }\n if (entries.length > 0) lastMaxIdRef.current = idNum(entries[entries.length - 1]);\n\n // Unchanged window → keep the previous array so React leaves the\n // DOM alone (hover states and text selection survive the poll).\n setLogs(prev => sameLogs(prev, entries) ? prev : entries);\n setError(null);\n } catch (e) {\n setError(e instanceof Error ? e.message : \"Could not load logs.\");\n }\n }, [level, source, search, apiConfig, apiBase]);\n\n // A filter change replaces the window wholesale — \"new entries since last\n // poll\" stops meaning anything, so the counter starts over.\n useEffect(() => {\n lastMaxIdRef.current = null;\n setNewCount(0);\n }, [level, source, search]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let cancelled = false;\n\n fetchLogs();\n\n const scheduleNext = () => {\n if (cancelled) return;\n timeoutId = setTimeout(async () => {\n if (document.visibilityState === \"visible\") {\n await fetchLogs();\n }\n scheduleNext();\n }, 3000);\n };\n\n scheduleNext();\n\n const handleVisibility = () => {\n if (document.visibilityState === \"visible\") {\n fetchLogs();\n }\n };\n document.addEventListener(\"visibilitychange\", handleVisibility);\n\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n document.removeEventListener(\"visibilitychange\", handleVisibility);\n };\n }, [fetchLogs]);\n\n const scrollToBottom = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n el.scrollTop = el.scrollHeight;\n stickRef.current = true;\n setAtBottom(true);\n setNewCount(0);\n }, []);\n\n // Stick to the bottom only while the user is already there. Scrolling up\n // disengages; scrolling back down re-engages. Our own scrollTop writes\n // always land at the bottom, so they can never disengage it.\n const handleScroll = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;\n stickRef.current = nearBottom;\n setAtBottom(nearBottom);\n if (nearBottom) setNewCount(0);\n }, []);\n\n useLayoutEffect(() => {\n if (autoScroll && stickRef.current) {\n const el = containerRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800\">\n {/* Toolbar */}\n <div className={cls(\n \"flex gap-2 p-3 border-b items-center flex-wrap shrink-0\",\n defaultBorderMixin\n )}>\n <Select\n value={level}\n onValueChange={setLevel}\n size=\"small\"\n placeholder=\"All Levels\"\n >\n <SelectItem value=\"all\">All Levels</SelectItem>\n <SelectItem value=\"debug\">Debug</SelectItem>\n <SelectItem value=\"info\">Info</SelectItem>\n <SelectItem value=\"warn\">Warn</SelectItem>\n <SelectItem value=\"error\">Error</SelectItem>\n </Select>\n <Select\n value={source}\n onValueChange={setSource}\n size=\"small\"\n placeholder=\"All Sources\"\n >\n <SelectItem value=\"all\">All Sources</SelectItem>\n <SelectItem value=\"api\">API</SelectItem>\n <SelectItem value=\"auth\">Auth</SelectItem>\n <SelectItem value=\"storage\">Storage</SelectItem>\n <SelectItem value=\"realtime\">Realtime</SelectItem>\n <SelectItem value=\"system\">System</SelectItem>\n </Select>\n <TextField\n size=\"small\"\n aria-label=\"Search logs\"\n placeholder=\"Search logs...\"\n value={searchInput}\n onChange={e => setSearchInput(e.target.value)}\n className=\"flex-1 min-w-[200px]\"\n />\n <div className=\"flex items-center gap-1.5 cursor-pointer ml-2\">\n <Checkbox\n id=\"auto-scroll\"\n checked={autoScroll}\n onCheckedChange={(checked: boolean) => {\n setAutoScroll(checked);\n if (checked) scrollToBottom();\n }}\n size=\"small\"\n padding={false}\n />\n <Label\n htmlFor=\"auto-scroll\"\n className=\"text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400\"\n >\n Auto-scroll\n </Label>\n </div>\n <div className=\"ml-auto pl-4\">\n <Typography variant=\"caption\" color=\"secondary\">\n {logs.length} entries\n </Typography>\n </div>\n </div>\n\n {/* A failing poll must stay visible even while stale logs are on\n screen — otherwise the view quietly freezes. */}\n {error && logs.length > 0 && (\n <div className={cls(\n \"px-4 py-1.5 border-b bg-amber-50 dark:bg-amber-950/20 shrink-0\",\n defaultBorderMixin\n )}>\n <Typography variant=\"caption\" className=\"text-amber-700 dark:text-amber-400\">\n {error}\n </Typography>\n </div>\n )}\n\n {/* Log entries */}\n <div className=\"relative flex-1 min-h-0\">\n <div\n ref={containerRef}\n onScroll={handleScroll}\n className=\"h-full overflow-auto py-2\"\n >\n {logs.map(log => (\n <div\n key={log.id}\n className={cls(\n \"flex gap-4 px-4 py-[6px] border-b hover:bg-surface-100 dark:hover:bg-surface-900 transition-colors\",\n defaultBorderMixin\n )}\n >\n <Typography variant=\"body2\" color=\"secondary\" className=\"w-[72px] shrink-0 font-mono\">\n {new Date(log.timestamp).toLocaleTimeString()}\n </Typography>\n <Typography variant=\"body2\" className={cls(\"w-[48px] shrink-0 uppercase font-semibold font-mono\", LEVEL_COLORS[log.level] || \"text-surface-500\")}>\n {log.level}\n </Typography>\n <Typography variant=\"body2\" className={cls(\"w-[80px] shrink-0 font-mono\", SOURCE_COLORS[log.source] || \"text-surface-500\")}>\n [{log.source}]\n </Typography>\n <Typography variant=\"body2\" className=\"flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100\">\n {log.message}\n </Typography>\n </div>\n ))}\n {logs.length === 0 && (\n <div className=\"p-8 text-center\">\n <Typography\n variant=\"body2\"\n className={error ? \"text-red-600 dark:text-red-500\" : undefined}\n color={error ? undefined : \"secondary\"}\n >\n {error ?? \"No log entries yet. Logs will appear here as requests come in.\"}\n </Typography>\n </div>\n )}\n </div>\n\n {autoScroll && !atBottom && newCount > 0 && (\n <button\n onClick={scrollToBottom}\n className=\"absolute bottom-3 left-1/2 -translate-x-1/2 flex items-center gap-1.5 rounded-full bg-primary text-white text-xs font-medium pl-2.5 pr-3 py-1.5 shadow-md hover:bg-primary-dark transition-colors\"\n >\n <ArrowDownToLineIcon size={14}/>\n {newCount} new {newCount === 1 ? \"entry\" : \"entries\"}\n </button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|