1import{r as s,j as e,B as u,ae as re,af as ne}from"./index-ByW9mAWg.js";import{M as se}from"./HamburgerWrapped-B41jo4nX.js";import{W as ie}from"./WorkspaceHeader-Bapxmg7X.js";import{B as oe}from"./Badge-CVpXyPFr.js";import{A as k}from"./Alert-DN4OPQky.js";import{S as ce}from"./Spinner-BYNs4OC5.js";import{C as N}from"./Card-B0yIbu5Y.js";import{F as i}from"./Form-OMB71QM4.js";import"./Col-CwLwia1S.js";const le="/api/external-api/keys";function J(a){if(!a||typeof a.id!="string"||typeof a.name!="string"||typeof a.prefix!="string"||typeof a.createdUtc!="string")throw new Error("The API key service returned an invalid response. Try refreshing the page.");return{id:a.id,name:a.name,prefix:a.prefix,createdUtc:a.createdUtc,expiresUtc:a.expiresUtc??null}}function G(a){const l=J(a);if(typeof a.key!="string"||!a.key)throw new Error("The key was created, but its one-time secret was not returned. Check your key list before trying again.");return{metadata:l,secret:a.key}}function F(a){return a?a.slice(0,10):"No expiration"}function V(a,l){return a instanceof SyntaxError?"The API key service returned an invalid response. Check your key list before retrying.":a instanceof Error?a.message:l}function de(a){if(!a)return null;const l=/^(\d{4})-(\d{2})-(\d{2})$/.exec(a);if(!l)return;const[,m,x,p]=l.map(Number),o=new Date(Date.UTC(m,x-1,p,23,59,59,999));if(!(o.getUTCFullYear()!==m||o.getUTCMonth()!==x-1||o.getUTCDate()!==p||o.getTime()<=Date.now()))return o.toISOString()}async function b(a,l,m,x="API key not found. Refresh your list and try again.",p="You do not have permission for this action. Check your account role and AiMax API access."){const o=await re(ne()+le+a,l);if(o.ok)return o;throw o.status===401?new Error("Your session has expired. Sign in again to manage API keys."):o.status===403?new Error(p):o.status===404?new Error(x):o.status===400?new Error("The request was not accepted. Check the key name and expiration date."):new Error(m)}function me(){const[a,l]=s.useState([]),[m,x]=s.useState(null),[p,o]=s.useState(!0),[U,P]=s.useState(""),[E,y]=s.useState(""),[L,f]=s.useState(""),[v,g]=s.useState(null),[R,j]=s.useState(""),[M,D]=s.useState(!1),[z,O]=s.useState(""),[Y,q]=s.useState(""),[W,w]=s.useState(null),[K,$]=s.useState(""),[T,Z]=s.useState(""),[I,H]=s.useState("name"),c=p||!!U,S=s.useCallback(async t=>{o(!0),y("");try{const r=await(await b("",{signal:t},"Unable to load API keys. Try again.","API key management is unavailable. Try again later.")).json();if(typeof r.enabled!="boolean"||!Array.isArray(r.keys))throw new Error("The API key service returned an invalid response. Try refreshing the page.");t?.aborted||(x(r.enabled),l(r.keys.map(J)))}catch(n){t?.aborted||y(V(n,"Unable to load API keys. Try again."))}finally{t?.aborted||o(!1)}},[]);s.useEffect(()=>{const t=new AbortController;S(t.signal);const n=()=>g(null);return window.addEventListener("pagehide",n),()=>{t.abort(),window.removeEventListener("pagehide",n)}},[S]);const B=s.useMemo(()=>{const t=T.trim().toLocaleLowerCase();return a.filter(r=>r.name.toLocaleLowerCase().includes(t)).sort((r,d)=>{switch(I){case"name-desc":return d.name.localeCompare(r.name);case"created-newest":return d.createdUtc.localeCompare(r.createdUtc);case"created-oldest":return r.createdUtc.localeCompare(d.createdUtc);case"expires-soonest":return(r.expiresUtc===null)-(d.expiresUtc===null)||(r.expiresUtc||"").localeCompare(d.expiresUtc||"");case"expires-latest":return(r.expiresUtc===null)-(d.expiresUtc===null)||(d.expiresUtc||"").localeCompare(r.expiresUtc||"");default:return r.name.localeCompare(d.name)}})},[a,T,I]);async function C(t,n){P(t),y(""),f("");try{await n()}catch(r){y(V(r,"The request failed. Try again."))}finally{P("")}}async function Q(t){if(t.preventDefault(),c||!m)return;const n=z.trim();if(!n||n.length>80){y("Enter a key name of 80 characters or fewer.");return}const r=de(Y);if(r===void 0){y("Expiration must be a future date in YYYY-MM-DD format.");return}await C("create",async()=>{const d=await b("",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:n,expiresUtc:r})},"Unable to create the API key. Try again.",void 0,"An AiMax subscription and active API access are required to create keys. Check your account permissions."),{metadata:h,secret:A}
1=G(await d.json());l(ae=>[h,...ae]),g({value:A,id:h.id}),j(""),f("API key created. Copy the secret now; it cannot be retrieved later."),O(""),q(""),D(!1)})}async function X(t,n){if(t.preventDefault(),c)return;const r=K.trim();if(!r||r.length>80){y("Enter a key name of 80 characters or fewer.");return}if(r===n.name){w(null);return}await C("rename",async()=>{await b(`/${encodeURIComponent(n.id)}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:r})},"Unable to rename the API key. Try again."),l(d=>d.map(h=>h.id===n.id?{...h,name:r}:h)),w(null),f("API key renamed.")})}async function _(t){c||!m||!window.confirm(`Rotate "${t.name}"? The current key will stop working immediately. The new secret is shown only once.`)||await C("rotate",async()=>{const n=await b(`/${encodeURIComponent(t.id)}/rotate`,{method:"POST"},"Unable to rotate the API key. Try again.","API key not found or AiMax API access is no longer active. Refresh your list and try again."),{metadata:r,secret:d}=G(await n.json());l(h=>h.map(A=>A.id===t.id?r:A)),g({value:d,id:r.id}),j(""),f("The old key was revoked. Copy the replacement secret now; it cannot be retrieved later.")})}async function ee(t){c||!window.confirm(`Revoke "${t.name}"? Applications using it will stop working immediately.`)||await C("revoke",async()=>{await b(`/${encodeURIComponent(t.id)}`,{method:"DELETE"},"Unable to revoke the API key. Try again."),l(n=>n.filter(r=>r.id!==t.id)),g(n=>n?.id===t.id?null:n),w(null),f("API key revoked.")})}async function te(){if(v)try{await navigator.clipboard.writeText(v.value),j("Copied. The secret remains on your clipboard until replaced; paste it only into a secure location.")}catch{j("Could not copy automatically. Select and copy the secret above instead.")}}return e.jsxs("main",{className:"api-keys-page workspace-page",children:[e.jsx(ie,{eyebrow:"INTEGRATIONS",title:"API keys",accent:"api-keys",description:"Manage keys for your OpenAI-compatible integrations. These keys belong to your account.",actions:m&&e.jsx(oe,{bg:"success",className:"p-2",children:"AiMax API enabled"})}),e.jsx(k,{variant:"info",children:"Keep secrets private. A full API key is shown only when created or rotated, never in the key list."}),E&&e.jsx(k,{variant:"danger",role:"alert",children:E}),L&&e.jsx(k,{variant:"success",role:"status",children:L}),v&&e.jsxs(k,{variant:"warning",className:"api-keys-secret",role:"status",children:[e.jsxs("div",{className:"d-flex justify-content-between align-items-center gap-2",children:[e.jsx("strong",{children:"One-time secret"}),e.jsx(u,{variant:"outline-dark",size:"sm",onClick:()=>{g(null),j("")},children:"Dismiss secret"})]}),e.jsx("p",{className:"mt-2 mb-2",children:"Copy and store this secret securely now. It cannot be retrieved later. Avoid sharing it or leaving it in your clipboard."}),e.jsx("code",{className:"api-keys-secret-value","aria-label":"One-time API key secret",children:v.value}),e.jsx(u,{variant:"dark",size:"sm",className:"mt-2",onClick:te,children:"Copy secret"}),R&&e.jsx("p",{className:"mb-0 mt-2",role:"status",children:R})]}),p&&e.jsxs("div",{role:"status",className:"mb-3",children:[e.jsx(ce,{animation:"border",size:"sm",className:"me-2"}),"Loading API keys..."]}),m===!1&&e.jsx(k,{variant:"warning",children:"AiMax API access is not enabled for this account. An AiMax subscription is required to create or rotate keys. You can still rename or revoke existing keys."}),m!==null&&e.jsxs(e.Fragment,{children:[m&&e.jsx(N,{className:"mb-4",children:e.jsxs(N.Body,{children:[e.jsxs("div",{className:"d-flex flex-wrap justify-content-between align-items-center gap-2",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"h5",children:"Create an integration key"}),e.jsx("p",{className:"text-muted mb-0",children:"Choose a name and, optionally, a UTC expiration date."})]}),e.jsx(u,{variant:"success",disabled:c,onClick:()=>D(t=>!t),children:M?"Cancel":"New API key"})]}),M&&e.jsxs(i,{onSubmit:Q,className:"mt-3",children:[e.jsxs(i.Group,{className:"mb-3",controlId:"api-key-name",children:[e.jsx(i.Label,{children:"Key name"}),e.jsx(i.Control,{value:z,onChange:t=>O(t.target.value),maxLength:80,required:!0,placeholder:"e.g. Production app",disabled:c})]}),e.jsxs(i.Group,{className:"mb-3",controlId:"api-key-expiration",children:[e.jsx(i.Label,{children:"Expiration date (optional, UTC)"}),e.jsx(i.Control,{type:"date",value:Y,onChange:t=>q(t.target.value),disabled:c}),e.jsx(i.Text,{children:"Valid through 23:59:59 UTC on the selected date. Leave blank for no expiration."})]}),e.jsx("p",{className:"small",children:"The secret is displayed only once. Store it securely."}
1),e.jsx(u,{type:"submit",variant:"success",disabled:c,children:U==="create"?"Creating...":"Create API key"})]})]})}),e.jsxs("section",{className:"workspace-panel","aria-labelledby":"api-keys-list-title",children:[e.jsxs("div",{className:"d-flex flex-wrap justify-content-between align-items-center gap-2 mb-2",children:[e.jsxs("div",{children:[e.jsx("h2",{className:"h5 mb-1",id:"api-keys-list-title",children:"Your keys"}),e.jsxs("p",{className:"text-muted mb-0",children:[a.length," listed · ",a.filter(t=>!t.expiresUtc).length," without expiration"]})]}),e.jsx(u,{variant:"outline-primary",size:"sm",disabled:c,onClick:()=>S(),children:"Refresh list"})]}),e.jsx("p",{className:"text-muted small",children:"Rotate regularly and revoke keys you no longer recognize."}),e.jsxs("div",{className:"d-flex flex-wrap gap-2 mb-3",children:[e.jsxs(i.Group,{className:"flex-grow-1",controlId:"api-keys-search",children:[e.jsx(i.Label,{children:"Search by key name"}),e.jsx(i.Control,{value:T,onChange:t=>Z(t.target.value),placeholder:"Search keys"})]}),e.jsxs(i.Group,{controlId:"api-keys-sort",children:[e.jsx(i.Label,{children:"Sort keys"}),e.jsxs(i.Select,{value:I,onChange:t=>H(t.target.value),children:[e.jsx("option",{value:"name",children:"Name (A-Z)"}),e.jsx("option",{value:"name-desc",children:"Name (Z-A)"}),e.jsx("option",{value:"created-newest",children:"Created (newest)"}),e.jsx("option",{value:"created-oldest",children:"Created (oldest)"}),e.jsx("option",{value:"expires-soonest",children:"Expiration (soonest)"}),e.jsx("option",{value:"expires-latest",children:"Expiration (latest)"})]})]})]}),!p&&B.length===0&&e.jsx("p",{children:a.length?"No matching API keys. Try a different name.":"No API keys yet."}),e.jsx("div",{className:"api-keys-grid",children:B.map(t=>e.jsx(N,{children:e.jsxs(N.Body,{children:[e.jsx("h3",{className:"h6 api-keys-name",children:t.name}),e.jsxs("dl",{className:"api-keys-details",children:[e.jsx("dt",{children:"Prefix"}),e.jsx("dd",{children:e.jsx("code",{children:t.prefix})}),e.jsx("dt",{children:"Created (UTC)"}),e.jsx("dd",{children:F(t.createdUtc)}),e.jsx("dt",{children:"Expires (UTC)"}),e.jsx("dd",{children:F(t.expiresUtc)})]}),W===t.id?e.jsxs(i,{onSubmit:n=>X(n,t),children:[e.jsxs(i.Group,{controlId:`rename-${t.id}`,className:"mb-2",children:[e.jsx(i.Label,{children:"New name"}),e.jsx(i.Control,{value:K,onChange:n=>$(n.target.value),maxLength:80,required:!0,disabled:c})]}),e.jsxs("div",{className:"d-flex flex-wrap gap-2",children:[e.jsx(u,{size:"sm",type:"submit",disabled:c,children:"Save name"}),e.jsx(u,{size:"sm",variant:"outline-secondary",disabled:c,onClick:()=>w(null),children:"Cancel"})]})]}):e.jsxs("div",{className:"d-flex flex-wrap gap-2",children:[e.jsx(u,{size:"sm",variant:"outline-primary",disabled:c,onClick:()=>{w(t.id),$(t.name)},children:"Rename"}),e.jsx(u,{size:"sm",variant:"outline-warning",disabled:c||!m,onClick:()=>_(t),children:"Rotate"}),e.jsx(u,{size:"sm",variant:"outline-danger",disabled:c,onClick:()=>ee(t),children:"Revoke"})]})]})},t.id))})]})]})]})}function ke(){return e.jsx(se,{componentToRender:me})}export{ke as default};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.