PageSourceSearch

https://founder-spark-63.lovable.app/assets/AIHealth-DdPgXebQ.js

js founder-spark-63.lovable.app collected 2026-10-03 03:32:14 UTC 9,577 bytes, 1 lines download raw bytes

1import{j as e}from"./query-_koPiPsI.js";import{f as T,r as c}from"./react-vendor-h1rQ_Bae.js";import{f as B,s as m,L as I,C as a,o as r,p as l,n,B as H,R as D,G as O,H as P,k as h,E as L,g as j}from"./index-Dq2zLArw.js";import{f as S}from"./format-Bq4KMpNp.js";import"./ui-radix-CKwvnS8F.js";import"./supabase-DL6nu9mS.js";import"./charts-Xu79y1bN.js";import"./motion-nVmI7L5T.js";const Y=()=>{const{user:p,loading:g}=B(),v=T(),[u,q]=c.useState(null),[d,E]=c.useState([]),[f,y]=c.useState([]),[G,b]=c.useState(!0),[w,_]=c.useState(!1);c.useEffect(()=>{if(!g){if(!p){v("/auth");return}(async()=>{const{data:s}=await m.from("user_roles").select("role").eq("user_id",p.id).eq("role","admin").maybeSingle();q(!!s)})()}},[p,g,v]);const k=async()=>{b(!0);const s=new Date(Date.now()-24*60*60*1e3).toISOString(),[t,o]=await Promise.all([m.from("ai_usage_log").select("*").gte("created_at",s).order("created_at",{ascending:!1}).limit(1e3),m.from("admin_alerts").select("*").in("alert_type",["ai_lovable_credits_exhausted","ai_google_rate_limited","ai_unavailable"]).order("created_at",{ascending:!1}).limit(20)]);t.error?j.error("Couldn't load AI usage log"):E(t.data||[]),o.error||y(o.data||[]),b(!1)},M=async s=>{const{error:t}=await m.from("admin_alerts").update({resolved:!0,resolved_at:new Date().toISOString()}).eq("id",s);if(t){j.error("Couldn't resolve alert");return}y(o=>o.map(x=>x.id===s?{...x,resolved:!0}:x))};c.useEffect(()=>{u&&k()},[u]);const $=async()=>{_(!0);try{const{data:s,error:t}=await m.functions.invoke("ai-health-check");if(t)throw t;j.success("Health check complete",{description:`${s?.total_requests??0} requests in last hour, ${(s?.error_rate??0)*100|0}% errors.`}),await k()}catch{j.error("Health check failed")}finally{_(!1)}};if(g||u===null)return e.jsx("div",{className:"min-h-screen flex items-center justify-center",children:e.jsx(I,{className:"h-6 w-6 animate-spin"})});if(!u)return e.jsx("div",{className:"min-h-screen flex items-center justify-center p-8",children:e.jsxs(a,{className:"max-w-md",children:[e.jsx(r,{children:e.jsx(l,{children:"Admins only"})}),e.jsx(n,{children:e.jsx("p",{className:"text-muted-foreground",children:"This dashboard is only available to workspace admins."})})]})});const N=d.length,F=d.filter(s=>s.status!=="ok").length,C=N>0?F/N*100:0,A=d.reduce((s,t)=>(s[t.provider]=(s[t.provider]||0)+1,s),{}),i={};for(const s of d){const t=s.function_name;i[t]||(i[t]={ok:0,fail:0,latency:[]}),s.status==="ok"?i[t].ok+=1:i[t].fail+=1,s.latency_ms&&i[t].latency.push(s.latency_ms)}const R=Object.entries(i).map(([s,t])=>({fn:s,total:t.ok+t.fail,fail:t.fail,errorRate:t.ok+t.fail>0?t.fail/(t.ok+t.fail)*100:0,p95:t.latency.length?t.latency.sort((o,x)=>o-x)[Math.floor(t.latency.length*.95)]??0:0})).sort((s,t)=>t.total-s.total);return e.jsx("div",{className:"min-h-screen bg-background p-4 md:p-8",children:e.jsxs("div",{className:"max-w-6xl mx-auto space-y-6",children:[e.jsxs("div",{className:"flex items-center justify-between gap-4",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl md:text-3xl font-bold",children:"AI Health"}),e.jsx("p",{className:"text-sm text-muted-foreground mt-1",children:"Last 24 hours of AI calls. Provider mix, failure rate, and latency."})]}),e.jsxs(H,{onClick:$,disabled:w,variant:"outline",children:[e.jsx(D,{className:`h-4 w-4 mr-2 ${w?"animate-spin":""}`}),"Run health check"]})]}),e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3",children:[e.jsxs(a,{children:[e.jsx(r,{className:"pb-2",children:e.jsx(l,{className:"text-xs text-muted-foreground font-normal",children:"Requests (24h)"})}),e.jsx(n,{className:"text-2xl font-bold",children:N})]}),e.jsxs(a,{children:[e.jsx(r,{className:"pb-2",children:e.jsx(l,{className:"text-xs text-muted-foreground font-normal",children:"Error rate"})}),e.jsxs(n,{className:"flex items-center gap-2",children:[e.jsxs("span",{className:"text-2xl font-bold",children:[C.toFixed(1),"%"]}),C>10?e.jsx(O,{className:"h-5 w-5 text-destructive"}):e.jsx(P,{className:"h-5 w-5 text-foreground/40"})]})]}),e.jsxs(a,{children:[e.jsx(r,{className:"pb-2",children:e.jsx(l,{className:"text-xs text-muted-foreground font-normal",children:"Google direct"})}),e.jsx(n,{className:"text-2xl font-bold",children:A.google||0})]}),e.jsxs(a,{children:[e.jsx(r,{className:"pb-2",children:e.jsx(l,{className:"text-xs text-muted-foreground font-normal",children:"Lovable fallback"})}),e.jsx(n,{className:"text-2xl font-bold",children:A.lovable||0})]})]}),e.jsxs(a,{children:[e.jsxs(r,{className:"flex flex-row items-center justify-between gap-2 space-y-0",children:[e.jsxs("div",{children:[e.jsx(l,{className:"text-base",children:"AI quota alerts"}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:'Users never see "credits exhausted." These alerts are admin-only.'})]}),f.filter(s=>!s.resolved).length>0&&e.jsxs(h,{variant:"destructive",children:[f.filter(s=>!s.resolved).length," active"]})]}),e.jsx(n,{className:"space-y-2",children:f.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No quota alerts. Gemini and the Lovable fallback are healthy."}):f.map(s=>e.jsx("div",{className:`border rounded-lg p-3 text-sm ${s.resolved?"opacity-50":""}`,children:e.jsxs("div",{className:"flex items-start justify-between gap-3",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2 flex-wrap",children:[e.jsx(h,{variant:s.severity==="high"?"destructive":"secondary",className:"text-[10px]",children:s.severity}),e.jsx("span",{className:"font-medium",children:s.title})]}),e.jsx("p",{className:"text-xs text-muted-foreground mt-1",children:s.message}),e.jsxs("p",{className:"text-[10px] text-muted-foreground mt-1",children:[S(new Date(s.created_at),"MMM d HH:mm")," ·"," ",e.jsx("span",{className:"font-mono",children:s.alert_type})]})]}),!s.resolved&&e.jsx(H,{size:"sm",variant:"outline",onClick:()=>M(s.id),children:"Resolve"})]})},s.id))})]}),e.jsxs(a,{children:[e.jsx(r,{children:e.jsx(l,{className:"text-base",children:"Where AI cost actually goe
1s"})}),e.jsxs(n,{className:"grid md:grid-cols-2 gap-3 text-sm",children:[e.jsxs("a",{href:"https://console.cloud.google.com/billing",target:"_blank",rel:"noopener noreferrer",className:"border rounded-lg p-3 hover:bg-muted/50 transition-colors flex items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"font-medium",children:"Google Cloud Billing"}),e.jsxs("div",{className:"text-xs text-muted-foreground mt-0.5",children:["Filter: ",e.jsx("span",{className:"font-mono",children:"Generative Language API"}),". This is where your $428 Free Trial drains from."]})]}),e.jsx(L,{className:"h-4 w-4 shrink-0"})]}),e.jsxs("a",{href:"https://lovable.dev/settings/workspace/usage",target:"_blank",rel:"noopener noreferrer",className:"border rounded-lg p-3 hover:bg-muted/50 transition-colors flex items-center justify-between gap-2",children:[e.jsxs("div",{children:[e.jsx("div",{className:"font-medium",children:"Lovable Workspace Usage"}),e.jsx("div",{className:"text-xs text-muted-foreground mt-0.5",children:"AI Gateway credits. Only used when Google rate-limits us."})]}),e.jsx(L,{className:"h-4 w-4 shrink-0"})]})]})]}),e.jsxs(a,{children:[e.jsx(r,{children:e.jsx(l,{className:"text-base",children:"By function (last 24h)"})}),e.jsx(n,{children:G?e.jsx(I,{className:"h-5 w-5 animate-spin"}):R.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No AI calls in the last 24 hours."}):e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"text-left text-xs text-muted-foreground border-b",children:[e.jsx("th",{className:"py-2 pr-3",children:"Function"}),e.jsx("th",{className:"py-2 pr-3 text-right",children:"Calls"}),e.jsx("th",{className:"py-2 pr-3 text-right",children:"Failures"}),e.jsx("th",{className:"py-2 pr-3 text-right",children:"Error %"}),e.jsx("th",{className:"py-2 text-right",children:"p95 latency"})]})}),e.jsx("tbody",{children:R.map(s=>e.jsxs("tr",{className:"border-b last:border-0",children:[e.jsx("td",{className:"py-2 pr-3 font-mono text-xs",children:s.fn}),e.jsx("td",{className:"py-2 pr-3 text-right",children:s.total}),e.jsx("td",{className:"py-2 pr-3 text-right",children:s.fail}),e.jsx("td",{className:"py-2 pr-3 text-right",children:s.errorRate>10?e.jsxs(h,{variant:"destructive",children:[s.errorRate.toFixed(0),"%"]}):e.jsxs("span",{children:[s.errorRate.toFixed(0),"%"]})}),e.jsx("td",{className:"py-2 text-right",children:s.p95?`${s.p95}ms`:"—"})]},s.fn))})]})})})]}),e.jsxs(a,{children:[e.jsx(r,{children:e.jsx(l,{className:"text-base",children:"Recent failures"})}),e.jsx(n,{children:(()=>{const s=d.filter(t=>t.status!=="ok").slice(0,50);return s.length===0?e.jsx("p",{className:"text-sm text-muted-foreground",children:"No failures in the last 24 hours."}):e.jsx("div",{className:"space-y-2",children:s.map(t=>e.jsxs("div",{className:"flex items-center justify-between gap-2 text-xs border rounded-lg px-3 py-2",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx(h,{variant:"outline",className:"font-mono text-[10px]",children:t.function_name}),e.jsx("span",{className:"text-muted-foreground",children:t.provider}),e.jsxs(h,{variant:"secondary",className:"text-[10px]",children:[t.status," ",t.status_code?`(${t.status_code})`:""]})]}),e.jsx("span",{className:"text-muted-foreground shrink-0",children:S(new Date(t.created_at),"MMM d HH:mm")})]},t.id))})})()})]})]})})};export{Y 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.