@burdenoff/microfe-devportal 2026.720.5 → 2026.720.6

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.
Files changed (33) hide show
  1. package/dist/devportal/hooks/usePackageUpload.d.ts.map +1 -1
  2. package/dist/devportal/hooks/usePackageUpload.js.map +1 -1
  3. package/dist/devportal/pages/CreateApiKeyPage.d.ts.map +1 -1
  4. package/dist/devportal/pages/CreateApiKeyPage.js +103 -104
  5. package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
  6. package/dist/devportal/pages/CreateApplicationPage.d.ts.map +1 -1
  7. package/dist/devportal/pages/CreateApplicationPage.js +115 -116
  8. package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
  9. package/dist/devportal/pages/CreateOAuthClientPage.d.ts.map +1 -1
  10. package/dist/devportal/pages/CreateOAuthClientPage.js +129 -130
  11. package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
  12. package/dist/devportal/pages/CreateWebhookPage.js +1 -1
  13. package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
  14. package/dist/devportal/pages/DelegationsPage.js +214 -214
  15. package/dist/devportal/pages/DelegationsPage.js.map +1 -1
  16. package/dist/devportal/pages/EditApplicationPage.d.ts.map +1 -1
  17. package/dist/devportal/pages/EditApplicationPage.js +1 -1
  18. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  19. package/dist/devportal/pages/PublishPage.d.ts.map +1 -1
  20. package/dist/devportal/pages/PublishPage.js +106 -106
  21. package/dist/devportal/pages/PublishPage.js.map +1 -1
  22. package/dist/devportal/pages/SellerDashboardPage.d.ts.map +1 -1
  23. package/dist/devportal/pages/SellerDashboardPage.js +136 -137
  24. package/dist/devportal/pages/SellerDashboardPage.js.map +1 -1
  25. package/dist/devportal/pages/SellerItemsPage.d.ts.map +1 -1
  26. package/dist/devportal/pages/SellerItemsPage.js +190 -191
  27. package/dist/devportal/pages/SellerItemsPage.js.map +1 -1
  28. package/dist/devportal/pages/SellerOrdersPage.d.ts.map +1 -1
  29. package/dist/devportal/pages/SellerOrdersPage.js +138 -139
  30. package/dist/devportal/pages/SellerOrdersPage.js.map +1 -1
  31. package/dist/devportal/types/index.d.ts.map +1 -1
  32. package/dist/devportal/types/index.js.map +1 -1
  33. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA2JD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eAnKvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBA0KL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
1
+ {"version":3,"file":"usePackageUpload.d.ts","sourceRoot":"","sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AA6JD,wBAAgB,gBAAgB,CAC9B,oBAAoB,EAAE,MAAM,GAAG,SAAS,EACxC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,iBAAiB,EAAE,CAAC,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,GAAG,SAAS,EAC7D,UAAU,SAAwB;eArKvB,OAAO;cACR,MAAM;WACT,MAAM,GAAG,IAAI;mBA4KL,IAAI,KAAG,OAAO,CAAC,mBAAmB,CAAC;EAoDnD"}
@@ -1 +1 @@
1
- {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n { uploadUrl?: string; session?: { id?: string } } | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n { file?: { id?: string; downloadUrl?: string } } | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePackageUpload.js","names":[],"sources":["../../../src/devportal/hooks/usePackageUpload.ts"],"sourcesContent":["/**\n * usePackageUpload — uploads a zip package to wspace-files-svc and returns a\n * stable downloadUrl to store as ApplicationVersion.packageUrl.\n *\n * Flow (mirrors uploadBlobToFiles in @burdenoff/fe-libs/storage):\n * 1. ensureFolderByPath(\"/DevPortal/Packages\") → folderId\n * 2. initiateUpload → { sessionId, presignedUrl }\n * 3. PUT blob to presignedUrl (progress tracked via XHR)\n * 4. completeUpload → { file.downloadUrl }\n *\n * Uses raw fetch for GraphQL mutations so we can target the workspace gateway\n * without a second ApolloProvider.\n */\nimport { useCallback, useState } from \"react\";\n\nexport interface PackageUploadResult {\n fileId: string;\n downloadUrl: string;\n}\n\nexport interface PackageUploadState {\n uploading: boolean;\n progress: number;\n error: string | null;\n}\n\ntype GqlFetcher = (\n query: string,\n variables: Record<string, unknown>,\n) => Promise<Record<string, unknown>>;\n\nfunction makeFetcher(\n workspacesGatewayUrl: string,\n authToken: string | undefined,\n workspaceId: string | undefined,\n workspaceToken: string | undefined,\n): GqlFetcher {\n return async (query, variables) => {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n if (authToken) headers[\"Authorization\"] = `Bearer ${authToken}`;\n if (workspaceToken)\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n if (workspaceId) headers[\"x-workspace-id\"] = workspaceId;\n\n const endpoint = workspacesGatewayUrl.endsWith(\"/graphql\")\n ? workspacesGatewayUrl\n : `${workspacesGatewayUrl}/workspaces/graphql`;\n\n const res = await fetch(endpoint, {\n method: \"POST\",\n headers,\n credentials: \"include\",\n body: JSON.stringify({ query, variables }),\n });\n if (!res.ok) {\n throw new Error(\n `GraphQL request failed: ${res.status} ${res.statusText}`,\n );\n }\n const json = (await res.json()) as {\n data?: Record<string, unknown>;\n errors?: Array<{ message: string }>;\n };\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join(\"; \"));\n }\n return json.data ?? {};\n };\n}\n\nasync function ensureFolder(gql: GqlFetcher, path: string): Promise<string> {\n const data = await gql(\n `mutation DevPortal_EnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }`,\n { path },\n );\n const id = (data.ensureFolderByPath as { id?: string } | undefined)?.id;\n if (!id) throw new Error(\"ensureFolderByPath returned no id\");\n return id;\n}\n\nasync function initiateUpload(\n gql: GqlFetcher,\n file: File,\n folderId: string,\n): Promise<{ sessionId: string; uploadUrl: string }> {\n const ext = file.name.includes(\".\")\n ? file.name.substring(file.name.lastIndexOf(\".\") + 1)\n : \"zip\";\n const data = await gql(\n `mutation DevPortal_InitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }`,\n {\n input: {\n fileName: file.name,\n fileSize: String(file.size),\n mimeType: file.type || \"application/zip\",\n extension: ext,\n folderId,\n visibility: \"PUBLIC\",\n },\n },\n );\n const result = data.initiateUpload as\n | { uploadUrl?: string; session?: { id?: string } }\n | undefined;\n const sessionId = result?.session?.id;\n const uploadUrl = result?.uploadUrl;\n if (!sessionId || !uploadUrl) {\n throw new Error(\"initiateUpload did not return sessionId or uploadUrl\");\n }\n return { sessionId, uploadUrl };\n}\n\nasync function putToPresignedUrl(\n file: File,\n uploadUrl: string,\n onProgress: (pct: number) => void,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.upload.addEventListener(\"progress\", (evt) => {\n if (evt.lengthComputable) {\n onProgress(10 + Math.round((evt.loaded / evt.total) * 80));\n }\n });\n xhr.addEventListener(\"load\", () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n resolve();\n } else {\n reject(new Error(`S3 PUT failed: ${xhr.status}`));\n }\n });\n xhr.addEventListener(\"error\", () =>\n reject(new Error(\"S3 PUT network error\")),\n );\n xhr.addEventListener(\"abort\", () => reject(new Error(\"S3 PUT aborted\")));\n xhr.open(\"PUT\", uploadUrl);\n xhr.setRequestHeader(\"Content-Type\", file.type || \"application/zip\");\n xhr.send(file);\n });\n}\n\nasync function completeUpload(\n gql: GqlFetcher,\n sessionId: string,\n folderId: string,\n fileSize: number,\n): Promise<PackageUploadResult> {\n const data = await gql(\n `mutation DevPortal_CompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n file { id downloadUrl }\n }\n }`,\n {\n input: {\n sessionId,\n folderId,\n fileSize: String(fileSize),\n },\n },\n );\n const file = (\n data.completeUpload as\n | { file?: { id?: string; downloadUrl?: string } }\n | undefined\n )?.file;\n if (!file?.id || !file.downloadUrl) {\n throw new Error(\"completeUpload did not return file id or downloadUrl\");\n }\n return { fileId: file.id, downloadUrl: file.downloadUrl };\n}\n\nexport function usePackageUpload(\n workspacesGatewayUrl: string | undefined,\n authToken: string | undefined,\n workspaceId: string | undefined,\n getWorkspaceToken: (() => Promise<string | null>) | undefined,\n folderPath = \"/DevPortal/Packages\",\n) {\n const [state, setState] = useState<PackageUploadState>({\n uploading: false,\n progress: 0,\n error: null,\n });\n\n const upload = useCallback(\n async (file: File): Promise<PackageUploadResult> => {\n if (!workspacesGatewayUrl) {\n throw new Error(\n \"workspacesGatewayUrl is not configured. Pass it as a prop to DevportalRoot.\",\n );\n }\n\n const freshToken = getWorkspaceToken\n ? await getWorkspaceToken()\n : undefined;\n const gql = makeFetcher(\n workspacesGatewayUrl,\n authToken,\n workspaceId,\n freshToken ?? undefined,\n );\n\n setState({ uploading: true, progress: 0, error: null });\n try {\n const folderId = await ensureFolder(gql, folderPath);\n setState((s) => ({ ...s, progress: 5 }));\n\n const { sessionId, uploadUrl } = await initiateUpload(\n gql,\n file,\n folderId,\n );\n setState((s) => ({ ...s, progress: 10 }));\n\n await putToPresignedUrl(file, uploadUrl, (pct) =>\n setState((s) => ({ ...s, progress: pct })),\n );\n setState((s) => ({ ...s, progress: 90 }));\n\n const result = await completeUpload(\n gql,\n sessionId,\n folderId,\n file.size,\n );\n setState({ uploading: false, progress: 100, error: null });\n return result;\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState({ uploading: false, progress: 0, error: message });\n throw err;\n }\n },\n [workspacesGatewayUrl, authToken, workspaceId, getWorkspaceToken],\n );\n\n return { upload, ...state };\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CreateApiKeyPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAsChC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,EA0V9B,CAAC"}
1
+ {"version":3,"file":"CreateApiKeyPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAqChC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,EA0V9B,CAAC"}
@@ -8,15 +8,14 @@ import { Alert as u } from "../components/ConfirmDialog.js";
8
8
  import { PermissionGuard as d } from "../components/PermissionGuard.js";
9
9
  import "../components/index.js";
10
10
  import "../hooks/index.js";
11
- import { DEVPORTAL_PERMISSIONS as f } from "../constants/permissions.js";
12
- import { executeRecaptcha as p } from "../utils/recaptcha.js";
13
- import { useState as m } from "react";
14
- import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
- import { Loader2 as g, Save as _, X as v } from "lucide-react";
16
- import { jsx as y, jsxs as b } from "react/jsx-runtime";
17
- import { OptionalFields as x } from "@burdenoff/fe-libs/ui";
11
+ import { executeRecaptcha as f } from "../utils/recaptcha.js";
12
+ import { useState as p } from "react";
13
+ import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
14
+ import { Loader2 as h, Save as g, X as _ } from "lucide-react";
15
+ import { jsx as v, jsxs as y } from "react/jsx-runtime";
16
+ import { OptionalFields as b } from "@burdenoff/fe-libs/ui";
18
17
  //#region src/devportal/pages/CreateApiKeyPage.tsx
19
- var S = {
18
+ var x = {
20
19
  "30days": 720 * 60 * 60 * 1e3,
21
20
  "90days": 2160 * 60 * 60 * 1e3,
22
21
  "180days": 4320 * 60 * 60 * 1e3,
@@ -24,212 +23,212 @@ var S = {
24
23
  "2years": 2 * 365 * 24 * 60 * 60 * 1e3,
25
24
  never: null
26
25
  };
27
- function C(e) {
28
- let t = S[e];
26
+ function S(e) {
27
+ let t = x[e];
29
28
  if (t != null) return new Date(Date.now() + t).toISOString();
30
29
  }
31
- var w = () => {
32
- let { t: S } = h(), w = (e, t) => {
33
- let n = S(e);
30
+ var C = () => {
31
+ let { t: x } = m(), C = (e, t) => {
32
+ let n = x(e);
34
33
  return n === e ? t : n;
35
- }, { navigate: T, basePath: E } = e(), [D, O] = m(null), [k, A] = m({}), [j, M] = m(null), [N, { loading: P }] = r(), F = t(), { data: I, loading: L } = n({ fetchPolicy: "cache-and-network" }), R = I?.currentUserApps?.apps.map((e) => ({
34
+ }, { navigate: w, basePath: T } = e(), [E, D] = p(null), [O, k] = p({}), [A, j] = p(null), [M, { loading: N }] = r(), P = t(), { data: F, loading: I } = n({ fetchPolicy: "cache-and-network" }), L = F?.currentUserApps?.apps.map((e) => ({
36
35
  id: e.id,
37
36
  name: e.name,
38
37
  clientId: e.clientId
39
- })) ?? [], [z, B] = m({
38
+ })) ?? [], [R, z] = p({
40
39
  appId: "",
41
40
  name: "",
42
41
  scopes: [],
43
42
  expiresIn: "1year"
44
- }), V = (e) => {
45
- T?.(`${E}${e}`);
46
- }, H = () => {
47
- V("/api-keys");
48
- }, U = (e, t) => {
49
- B((n) => ({
43
+ }), B = (e) => {
44
+ w?.(`${T}${e}`);
45
+ }, V = () => {
46
+ B("/api-keys");
47
+ }, H = (e, t) => {
48
+ z((n) => ({
50
49
  ...n,
51
50
  [e]: t
52
- })), k[e] && A((t) => ({
51
+ })), O[e] && k((t) => ({
53
52
  ...t,
54
53
  [e]: ""
55
- })), j && M(null);
56
- }, W = () => {
54
+ })), A && j(null);
55
+ }, U = () => {
57
56
  let e = {};
58
- return z.appId || (e.appId = w("devportal.selectAppError", "Please select an app to associate this key with")), z.name.trim() || (e.name = w("devportal.apiKeyNameRequired", "API key name is required")), z.scopes.length === 0 && (e.scopes = w("devportal.atLeastOneScopeRequired", "At least one scope is required")), A(e), Object.keys(e).length === 0;
59
- }, G = async (e) => {
60
- if (e.preventDefault(), W()) {
61
- M(null);
57
+ return R.appId || (e.appId = C("devportal.selectAppError", "Please select an app to associate this key with")), R.name.trim() || (e.name = C("devportal.apiKeyNameRequired", "API key name is required")), R.scopes.length === 0 && (e.scopes = C("devportal.atLeastOneScopeRequired", "At least one scope is required")), k(e), Object.keys(e).length === 0;
58
+ }, W = async (e) => {
59
+ if (e.preventDefault(), U()) {
60
+ j(null);
62
61
  try {
63
- let e = C(z.expiresIn), t = await p("api_key_create"), { data: n } = await N({ variables: { input: {
64
- appId: z.appId,
65
- name: z.name.trim(),
66
- scope: z.scopes,
62
+ let e = S(R.expiresIn), t = await f("api_key_create"), { data: n } = await M({ variables: { input: {
63
+ appId: R.appId,
64
+ name: R.name.trim(),
65
+ scope: R.scopes,
67
66
  expiresAt: e,
68
67
  ...t ? { recaptchaToken: t } : {}
69
68
  } } });
70
- n?.generateAPIKey && (F.emit("devportal.apikey.generated", {
69
+ n?.generateAPIKey && (P.emit("devportal.apikey.generated", {
71
70
  keyId: n.generateAPIKey.keyId,
72
- appId: z.appId
73
- }), O({
71
+ appId: R.appId
72
+ }), D({
74
73
  apiKey: n.generateAPIKey.apiKey,
75
74
  apiSecret: n.generateAPIKey.apiSecret,
76
75
  keyId: n.generateAPIKey.keyId
77
76
  }));
78
77
  } catch (e) {
79
- M(e instanceof Error ? e.message : w("devportal.failedToGenerateApiKey", "Failed to generate API key"));
78
+ j(e instanceof Error ? e.message : C("devportal.failedToGenerateApiKey", "Failed to generate API key"));
80
79
  }
81
80
  }
82
- }, K = () => {
83
- V("/api-keys");
81
+ }, G = () => {
82
+ B("/api-keys");
84
83
  };
85
- return D ? /* @__PURE__ */ y(i, {
84
+ return E ? /* @__PURE__ */ v(i, {
86
85
  title: "API Key Generated",
87
86
  description: "Your new API key has been generated successfully",
88
- children: /* @__PURE__ */ b("div", {
87
+ children: /* @__PURE__ */ y("div", {
89
88
  className: "max-w-2xl space-y-6",
90
89
  children: [
91
- /* @__PURE__ */ y(o, {
90
+ /* @__PURE__ */ v(o, {
92
91
  title: "API Key & Secret",
93
- secret: D.apiSecret,
92
+ secret: E.apiSecret,
94
93
  description: "Store this secret securely — you won't be able to see it again.",
95
- onDismiss: K
94
+ onDismiss: G
96
95
  }),
97
- /* @__PURE__ */ y(u, {
96
+ /* @__PURE__ */ v(u, {
98
97
  variant: "info",
99
98
  title: "Usage Instructions",
100
- children: /* @__PURE__ */ b("div", {
99
+ children: /* @__PURE__ */ y("div", {
101
100
  className: "mt-2 space-y-2",
102
101
  children: [
103
- /* @__PURE__ */ y("p", { children: "Your API key details:" }),
104
- /* @__PURE__ */ b("div", {
102
+ /* @__PURE__ */ v("p", { children: "Your API key details:" }),
103
+ /* @__PURE__ */ y("div", {
105
104
  className: "space-y-1",
106
- children: [/* @__PURE__ */ b("div", {
105
+ children: [/* @__PURE__ */ y("div", {
107
106
  className: "flex items-center gap-2",
108
- children: [/* @__PURE__ */ y("span", {
107
+ children: [/* @__PURE__ */ v("span", {
109
108
  className: "text-xs font-medium text-text-secondary",
110
109
  children: "Key ID:"
111
- }), /* @__PURE__ */ y("code", {
110
+ }), /* @__PURE__ */ v("code", {
112
111
  className: "rounded bg-bg-sunken px-2 py-0.5 text-sm",
113
- children: D.keyId
112
+ children: E.keyId
114
113
  })]
115
- }), /* @__PURE__ */ b("div", {
114
+ }), /* @__PURE__ */ y("div", {
116
115
  className: "flex items-center gap-2",
117
- children: [/* @__PURE__ */ y("span", {
116
+ children: [/* @__PURE__ */ v("span", {
118
117
  className: "text-xs font-medium text-text-secondary",
119
118
  children: "API Key:"
120
- }), /* @__PURE__ */ y("code", {
119
+ }), /* @__PURE__ */ v("code", {
121
120
  className: "rounded bg-bg-sunken px-2 py-0.5 text-sm",
122
- children: D.apiKey
121
+ children: E.apiKey
123
122
  })]
124
123
  })]
125
124
  }),
126
- /* @__PURE__ */ y("p", {
125
+ /* @__PURE__ */ v("p", {
127
126
  className: "text-xs text-text-secondary mt-2",
128
127
  children: "Use the API key and secret to authenticate your requests."
129
128
  })
130
129
  ]
131
130
  })
132
131
  }),
133
- /* @__PURE__ */ y("div", {
132
+ /* @__PURE__ */ v("div", {
134
133
  className: "flex gap-3",
135
- children: /* @__PURE__ */ y("button", {
136
- onClick: K,
134
+ children: /* @__PURE__ */ v("button", {
135
+ onClick: G,
137
136
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
138
137
  children: "Continue to API Keys"
139
138
  })
140
139
  })
141
140
  ]
142
141
  })
143
- }) : /* @__PURE__ */ y(d, {
144
- permission: f.APP_UPDATE,
145
- children: /* @__PURE__ */ y(i, {
142
+ }) : /* @__PURE__ */ v(d, {
143
+ authOnly: !0,
144
+ children: /* @__PURE__ */ v(i, {
146
145
  title: "Generate API Key",
147
146
  description: "Generate a new API key for programmatic access",
148
147
  purpose: "Create a key your backend or scripts can use to call the API without a logged-in user. Scope it to one app and the minimum permissions it needs — the secret is shown only once at creation, so copy it somewhere safe.",
149
148
  backLink: "/api-keys",
150
149
  backLabel: "Back to API Keys",
151
- children: /* @__PURE__ */ b("form", {
152
- onSubmit: G,
150
+ children: /* @__PURE__ */ y("form", {
151
+ onSubmit: W,
153
152
  className: "max-w-3xl space-y-8",
154
153
  children: [
155
- j && /* @__PURE__ */ y(u, {
154
+ A && /* @__PURE__ */ v(u, {
156
155
  variant: "error",
157
156
  title: "Error Generating API Key",
158
- onDismiss: () => M(null),
159
- children: j
157
+ onDismiss: () => j(null),
158
+ children: A
160
159
  }),
161
- /* @__PURE__ */ y(a, {
160
+ /* @__PURE__ */ v(a, {
162
161
  title: "Basic Information",
163
162
  description: "Configure the API key settings",
164
- children: /* @__PURE__ */ b("div", {
163
+ children: /* @__PURE__ */ y("div", {
165
164
  className: "space-y-4",
166
- children: [L ? /* @__PURE__ */ b("div", {
165
+ children: [I ? /* @__PURE__ */ y("div", {
167
166
  className: "flex items-center gap-2 py-2",
168
- children: [/* @__PURE__ */ y(g, { className: "h-4 w-4 animate-spin text-text-secondary" }), /* @__PURE__ */ y("span", {
167
+ children: [/* @__PURE__ */ v(h, { className: "h-4 w-4 animate-spin text-text-secondary" }), /* @__PURE__ */ v("span", {
169
168
  className: "text-sm text-text-secondary",
170
169
  children: "Loading apps..."
171
170
  })]
172
- }) : R.length === 0 ? /* @__PURE__ */ y(u, {
171
+ }) : L.length === 0 ? /* @__PURE__ */ v(u, {
173
172
  variant: "warning",
174
173
  title: "No Apps Found",
175
- children: /* @__PURE__ */ b("p", { children: [
174
+ children: /* @__PURE__ */ y("p", { children: [
176
175
  "You need to create an OAuth app first before generating API keys.",
177
176
  " ",
178
- /* @__PURE__ */ y("button", {
177
+ /* @__PURE__ */ v("button", {
179
178
  type: "button",
180
- onClick: () => V("/oauth/new"),
179
+ onClick: () => B("/oauth/new"),
181
180
  className: "text-primary underline hover:no-underline",
182
181
  children: "Create an app"
183
182
  })
184
183
  ] })
185
- }) : /* @__PURE__ */ y(c, {
184
+ }) : /* @__PURE__ */ v(c, {
186
185
  label: "App",
187
- value: z.appId,
188
- onChange: (e) => U("appId", e.target.value),
189
- options: R.map((e) => ({
186
+ value: R.appId,
187
+ onChange: (e) => H("appId", e.target.value),
188
+ options: L.map((e) => ({
190
189
  value: e.id,
191
190
  label: `${e.name} (${e.clientId})`
192
191
  })),
193
192
  placeholder: "Select an app…",
194
- error: k.appId,
193
+ error: O.appId,
195
194
  required: !0,
196
195
  hint: "The OAuth app this API key will be associated with"
197
- }), /* @__PURE__ */ y(s, {
196
+ }), /* @__PURE__ */ v(s, {
198
197
  label: "Key Name",
199
198
  placeholder: "e.g., Production Backend Key",
200
- value: z.name,
201
- onChange: (e) => U("name", e.target.value),
202
- error: k.name,
199
+ value: R.name,
200
+ onChange: (e) => H("name", e.target.value),
201
+ error: O.name,
203
202
  required: !0,
204
203
  hint: "A descriptive name to identify this key"
205
204
  })]
206
205
  })
207
206
  }),
208
- /* @__PURE__ */ y(a, {
207
+ /* @__PURE__ */ v(a, {
209
208
  title: "Scopes",
210
209
  description: "Define what this API key can access",
211
- children: /* @__PURE__ */ y("div", {
210
+ children: /* @__PURE__ */ v("div", {
212
211
  className: "space-y-4",
213
- children: /* @__PURE__ */ y(l, {
212
+ children: /* @__PURE__ */ v(l, {
214
213
  label: "Scopes",
215
- value: z.scopes,
216
- onChange: (e) => U("scopes", e),
214
+ value: R.scopes,
215
+ onChange: (e) => H("scopes", e),
217
216
  placeholder: "e.g., workspace:read, vibecontrols:write",
218
217
  hint: "Press Enter to add each scope. At least one scope is required.",
219
- error: k.scopes
218
+ error: O.scopes
220
219
  })
221
220
  })
222
221
  }),
223
- /* @__PURE__ */ y(x, {
222
+ /* @__PURE__ */ v(b, {
224
223
  title: "Advanced settings",
225
224
  description: "Rate-limit overrides, allowed origins, scope tuning, and expiration.",
226
- children: /* @__PURE__ */ b(a, {
225
+ children: /* @__PURE__ */ y(a, {
227
226
  title: "Key Expiration",
228
227
  description: "Set when this key should expire",
229
- children: [/* @__PURE__ */ y(c, {
228
+ children: [/* @__PURE__ */ v(c, {
230
229
  label: "Expires In",
231
- value: z.expiresIn,
232
- onChange: (e) => U("expiresIn", e.target.value),
230
+ value: R.expiresIn,
231
+ onChange: (e) => H("expiresIn", e.target.value),
233
232
  options: [
234
233
  {
235
234
  value: "30days",
@@ -257,25 +256,25 @@ var w = () => {
257
256
  }
258
257
  ],
259
258
  hint: "Keys can be revoked at any time regardless of expiration"
260
- }), z.expiresIn === "never" && /* @__PURE__ */ y(u, {
259
+ }), R.expiresIn === "never" && /* @__PURE__ */ v(u, {
261
260
  variant: "warning",
262
261
  className: "mt-4",
263
262
  children: "Non-expiring keys are a security risk. Consider setting an expiration and rotating keys periodically."
264
263
  })]
265
264
  })
266
265
  }),
267
- /* @__PURE__ */ b("div", {
266
+ /* @__PURE__ */ y("div", {
268
267
  className: "flex items-center justify-end gap-3 border-t pt-6",
269
- children: [/* @__PURE__ */ b("button", {
268
+ children: [/* @__PURE__ */ y("button", {
270
269
  type: "button",
271
- onClick: H,
270
+ onClick: V,
272
271
  className: "inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors",
273
- children: [/* @__PURE__ */ y(v, { size: 16 }), "Cancel"]
274
- }), /* @__PURE__ */ b("button", {
272
+ children: [/* @__PURE__ */ v(_, { size: 16 }), "Cancel"]
273
+ }), /* @__PURE__ */ y("button", {
275
274
  type: "submit",
276
- disabled: P || R.length === 0,
275
+ disabled: N || L.length === 0,
277
276
  className: "inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50",
278
- children: [/* @__PURE__ */ y(_, { size: 16 }), P ? "Generating..." : "Generate API Key"]
277
+ children: [/* @__PURE__ */ v(g, { size: 16 }), N ? "Generating..." : "Generate API Key"]
279
278
  })]
280
279
  })
281
280
  ]
@@ -284,6 +283,6 @@ var w = () => {
284
283
  });
285
284
  };
286
285
  //#endregion
287
- export { w as CreateApiKeyPage };
286
+ export { C as CreateApiKeyPage };
288
287
 
289
288
  //# sourceMappingURL=CreateApiKeyPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreateApiKeyPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { OptionalFields } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, SelectField, TagInput } from \"../components/FormField\";\nimport { NewSecretAlert } from \"../components/SecretDisplay\";\nimport { Alert } from \"../components/ConfirmDialog\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useGenerateApiKeyMutation, useCurrentUserAppsQuery } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { executeRecaptcha } from \"../utils/recaptcha\";\n\ninterface CreateApiKeyFormData {\n appId: string;\n name: string;\n scopes: string[];\n expiresIn: string;\n}\n\nconst EXPIRATION_DURATIONS: Record<string, number | null> = {\n \"30days\": 30 * 24 * 60 * 60 * 1000,\n \"90days\": 90 * 24 * 60 * 60 * 1000,\n \"180days\": 180 * 24 * 60 * 60 * 1000,\n \"1year\": 365 * 24 * 60 * 60 * 1000,\n \"2years\": 2 * 365 * 24 * 60 * 60 * 1000,\n never: null,\n};\n\nfunction computeExpiresAt(expiresIn: string): string | undefined {\n const duration = EXPIRATION_DURATIONS[expiresIn];\n if (duration == null) return undefined;\n return new Date(Date.now() + duration).toISOString();\n}\n\n/**\n * Page for generating a new API key via auth-svc.\n *\n * Previously created API keys through devportal-svc per application;\n * now generates keys through auth-svc's generateAPIKey mutation.\n * The user selects which OAuth app to associate the key with.\n */\nexport const CreateApiKeyPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { navigate, basePath } = useDevportal();\n const [newApiKey, setNewApiKey] = useState<{\n apiKey: string;\n apiSecret: string;\n keyId: string;\n } | null>(null);\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [mutationError, setMutationError] = useState<string | null>(null);\n\n const [generateApiKey, { loading: isSubmitting }] =\n useGenerateApiKeyMutation();\n const bus = useDevportalEventBus();\n\n // Fetch user's apps to populate the app selector\n const { data: appsData, loading: appsLoading } = useCurrentUserAppsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const apps: Array<{ id: string; name: string; clientId: string }> =\n appsData?.currentUserApps?.apps.map(\n (app: { id: string; name: string; clientId: string }) => ({\n id: app.id,\n name: app.name,\n clientId: app.clientId,\n }),\n ) ?? [];\n\n const [formData, setFormData] = useState<CreateApiKeyFormData>({\n appId: \"\",\n name: \"\",\n scopes: [],\n expiresIn: \"1year\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCancel = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const updateField = <K extends keyof CreateApiKeyFormData>(\n field: K,\n value: CreateApiKeyFormData[K],\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: \"\" }));\n }\n if (mutationError) {\n setMutationError(null);\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.appId) {\n newErrors.appId = tr(\n \"devportal.selectAppError\",\n \"Please select an app to associate this key with\",\n );\n }\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.apiKeyNameRequired\",\n \"API key name is required\",\n );\n }\n\n if (formData.scopes.length === 0) {\n newErrors.scopes = tr(\n \"devportal.atLeastOneScopeRequired\",\n \"At least one scope is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n setMutationError(null);\n\n try {\n const expiresAt = computeExpiresAt(formData.expiresIn);\n const recaptchaToken = await executeRecaptcha(\"api_key_create\");\n const input = {\n appId: formData.appId,\n name: formData.name.trim(),\n scope: formData.scopes,\n expiresAt,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n };\n\n const { data } = await generateApiKey({\n variables: {\n input,\n },\n });\n\n if (data?.generateAPIKey) {\n bus.emit(\"devportal.apikey.generated\", {\n keyId: data.generateAPIKey.keyId,\n appId: formData.appId,\n });\n setNewApiKey({\n apiKey: data.generateAPIKey.apiKey,\n apiSecret: data.generateAPIKey.apiSecret,\n keyId: data.generateAPIKey.keyId,\n });\n }\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.failedToGenerateApiKey\",\n \"Failed to generate API key\",\n );\n setMutationError(message);\n }\n };\n\n const handleKeyDismiss = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const expirationOptions = [\n { value: \"30days\", label: \"30 days\" },\n { value: \"90days\", label: \"90 days\" },\n { value: \"180days\", label: \"180 days\" },\n { value: \"1year\", label: \"1 year\" },\n { value: \"2years\", label: \"2 years\" },\n { value: \"never\", label: \"Never expires\" },\n ];\n\n if (newApiKey) {\n return (\n <PageLayout\n title=\"API Key Generated\"\n description=\"Your new API key has been generated successfully\"\n >\n <div className=\"max-w-2xl space-y-6\">\n <NewSecretAlert\n title=\"API Key & Secret\"\n secret={newApiKey.apiSecret}\n description=\"Store this secret securely — you won't be able to see it again.\"\n onDismiss={handleKeyDismiss}\n />\n\n <Alert variant=\"info\" title=\"Usage Instructions\">\n <div className=\"mt-2 space-y-2\">\n <p>Your API key details:</p>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Key ID:\n </span>\n <code className=\"rounded bg-bg-sunken px-2 py-0.5 text-sm\">\n {newApiKey.keyId}\n </code>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-text-secondary\">\n API Key:\n </span>\n <code className=\"rounded bg-bg-sunken px-2 py-0.5 text-sm\">\n {newApiKey.apiKey}\n </code>\n </div>\n </div>\n <p className=\"text-xs text-text-secondary mt-2\">\n Use the API key and secret to authenticate your requests.\n </p>\n </div>\n </Alert>\n\n <div className=\"flex gap-3\">\n <button\n onClick={handleKeyDismiss}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Continue to API Keys\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_UPDATE}>\n <PageLayout\n title=\"Generate API Key\"\n description=\"Generate a new API key for programmatic access\"\n purpose=\"Create a key your backend or scripts can use to call the API without a logged-in user. Scope it to one app and the minimum permissions it needs — the secret is shown only once at creation, so copy it somewhere safe.\"\n backLink=\"/api-keys\"\n backLabel=\"Back to API Keys\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-3xl space-y-8\">\n {/* Mutation error */}\n {mutationError && (\n <Alert\n variant=\"error\"\n title=\"Error Generating API Key\"\n onDismiss={() => setMutationError(null)}\n >\n {mutationError}\n </Alert>\n )}\n\n {/* Basic Information */}\n <PageSection\n title=\"Basic Information\"\n description=\"Configure the API key settings\"\n >\n <div className=\"space-y-4\">\n {/* App Selector */}\n {appsLoading ? (\n <div className=\"flex items-center gap-2 py-2\">\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n <span className=\"text-sm text-text-secondary\">\n Loading apps...\n </span>\n </div>\n ) : apps.length === 0 ? (\n <Alert variant=\"warning\" title=\"No Apps Found\">\n <p>\n You need to create an OAuth app first before generating API\n keys.{\" \"}\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth/new\")}\n className=\"text-primary underline hover:no-underline\"\n >\n Create an app\n </button>\n </p>\n </Alert>\n ) : (\n <SelectField\n label=\"App\"\n value={formData.appId}\n onChange={(e) => updateField(\"appId\", e.target.value)}\n options={apps.map((app) => ({\n value: app.id,\n label: `${app.name} (${app.clientId})`,\n }))}\n placeholder=\"Select an app…\"\n error={errors.appId}\n required\n hint=\"The OAuth app this API key will be associated with\"\n />\n )}\n\n <InputField\n label=\"Key Name\"\n placeholder=\"e.g., Production Backend Key\"\n value={formData.name}\n onChange={(e) => updateField(\"name\", e.target.value)}\n error={errors.name}\n required\n hint=\"A descriptive name to identify this key\"\n />\n </div>\n </PageSection>\n\n {/* Scopes */}\n <PageSection\n title=\"Scopes\"\n description=\"Define what this API key can access\"\n >\n <div className=\"space-y-4\">\n <TagInput\n label=\"Scopes\"\n value={formData.scopes}\n onChange={(values) => updateField(\"scopes\", values)}\n placeholder=\"e.g., workspace:read, vibecontrols:write\"\n hint=\"Press Enter to add each scope. At least one scope is required.\"\n error={errors.scopes}\n />\n </div>\n </PageSection>\n\n {/* Advanced: rate-limit overrides / allowed origins / scope tuning / expiration — collapse the long-tail knobs */}\n <OptionalFields\n title=\"Advanced settings\"\n description=\"Rate-limit overrides, allowed origins, scope tuning, and expiration.\"\n >\n <PageSection\n title=\"Key Expiration\"\n description=\"Set when this key should expire\"\n >\n <SelectField\n label=\"Expires In\"\n value={formData.expiresIn}\n onChange={(e) => updateField(\"expiresIn\", e.target.value)}\n options={expirationOptions}\n hint=\"Keys can be revoked at any time regardless of expiration\"\n />\n\n {formData.expiresIn === \"never\" && (\n <Alert variant=\"warning\" className=\"mt-4\">\n Non-expiring keys are a security risk. Consider setting an\n expiration and rotating keys periodically.\n </Alert>\n )}\n </PageSection>\n </OptionalFields>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors\"\n >\n <X size={16} />\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || apps.length === 0}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50\"\n >\n <Save size={16} />\n {isSubmitting ? \"Generating...\" : \"Generate API Key\"}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateApiKeyPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { OptionalFields } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, SelectField, TagInput } from \"../components/FormField\";\nimport { NewSecretAlert } from \"../components/SecretDisplay\";\nimport { Alert } from \"../components/ConfirmDialog\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { useGenerateApiKeyMutation, useCurrentUserAppsQuery } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { executeRecaptcha } from \"../utils/recaptcha\";\n\ninterface CreateApiKeyFormData {\n appId: string;\n name: string;\n scopes: string[];\n expiresIn: string;\n}\n\nconst EXPIRATION_DURATIONS: Record<string, number | null> = {\n \"30days\": 30 * 24 * 60 * 60 * 1000,\n \"90days\": 90 * 24 * 60 * 60 * 1000,\n \"180days\": 180 * 24 * 60 * 60 * 1000,\n \"1year\": 365 * 24 * 60 * 60 * 1000,\n \"2years\": 2 * 365 * 24 * 60 * 60 * 1000,\n never: null,\n};\n\nfunction computeExpiresAt(expiresIn: string): string | undefined {\n const duration = EXPIRATION_DURATIONS[expiresIn];\n if (duration == null) return undefined;\n return new Date(Date.now() + duration).toISOString();\n}\n\n/**\n * Page for generating a new API key via auth-svc.\n *\n * Previously created API keys through devportal-svc per application;\n * now generates keys through auth-svc's generateAPIKey mutation.\n * The user selects which OAuth app to associate the key with.\n */\nexport const CreateApiKeyPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { navigate, basePath } = useDevportal();\n const [newApiKey, setNewApiKey] = useState<{\n apiKey: string;\n apiSecret: string;\n keyId: string;\n } | null>(null);\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [mutationError, setMutationError] = useState<string | null>(null);\n\n const [generateApiKey, { loading: isSubmitting }] =\n useGenerateApiKeyMutation();\n const bus = useDevportalEventBus();\n\n // Fetch user's apps to populate the app selector\n const { data: appsData, loading: appsLoading } = useCurrentUserAppsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const apps: Array<{ id: string; name: string; clientId: string }> =\n appsData?.currentUserApps?.apps.map(\n (app: { id: string; name: string; clientId: string }) => ({\n id: app.id,\n name: app.name,\n clientId: app.clientId,\n }),\n ) ?? [];\n\n const [formData, setFormData] = useState<CreateApiKeyFormData>({\n appId: \"\",\n name: \"\",\n scopes: [],\n expiresIn: \"1year\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCancel = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const updateField = <K extends keyof CreateApiKeyFormData>(\n field: K,\n value: CreateApiKeyFormData[K],\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: \"\" }));\n }\n if (mutationError) {\n setMutationError(null);\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.appId) {\n newErrors.appId = tr(\n \"devportal.selectAppError\",\n \"Please select an app to associate this key with\",\n );\n }\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.apiKeyNameRequired\",\n \"API key name is required\",\n );\n }\n\n if (formData.scopes.length === 0) {\n newErrors.scopes = tr(\n \"devportal.atLeastOneScopeRequired\",\n \"At least one scope is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n setMutationError(null);\n\n try {\n const expiresAt = computeExpiresAt(formData.expiresIn);\n const recaptchaToken = await executeRecaptcha(\"api_key_create\");\n const input = {\n appId: formData.appId,\n name: formData.name.trim(),\n scope: formData.scopes,\n expiresAt,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n };\n\n const { data } = await generateApiKey({\n variables: {\n input,\n },\n });\n\n if (data?.generateAPIKey) {\n bus.emit(\"devportal.apikey.generated\", {\n keyId: data.generateAPIKey.keyId,\n appId: formData.appId,\n });\n setNewApiKey({\n apiKey: data.generateAPIKey.apiKey,\n apiSecret: data.generateAPIKey.apiSecret,\n keyId: data.generateAPIKey.keyId,\n });\n }\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.failedToGenerateApiKey\",\n \"Failed to generate API key\",\n );\n setMutationError(message);\n }\n };\n\n const handleKeyDismiss = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const expirationOptions = [\n { value: \"30days\", label: \"30 days\" },\n { value: \"90days\", label: \"90 days\" },\n { value: \"180days\", label: \"180 days\" },\n { value: \"1year\", label: \"1 year\" },\n { value: \"2years\", label: \"2 years\" },\n { value: \"never\", label: \"Never expires\" },\n ];\n\n if (newApiKey) {\n return (\n <PageLayout\n title=\"API Key Generated\"\n description=\"Your new API key has been generated successfully\"\n >\n <div className=\"max-w-2xl space-y-6\">\n <NewSecretAlert\n title=\"API Key & Secret\"\n secret={newApiKey.apiSecret}\n description=\"Store this secret securely — you won't be able to see it again.\"\n onDismiss={handleKeyDismiss}\n />\n\n <Alert variant=\"info\" title=\"Usage Instructions\">\n <div className=\"mt-2 space-y-2\">\n <p>Your API key details:</p>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-text-secondary\">\n Key ID:\n </span>\n <code className=\"rounded bg-bg-sunken px-2 py-0.5 text-sm\">\n {newApiKey.keyId}\n </code>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-text-secondary\">\n API Key:\n </span>\n <code className=\"rounded bg-bg-sunken px-2 py-0.5 text-sm\">\n {newApiKey.apiKey}\n </code>\n </div>\n </div>\n <p className=\"text-xs text-text-secondary mt-2\">\n Use the API key and secret to authenticate your requests.\n </p>\n </div>\n </Alert>\n\n <div className=\"flex gap-3\">\n <button\n onClick={handleKeyDismiss}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n Continue to API Keys\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title=\"Generate API Key\"\n description=\"Generate a new API key for programmatic access\"\n purpose=\"Create a key your backend or scripts can use to call the API without a logged-in user. Scope it to one app and the minimum permissions it needs — the secret is shown only once at creation, so copy it somewhere safe.\"\n backLink=\"/api-keys\"\n backLabel=\"Back to API Keys\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-3xl space-y-8\">\n {/* Mutation error */}\n {mutationError && (\n <Alert\n variant=\"error\"\n title=\"Error Generating API Key\"\n onDismiss={() => setMutationError(null)}\n >\n {mutationError}\n </Alert>\n )}\n\n {/* Basic Information */}\n <PageSection\n title=\"Basic Information\"\n description=\"Configure the API key settings\"\n >\n <div className=\"space-y-4\">\n {/* App Selector */}\n {appsLoading ? (\n <div className=\"flex items-center gap-2 py-2\">\n <Loader2 className=\"h-4 w-4 animate-spin text-text-secondary\" />\n <span className=\"text-sm text-text-secondary\">\n Loading apps...\n </span>\n </div>\n ) : apps.length === 0 ? (\n <Alert variant=\"warning\" title=\"No Apps Found\">\n <p>\n You need to create an OAuth app first before generating API\n keys.{\" \"}\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth/new\")}\n className=\"text-primary underline hover:no-underline\"\n >\n Create an app\n </button>\n </p>\n </Alert>\n ) : (\n <SelectField\n label=\"App\"\n value={formData.appId}\n onChange={(e) => updateField(\"appId\", e.target.value)}\n options={apps.map((app) => ({\n value: app.id,\n label: `${app.name} (${app.clientId})`,\n }))}\n placeholder=\"Select an app…\"\n error={errors.appId}\n required\n hint=\"The OAuth app this API key will be associated with\"\n />\n )}\n\n <InputField\n label=\"Key Name\"\n placeholder=\"e.g., Production Backend Key\"\n value={formData.name}\n onChange={(e) => updateField(\"name\", e.target.value)}\n error={errors.name}\n required\n hint=\"A descriptive name to identify this key\"\n />\n </div>\n </PageSection>\n\n {/* Scopes */}\n <PageSection\n title=\"Scopes\"\n description=\"Define what this API key can access\"\n >\n <div className=\"space-y-4\">\n <TagInput\n label=\"Scopes\"\n value={formData.scopes}\n onChange={(values) => updateField(\"scopes\", values)}\n placeholder=\"e.g., workspace:read, vibecontrols:write\"\n hint=\"Press Enter to add each scope. At least one scope is required.\"\n error={errors.scopes}\n />\n </div>\n </PageSection>\n\n {/* Advanced: rate-limit overrides / allowed origins / scope tuning / expiration — collapse the long-tail knobs */}\n <OptionalFields\n title=\"Advanced settings\"\n description=\"Rate-limit overrides, allowed origins, scope tuning, and expiration.\"\n >\n <PageSection\n title=\"Key Expiration\"\n description=\"Set when this key should expire\"\n >\n <SelectField\n label=\"Expires In\"\n value={formData.expiresIn}\n onChange={(e) => updateField(\"expiresIn\", e.target.value)}\n options={expirationOptions}\n hint=\"Keys can be revoked at any time regardless of expiration\"\n />\n\n {formData.expiresIn === \"never\" && (\n <Alert variant=\"warning\" className=\"mt-4\">\n Non-expiring keys are a security risk. Consider setting an\n expiration and rotating keys periodically.\n </Alert>\n )}\n </PageSection>\n </OptionalFields>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors\"\n >\n <X size={16} />\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || apps.length === 0}\n className=\"inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90 transition-colors disabled:opacity-50\"\n >\n <Save size={16} />\n {isSubmitting ? \"Generating...\" : \"Generate API Key\"}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CreateApplicationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreateApplicationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA0ThC,eAAO,MAAM,qBAAqB,EAAE,EAmmBnC,CAAC"}
1
+ {"version":3,"file":"CreateApplicationPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/CreateApplicationPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAyThC,eAAO,MAAM,qBAAqB,EAAE,EAsmBnC,CAAC"}