tinbase 0.8.0 → 0.9.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/README.md +2 -2
- package/assets/logo.svg +9 -13
- package/dist/admin/ui.d.ts +1 -1
- package/dist/admin/ui.js +1 -1
- package/dist/auth/handler.d.ts +25 -0
- package/dist/auth/handler.js +127 -8
- package/dist/auth/handler.js.map +1 -1
- package/dist/auth/oauth.d.ts +1 -0
- package/dist/auth/oauth.js +28 -9
- package/dist/auth/oauth.js.map +1 -1
- package/dist/cron/service.js +2 -1
- package/dist/cron/service.js.map +1 -1
- package/dist/db/bootstrap.d.ts +2 -2
- package/dist/db/bootstrap.js +19 -0
- package/dist/db/bootstrap.js.map +1 -1
- package/dist/db/database.d.ts +3 -1
- package/dist/db/database.js +6 -1
- package/dist/db/database.js.map +1 -1
- package/dist/db/emulated.d.ts +3 -3
- package/dist/db/emulated.js +46 -13
- package/dist/db/emulated.js.map +1 -1
- package/dist/functions/deno-shim.d.ts +3 -2
- package/dist/functions/deno-shim.js +12 -4
- package/dist/functions/deno-shim.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -4
- package/dist/index.js.map +1 -1
- package/dist/jwt.js +5 -0
- package/dist/jwt.js.map +1 -1
- package/dist/net/service.d.ts +9 -0
- package/dist/net/service.js +97 -10
- package/dist/net/service.js.map +1 -1
- package/dist/node/ws.d.ts +9 -0
- package/dist/node/ws.js +8 -2
- package/dist/node/ws.js.map +1 -1
- package/dist/realtime/engine.js +19 -1
- package/dist/realtime/engine.js.map +1 -1
- package/dist/retention/service.d.ts +37 -0
- package/dist/retention/service.js +66 -0
- package/dist/retention/service.js.map +1 -0
- package/dist/storage/handler.js +48 -21
- package/dist/storage/handler.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/dist/admin/ui.js
CHANGED
|
@@ -61,7 +61,7 @@ Error generating stack: \`+e.message+\`
|
|
|
61
61
|
\`},On=function(){var e=parseInt(document.body.getAttribute(\`data-scroll-locked\`)||\`0\`,10);return isFinite(e)?e:0},kn=function(){_.useEffect(function(){return document.body.setAttribute(En,(On()+1).toString()),function(){var e=On()-1;e<=0?document.body.removeAttribute(En):document.body.setAttribute(En,e.toString())}},[])},An=function(e){var t=e.noRelative,n=e.noImportant,r=e.gapMode,i=r===void 0?\`margin\`:r;kn();var a=_.useMemo(function(){return wn(i)},[i]);return _.createElement(Tn,{styles:Dn(a,!t,i,n?\`\`:\`!important\`)})},jn=!1;if(typeof window<\`u\`)try{var Mn=Object.defineProperty({},"passive",{get:function(){return jn=!0,!0}});window.addEventListener(\`test\`,Mn,Mn),window.removeEventListener(\`test\`,Mn,Mn)}catch{jn=!1}var Nn=jn?{passive:!1}:!1,Pn=function(e){return e.tagName===\`TEXTAREA\`},Fn=function(e,t){if(!(e instanceof Element))return!1;var n=window.getComputedStyle(e);return n[t]!==\`hidden\`&&!(n.overflowY===n.overflowX&&!Pn(e)&&n[t]===\`visible\`)},In=function(e){return Fn(e,\`overflowY\`)},Ln=function(e){return Fn(e,\`overflowX\`)},Rn=function(e,t){var n=t.ownerDocument,r=t;do{if(typeof ShadowRoot<\`u\`&&r instanceof ShadowRoot&&(r=r.host),Vn(e,r)){var i=Hn(e,r);if(i[1]>i[2])return!0}r=r.parentNode}while(r&&r!==n.body);return!1},zn=function(e){return[e.scrollTop,e.scrollHeight,e.clientHeight]},Bn=function(e){return[e.scrollLeft,e.scrollWidth,e.clientWidth]},Vn=function(e,t){return e===\`v\`?In(t):Ln(t)},Hn=function(e,t){return e===\`v\`?zn(t):Bn(t)},Un=function(e,t){return e===\`h\`&&t===\`rtl\`?-1:1},Wn=function(e,t,n,r,i){var a=Un(e,window.getComputedStyle(t).direction),o=a*r,s=n.target,c=t.contains(s),l=!1,u=o>0,d=0,f=0;do{if(!s)break;var p=Hn(e,s),m=p[0],h=p[1]-p[2]-a*m;(m||h)&&Vn(e,s)&&(d+=h,f+=m);var g=s.parentNode;s=g&&g.nodeType===Node.DOCUMENT_FRAGMENT_NODE?g.host:g}while(!c&&s!==document.body||c&&(t.contains(s)||t===s));return(u&&(i&&Math.abs(d)<1||!i&&o>d)||!u&&(i&&Math.abs(f)<1||!i&&-o>f))&&(l=!0),l},Gn=function(e){return\`changedTouches\`in e?[e.changedTouches[0].clientX,e.changedTouches[0].clientY]:[0,0]},Kn=function(e){return[e.deltaX,e.deltaY]},qn=function(e){return e&&\`current\`in e?e.current:e},Jn=function(e,t){return e[0]===t[0]&&e[1]===t[1]},Yn=function(e){return\`
|
|
62
62
|
.block-interactivity-\${e} {pointer-events: none;}
|
|
63
63
|
.allow-interactivity-\${e} {pointer-events: all;}
|
|
64
|
-
\`},Xn=0,Zn=[];function Qn(e){var t=_.useRef([]),n=_.useRef([0,0]),r=_.useRef(),i=_.useState(Xn++)[0],a=_.useState(bn)[0],o=_.useRef(e);_.useEffect(function(){o.current=e},[e]),_.useEffect(function(){if(e.inert){document.body.classList.add(\`block-interactivity-\${i}\`);var t=Jt([e.lockRef.current],(e.shards||[]).map(qn),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(\`allow-interactivity-\${i}\`)}),function(){document.body.classList.remove(\`block-interactivity-\${i}\`),t.forEach(function(e){return e.classList.remove(\`allow-interactivity-\${i}\`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=_.useCallback(function(e,t){if(\`touches\`in e&&e.touches.length===2||e.type===\`wheel\`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Gn(e),a=n.current,s=\`deltaX\`in e?e.deltaX:a[0]-i[0],c=\`deltaY\`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?\`h\`:\`v\`;if(\`touches\`in e&&d===\`h\`&&u.type===\`range\`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=Rn(d,u);if(!m)return!0;if(m?l=d:(l=d===\`v\`?\`h\`:\`v\`,m=Rn(d,u)),!m)return!1;if(!r.current&&\`changedTouches\`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Wn(h,t,e,h===\`h\`?s:c,!0)},[]),c=_.useCallback(function(e){var n=e;if(!(!Zn.length||Zn[Zn.length-1]!==a)){var r=\`deltaY\`in n?Kn(n):Gn(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Jn(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(qn).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=_.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:$n(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=_.useCallback(function(e){n.current=Gn(e),r.current=void 0},[]),d=_.useCallback(function(t){l(t.type,Kn(t),t.target,s(t,e.lockRef.current))},[]),f=_.useCallback(function(t){l(t.type,Gn(t),t.target,s(t,e.lockRef.current))},[]);_.useEffect(function(){return Zn.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(\`wheel\`,c,Nn),document.addEventListener(\`touchmove\`,c,Nn),document.addEventListener(\`touchstart\`,u,Nn),function(){Zn=Zn.filter(function(e){return e!==a}),document.removeEventListener(\`wheel\`,c,Nn),document.removeEventListener(\`touchmove\`,c,Nn),document.removeEventListener(\`touchstart\`,u,Nn)}},[]);var p=e.removeScrollBar,m=e.inert;return _.createElement(_.Fragment,null,m?_.createElement(a,{styles:Yn(i)}):null,p?_.createElement(An,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function $n(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var er=ln(un,Qn),tr=_.forwardRef(function(e,t){return _.createElement(fn,Kt({},e,{ref:t,sideCar:er}))});tr.classNames=fn.classNames;var nr=function(e){return typeof document>\`u\`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},rr=new WeakMap,ir=new WeakMap,ar={},or=0,sr=function(e){return e&&(e.host||sr(e.parentNode))},cr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=sr(t);return n&&e.contains(n)?n:(console.error(\`aria-hidden\`,t,\`in not contained inside\`,e,\`. Doing nothing\`),null)}).filter(function(e){return!!e})},lr=function(e,t,n,r){var i=cr(t,Array.isArray(e)?e:[e]);ar[n]||(ar[n]=new WeakMap);var a=ar[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==\`false\`,c=(rr.get(e)||0)+1,l=(a.get(e)||0)+1;rr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&ir.set(e,!0),l===1&&e.setAttribute(n,\`true\`),i||e.setAttribute(r,\`true\`)}catch(t){console.error(\`aria-hidden: cannot operate on \`,e,t)}})};return u(t),s.clear(),or++,function(){o.forEach(function(e){var t=rr.get(e)-1,i=a.get(e)-1;rr.set(e,t),a.set(e,i),t||(ir.has(e)||e.removeAttribute(r),ir.delete(e)),i||e.removeAttribute(n)}),or--,or||(rr=new WeakMap,rr=new WeakMap,ir=new WeakMap,ar={})}},ur=function(e,t,n){n===void 0&&(n=\`data-aria-hidden\`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(\`[aria-live], script\`))),lr(r,i,n,\`aria-hidden\`)):function(){return null}},dr=\`Dialog\`,[fr,pr]=ke(dr),[mr,hr]=fr(dr),gr=e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=_.useRef(null),c=_.useRef(null),[l,u]=ze({prop:r,defaultProp:i??!1,onChange:a,caller:dr});return(0,j.jsx)(mr,{scope:t,triggerRef:s,contentRef:c,contentId:Pe(),titleId:Pe(),descriptionId:Pe(),open:l,onOpenChange:u,onOpenToggle:_.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})};gr.displayName=dr;var _r=\`DialogTrigger\`,vr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(_r,n),a=De(t,i.triggerRef);return(0,j.jsx)(tt.button,{type:\`button\`,"aria-haspopup":\`dialog\`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":Rr(i.open),...r,ref:a,onClick:we(e.onClick,i.onOpenToggle)})});vr.displayName=_r;var yr=\`DialogPortal\`,[br,xr]=fr(yr,{forceMount:void 0}),Sr=e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=hr(yr,t);return(0,j.jsx)(br,{scope:t,forceMount:n,children:_.Children.map(r,e=>(0,j.jsx)(It,{present:n||a.open,children:(0,j.jsx)(Pt,{asChild:!0,container:i,children:e})}))})};Sr.displayName=yr;var Cr=\`DialogOverlay\`,wr=_.forwardRef((e,t)=>{let n=xr(Cr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=hr(Cr,e.__scopeDialog);return a.modal?(0,j.jsx)(It,{present:r||a.open,children:(0,j.jsx)(Er,{...i,ref:t})}):null});wr.displayName=Cr;var Tr=Ue(\`DialogOverlay.RemoveScroll\`),Er=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Cr,n),a=De(t,pt());return(0,j.jsx)(tr,{as:Tr,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(tt.div,{"data-state":Rr(i.open),...r,ref:a,style:{pointerEvents:\`auto\`,...r.style}})})}),Dr=\`DialogContent\`,Or=_.forwardRef((e,t)=>{let n=xr(Dr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=hr(Dr,e.__scopeDialog);return(0,j.jsx)(It,{present:r||a.open,children:a.modal?(0,j.jsx)(kr,{...i,ref:t}):(0,j.jsx)(Ar,{...i,ref:t})})});Or.displayName=Dr;var kr=_.forwardRef((e,t)=>{let n=hr(Dr,e.__scopeDialog),r=_.useRef(null),i=De(t,n.contentRef,r);return _.useEffect(()=>{let e=r.current;if(e)return ur(e)},[]),(0,j.jsx)(jr,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:we(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:we(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:we(e.onFocusOutside,e=>e.preventDefault())})}),Ar=_.forwardRef((e,t)=>{let n=hr(Dr,e.__scopeDialog),r=_.useRef(!1),i=_.useRef(!1);return(0,j.jsx)(jr,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===\`pointerdown\`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===\`focusin\`&&i.current&&t.preventDefault()}})}),jr=_.forwardRef((e,t)=>{let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=hr(Dr,n);return Wt(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(St,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(ut,{role:\`dialog\`,id:s.contentId,"aria-describedby":s.descriptionId,"aria-labelledby":s.titleId,"data-state":Rr(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})}),Mr=\`DialogTitle\`,Nr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Mr,n);return(0,j.jsx)(tt.h2,{id:i.titleId,...r,ref:t})});Nr.displayName=Mr;var Pr=\`DialogDescription\`,Fr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Pr,n);return(0,j.jsx)(tt.p,{id:i.descriptionId,...r,ref:t})});Fr.displayName=Pr;var Ir=\`DialogClose\`,Lr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Ir,n);return(0,j.jsx)(tt.button,{type:\`button\`,...r,ref:t,onClick:we(e.onClick,()=>i.onOpenChange(!1))})});Lr.displayName=Ir;function Rr(e){return e?\`open\`:\`closed\`}function zr(e){var t,n,r=\`\`;if(typeof e==\`string\`||typeof e==\`number\`)r+=e;else if(typeof e==\`object\`)if(Array.isArray(e)){var i=e.length;for(t=0;t<i;t++)e[t]&&(n=zr(e[t]))&&(r&&(r+=\` \`),r+=n)}else for(n in e)e[n]&&(r&&(r+=\` \`),r+=n);return r}function Br(){for(var e,t,n=0,r=\`\`,i=arguments.length;n<i;n++)(e=arguments[n])&&(t=zr(e))&&(r&&(r+=\` \`),r+=t);return r}function N({variant:e=\`default\`,size:t=\`sm\`,className:n,...r}){return(0,j.jsx)(\`button\`,{className:Br(\`inline-flex items-center justify-center gap-1.5 rounded-md transition-colors disabled:opacity-50 disabled:pointer-events-none\`,t===\`sm\`?\`h-8 px-3 text-[13px]\`:\`h-6 px-2 text-xs\`,{default:\`bg-emerald-600 text-white hover:bg-emerald-500 font-medium\`,outline:\`border border-neutral-700 text-neutral-200 hover:bg-neutral-800 hover:border-neutral-600\`,ghost:\`text-neutral-300 hover:bg-neutral-800\`,danger:\`border border-red-900/60 text-red-400 hover:bg-red-950/40\`}[e],n),...r})}function Vr({className:e,...t}){return(0,j.jsx)(\`input\`,{className:Br(\`h-8 w-full rounded-md border border-neutral-700 bg-neutral-900 px-2.5 text-[13px] text-neutral-100\`,\`placeholder:text-neutral-600 focus:border-brand focus:outline-none\`,e),...t})}function Hr({className:e,...t}){return(0,j.jsx)(\`textarea\`,{className:Br(\`w-full rounded-md border border-neutral-700 bg-neutral-900 p-3 font-mono text-[13px] text-neutral-100\`,\`placeholder:text-neutral-600 focus:border-brand focus:outline-none\`,e),...t})}function Ur({children:e}){return(0,j.jsx)(\`label\`,{className:\`mb-1 block text-xs font-medium text-neutral-400\`,children:e})}function Wr({open:e,onClose:t,title:n,children:r,wide:i}){return(0,j.jsx)(gr,{open:e,onOpenChange:e=>!e&&t(),children:(0,j.jsxs)(Sr,{children:[(0,j.jsx)(wr,{className:\`fixed inset-0 z-50 bg-black/60 backdrop-blur-sm\`}),(0,j.jsxs)(Or,{className:Br(\`fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 rounded-lg border border-neutral-700 bg-neutral-850 bg-[#1f1f1f] shadow-2xl\`,\`max-h-[85vh] overflow-auto\`,i?\`w-[640px]\`:\`w-[440px]\`),children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between border-b border-neutral-800 px-4 py-3\`,children:[(0,j.jsx)(Nr,{className:\`text-sm font-semibold\`,children:n}),(0,j.jsx)(Lr,{className:\`text-neutral-500 hover:text-neutral-200\`,children:(0,j.jsx)(ve,{size:16})})]}),(0,j.jsx)(\`div\`,{className:\`p-4\`,children:r})]})]})})}function Gr(){return(0,j.jsx)(\`div\`,{className:\`flex items-center justify-center py-16 text-neutral-500\`,children:(0,j.jsx)(\`div\`,{className:\`size-5 animate-spin rounded-full border-2 border-neutral-700 border-t-brand\`})})}function Kr({children:e}){return(0,j.jsx)(\`div\`,{className:\`py-16 text-center text-[13px] text-neutral-500\`,children:e})}var qr=50;function Jr(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(0),[c,l]=(0,_.useState)(0),[u,d]=(0,_.useState)(!0),[f,p]=(0,_.useState)(null),[m,h]=(0,_.useState)(!1),[g,v]=(0,_.useState)(\`\`),y=e.find(e=>e.name===n)||null,b=(0,_.useCallback)(async()=>{let e=await A.tables();t(e),r(t=>t??e[0]?.name??null)},[]);(0,_.useEffect)(()=>{b().finally(()=>d(!1))},[b]);let x=(0,_.useCallback)(async()=>{if(!n||!y)return;let e=y.primaryKey[0]?\`\${y.primaryKey[0]}.asc\`:void 0,{rows:t,total:r}=await A.rows(n,{limit:qr,offset:c*qr,order:e});a(t),s(r)},[n,c,y]);(0,_.useEffect)(()=>{x()},[x]);function ee(e){let t={};for(let n of y.primaryKey)t[n]=e[n];return t}async function S(e){if(!y?.primaryKey.length)return alert(\`Cannot delete: table has no primary key\`);if(confirm(\`Delete this row?\`))try{await A.deleteRow(n,ee(e)),await x(),await b()}catch(e){alert(e.message)}}return u?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex w-60 shrink-0 flex-col border-r border-neutral-800 bg-[#191919]\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between px-3 py-2.5 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:[\`Tables \`,(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:e.length})]}),(0,j.jsxs)(\`div\`,{className:\`flex-1 overflow-auto\`,children:[e.map(e=>(0,j.jsxs)(\`button\`,{onClick:()=>{r(e.name),l(0)},className:\`flex w-full items-center justify-between px-3 py-1.5 text-left text-[13px] \`+(n===e.name?\`bg-neutral-800 text-neutral-100\`:\`text-neutral-400 hover:bg-neutral-850 hover:bg-neutral-800/50\`),children:[(0,j.jsx)(\`span\`,{className:\`truncate font-mono\`,children:e.name}),(0,j.jsx)(\`span\`,{className:\`text-[11px] text-neutral-600\`,children:e.rowCount})]},e.name)),e.length===0&&(0,j.jsx)(Kr,{children:\`No tables. Create one in the SQL Editor.\`})]})]}),(0,j.jsx)(\`div\`,{className:\`flex min-w-0 flex-1 flex-col\`,children:y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-sm\`,children:y.name}),(0,j.jsxs)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:[o,\` rows\`]}),(0,j.jsxs)(\`div\`,{className:\`ml-auto flex items-center gap-1.5\`,children:[(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,onClick:()=>void x(),children:(0,j.jsx)(fe,{size:13})}),(0,j.jsxs)(N,{size:\`xs\`,onClick:()=>h(!0),children:[(0,j.jsx)(de,{size:13}),\` Insert\`]})]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 z-10 bg-[#1c1c1c]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800\`,children:[(0,j.jsx)(\`th\`,{className:\`w-16 px-2 py-1.5\`}),y.columns.map(e=>(0,j.jsxs)(\`th\`,{className:\`whitespace-nowrap px-3 py-1.5 text-left font-medium text-neutral-400\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-neutral-200\`,children:e.name}),e.isPrimaryKey&&(0,j.jsx)(\`span\`,{className:\`ml-1 text-[10px] text-brand\`,children:\`PK\`}),(0,j.jsx)(\`span\`,{className:\`ml-1.5 text-[11px] font-normal text-neutral-600\`,children:e.type})]},e.name))]})}),(0,j.jsx)(\`tbody\`,{children:i.map((e,t)=>(0,j.jsxs)(\`tr\`,{className:\`group border-b border-neutral-850 border-neutral-800/60 hover:bg-neutral-800/30\`,children:[(0,j.jsx)(\`td\`,{className:\`px-2\`,children:(0,j.jsxs)(\`div\`,{className:\`flex gap-0.5 opacity-0 group-hover:opacity-100\`,children:[(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-brand\`,onClick:()=>p(e),children:(0,j.jsx)(T,{size:13})}),(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-red-400\`,onClick:()=>S(e),children:(0,j.jsx)(ge,{size:13})})]})}),y.columns.map(t=>(0,j.jsx)(\`td\`,{className:\`max-w-[320px] truncate px-3 py-1.5 font-mono text-neutral-300\`,children:(0,j.jsx)(Yr,{value:e[t.name]})},t.name))]},t))})]}),i.length===0&&(0,j.jsx)(Kr,{children:\`No rows yet.\`})]}),(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-t border-neutral-800 px-4 py-2 text-xs text-neutral-500\`,children:[(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,disabled:c===0,onClick:()=>l(e=>e-1),children:(0,j.jsx)(ie,{size:13})}),(0,j.jsxs)(\`span\`,{children:[o===0?0:c*qr+1,\`–\`,Math.min((c+1)*qr,o),\` of \`,o]}),(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,disabled:(c+1)*qr>=o,onClick:()=>l(e=>e+1),children:(0,j.jsx)(ae,{size:13})})]})]}):(0,j.jsx)(Kr,{children:\`Select a table.\`})}),f&&y&&(0,j.jsx)(Xr,{table:y,initial:f,onClose:()=>p(null),onSave:async e=>{await A.updateRow(n,ee(f),e),p(null),await x()}}),m&&y&&(0,j.jsx)(Xr,{table:y,initial:{},isNew:!0,onClose:()=>h(!1),onSave:async e=>{await A.insertRow(n,e),h(!1),await x(),await b()}}),g&&(0,j.jsx)(\`p\`,{className:\`text-red-400\`,children:g})]})}function Yr({value:e}){return e==null?(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:\`NULL\`}):typeof e==\`boolean\`?(0,j.jsx)(\`span\`,{className:\`text-blue-400\`,children:String(e)}):typeof e==\`object\`?(0,j.jsx)(\`span\`,{className:\`text-amber-300/80\`,children:JSON.stringify(e)}):(0,j.jsx)(j.Fragment,{children:String(e)})}function Xr({table:e,initial:t,isNew:n,onClose:r,onSave:i}){let a=e.columns.filter(e=>!(n&&e.isPrimaryKey&&e.hasDefault)),[o,s]=(0,_.useState)(()=>{let e={};for(let n of a){let r=t[n.name];e[n.name]=r==null?\`\`:typeof r==\`object\`?JSON.stringify(r):String(r)}return e}),[c,l]=(0,_.useState)({}),[u,d]=(0,_.useState)(\`\`),[f,p]=(0,_.useState)(!1);async function m(){p(!0),d(\`\`);let e={};for(let t of a){if(!n&&!c[t.name])continue;let r=o[t.name];if(r===\`\`&&(t.nullable||t.hasDefault)){n||(e[t.name]=null);continue}e[t.name]=Zr(r,t.type)}try{await i(e)}catch(e){d(e.message),p(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:r,title:n?\`Insert into \${e.name}\`:\`Edit row in \${e.name}\`,wide:!0,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[a.map(e=>(0,j.jsxs)(\`div\`,{children:[(0,j.jsxs)(Ur,{children:[e.name,(0,j.jsxs)(\`span\`,{className:\`ml-1.5 font-normal text-neutral-600\`,children:[e.type,e.isPrimaryKey?\` · PK\`:\`\`,!e.nullable&&!e.hasDefault?\` · required\`:\`\`]})]}),(0,j.jsx)(Vr,{value:o[e.name]??\`\`,placeholder:e.hasDefault?\`default\`:e.nullable?\`NULL\`:\`\`,onChange:t=>{s(n=>({...n,[e.name]:t.target.value})),l(t=>({...t,[e.name]:!0}))}})]},e.name)),u&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:u}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2 pt-1\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:r,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:m,disabled:f,children:f?\`Saving…\`:n?\`Insert\`:\`Save\`})]})]})})}function Zr(e,t){if(t===\`bool\`)return e===\`true\`||e===\`t\`||e===\`1\`;if([\`int2\`,\`int4\`,\`int8\`,\`float4\`,\`float8\`,\`numeric\`].includes(t)){let t=Number(e);return Number.isNaN(t)?e:t}if(t===\`json\`||t===\`jsonb\`)try{return JSON.parse(e)}catch{return e}if(t.startsWith(\`_\`))try{return JSON.parse(e)}catch{return e}return e}function Qr(){let[e,t]=(0,_.useState)(\`select * from \`),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)(!1);async function o(){a(!0),r(await A.sql(e)),a(!1)}let s=n?.rows?.[0]?Object.keys(n.rows[0]):[];return(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`border-b border-neutral-800 p-3\`,children:[(0,j.jsx)(Hr,{className:\`h-40\`,value:e,onChange:e=>t(e.target.value),onKeyDown:e=>(e.metaKey||e.ctrlKey)&&e.key===\`Enter\`&&o(),spellCheck:!1,placeholder:\`select * from … (⌘⏎ to run)\`}),(0,j.jsxs)(\`div\`,{className:\`mt-2 flex items-center gap-3\`,children:[(0,j.jsxs)(N,{onClick:o,disabled:i,children:[(0,j.jsx)(E,{size:13}),\` Run\`]}),(0,j.jsx)(\`span\`,{className:\`text-xs text-neutral-500\`,children:\`⌘⏎\`}),n&&(0,j.jsx)(\`span\`,{className:\`ml-auto text-xs text-neutral-500\`,children:n.ok?\`\${n.rowCount} rows · \${n.ms} ms\${n.affectedRows==null?\`\`:\` · \${n.affectedRows} affected\`}\`:\`\`})]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[!n&&(0,j.jsx)(Kr,{children:\`Run a query to see results.\`}),n&&!n.ok&&(0,j.jsxs)(\`div\`,{className:\`m-4 rounded-md border border-red-900/50 bg-red-950/30 p-3 text-[13px] text-red-300\`,children:[(0,j.jsx)(\`div\`,{className:\`font-mono\`,children:n.error}),n.code&&(0,j.jsxs)(\`div\`,{className:\`mt-1 text-xs text-red-400/70\`,children:[\`SQLSTATE \`,n.code]}),n.hint&&(0,j.jsxs)(\`div\`,{className:\`mt-1 text-xs text-neutral-400\`,children:[\`Hint: \`,n.hint]})]}),n?.ok&&s.length>0&&(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 bg-[#1c1c1c]\`,children:(0,j.jsx)(\`tr\`,{className:\`border-b border-neutral-800\`,children:s.map(e=>(0,j.jsx)(\`th\`,{className:\`whitespace-nowrap px-3 py-1.5 text-left font-mono font-medium text-neutral-300\`,children:e},e))})}),(0,j.jsx)(\`tbody\`,{children:n.rows.map((e,t)=>(0,j.jsx)(\`tr\`,{className:\`border-b border-neutral-800/60 hover:bg-neutral-800/30\`,children:s.map(t=>(0,j.jsx)(\`td\`,{className:\`max-w-[360px] truncate px-3 py-1.5 font-mono text-neutral-300\`,children:$r(e[t])},t))},t))})]}),n?.ok&&s.length===0&&(0,j.jsxs)(Kr,{children:[\`Statement executed\`,n.affectedRows==null?\`\`:\` · \${n.affectedRows} rows affected\`,\`.\`]})]})]})}function $r(e){return e==null?(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:\`NULL\`}):typeof e==\`object\`?JSON.stringify(e):String(e)}function ei(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(!0),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)(null);async function c(){t(await A.users())}(0,_.useEffect)(()=>{c().finally(()=>r(!1))},[]);async function l(e){confirm(\`Delete \${e.email||e.id}?\`)&&(await A.deleteUser(e.id),await c())}return n?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`text-sm font-semibold\`,children:\`Users\`}),(0,j.jsx)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:e.length}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>a(!0),children:[(0,j.jsx)(de,{size:13}),\` Add user\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 bg-[#1c1c1c]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-1.5 font-medium\`,children:\`Email\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`UID\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Provider\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Created\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Last sign in\`}),(0,j.jsx)(\`th\`,{className:\`w-20\`})]})}),(0,j.jsx)(\`tbody\`,{children:e.map(e=>(0,j.jsxs)(\`tr\`,{className:\`group border-b border-neutral-800/60 hover:bg-neutral-800/30\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5\`,children:e.email||(0,j.jsx)(\`span\`,{className:\`text-neutral-500\`,children:e.is_anonymous?\`anonymous\`:\`—\`})}),(0,j.jsxs)(\`td\`,{className:\`px-3 py-1.5 font-mono text-[11px] text-neutral-500\`,children:[e.id.slice(0,8),\`…\`]}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:e.app_metadata?.provider||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:(e.created_at||\`\`).slice(0,10)}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:(e.last_sign_in_at||\`\`).slice(0,16).replace(\`T\`,\` \`)}),(0,j.jsx)(\`td\`,{className:\`px-3\`,children:(0,j.jsxs)(\`div\`,{className:\`flex gap-0.5 opacity-0 group-hover:opacity-100\`,children:[(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-brand\`,title:\`Reset password\`,onClick:()=>s(e),children:(0,j.jsx)(ue,{size:13})}),(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-red-400\`,onClick:()=>l(e),children:(0,j.jsx)(ge,{size:13})})]})})]},e.id))})]}),e.length===0&&(0,j.jsx)(Kr,{children:\`No users yet.\`})]}),i&&(0,j.jsx)(ti,{onClose:()=>a(!1),onDone:async()=>{a(!1),await c()}}),o&&(0,j.jsx)(ni,{user:o,onClose:()=>s(null),onDone:async()=>{s(null),await c()}})]})}function ti({onClose:e,onDone:t}){let[n,r]=(0,_.useState)(\`\`),[i,a]=(0,_.useState)(\`\`),[o,s]=(0,_.useState)(\`\`),[c,l]=(0,_.useState)(!1);async function u(){l(!0),s(\`\`);try{await A.createUser({email:n,password:i||void 0,email_confirm:!0}),t()}catch(e){s(e.message),l(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`Add user\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Email\`}),(0,j.jsx)(Vr,{value:n,onChange:e=>r(e.target.value),placeholder:\`user@example.com\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Password (optional)\`}),(0,j.jsx)(Vr,{type:\`password\`,value:i,onChange:e=>a(e.target.value)})]}),o&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:o}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:u,disabled:c||!n,children:c?\`Creating…\`:\`Create\`})]})]})})}function ni({user:e,onClose:t,onDone:n}){let[r,i]=(0,_.useState)(\`\`),[a,o]=(0,_.useState)(\`\`),[s,c]=(0,_.useState)(!1);async function l(){c(!0),o(\`\`);try{await A.updateUser(e.id,{password:r}),n()}catch(e){o(e.message),c(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:t,title:\`Reset password · \${e.email||e.id}\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`New password\`}),(0,j.jsx)(Vr,{type:\`password\`,value:r,onChange:e=>i(e.target.value)})]}),a&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:a}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:t,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:l,disabled:s||r.length<6,children:s?\`Saving…\`:\`Update\`})]})]})})}function ri(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(!0),[c,l]=(0,_.useState)(!1),u=(0,_.useRef)(null);async function d(){let e=await A.buckets();t(e),r(t=>t??e[0]?.id??null)}async function f(e){a(await A.listObjects(e))}(0,_.useEffect)(()=>{d().finally(()=>s(!1))},[]),(0,_.useEffect)(()=>{n&&f(n)},[n]);async function p(e){n&&(await A.uploadObject(n,e.name,e),await f(n))}async function m(e){!n||!confirm(\`Delete \${e}?\`)||(await A.removeObject(n,e),await f(n))}async function h(e){if(confirm(\`Delete bucket "\${e}"? It must be empty.\`))try{await A.deleteBucket(e),r(null),await d()}catch(e){alert(e.message)}}return o?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex w-60 shrink-0 flex-col border-r border-neutral-800 bg-[#191919]\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between px-3 py-2.5 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:[\`Buckets\`,(0,j.jsx)(\`button\`,{className:\`text-neutral-400 hover:text-brand\`,onClick:()=>l(!0),children:(0,j.jsx)(de,{size:14})})]}),(0,j.jsxs)(\`div\`,{className:\`flex-1 overflow-auto\`,children:[e.map(e=>(0,j.jsxs)(\`button\`,{onClick:()=>r(e.id),className:\`group flex w-full items-center justify-between px-3 py-1.5 text-left text-[13px] \`+(n===e.id?\`bg-neutral-800 text-neutral-100\`:\`text-neutral-400 hover:bg-neutral-800/50\`),children:[(0,j.jsxs)(\`span\`,{className:\`truncate\`,children:[e.id,e.public&&(0,j.jsx)(\`span\`,{className:\`ml-1.5 text-[10px] text-brand\`,children:\`public\`})]}),(0,j.jsx)(ge,{size:13,className:\`opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:t=>{t.stopPropagation(),h(e.id)}})]},e.id)),e.length===0&&(0,j.jsx)(Kr,{children:\`No buckets.\`})]})]}),(0,j.jsx)(\`div\`,{className:\`flex min-w-0 flex-1 flex-col\`,children:n?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-sm\`,children:n}),(0,j.jsx)(\`input\`,{ref:u,type:\`file\`,className:\`hidden\`,onChange:e=>e.target.files?.[0]&&p(e.target.files[0])}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>u.current?.click(),children:[(0,j.jsx)(_e,{size:13}),\` Upload\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-2\`,children:[i.map(e=>(0,j.jsxs)(\`div\`,{className:\`group flex items-center gap-2 rounded-md px-2 py-1.5 text-[13px] hover:bg-neutral-800/40\`,children:[(0,j.jsx)(se,{size:14,className:\`text-neutral-500\`}),(0,j.jsx)(\`span\`,{className:\`font-mono\`,children:e.name}),e.metadata?.size!=null&&(0,j.jsx)(\`span\`,{className:\`text-[11px] text-neutral-500\`,children:ai(e.metadata.size)}),(0,j.jsx)(\`button\`,{className:\`ml-auto p-1 text-neutral-500 opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:()=>m(e.name),children:(0,j.jsx)(ge,{size:13})})]},e.name)),i.length===0&&(0,j.jsx)(Kr,{children:\`Empty bucket. Upload a file.\`})]})]}):(0,j.jsx)(Kr,{children:\`Select or create a bucket.\`})}),c&&(0,j.jsx)(ii,{onClose:()=>l(!1),onDone:async()=>{l(!1),await d()}})]})}function ii({onClose:e,onDone:t}){let[n,r]=(0,_.useState)(\`\`),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)(\`\`),[c,l]=(0,_.useState)(!1);async function u(){l(!0),s(\`\`);try{await A.createBucket({id:n,name:n,public:i}),t()}catch(e){s(e.message),l(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`New bucket\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Name\`}),(0,j.jsx)(Vr,{value:n,onChange:e=>r(e.target.value),placeholder:\`avatars\`})]}),(0,j.jsxs)(\`label\`,{className:\`flex items-center gap-2 text-[13px] text-neutral-300\`,children:[(0,j.jsx)(\`input\`,{type:\`checkbox\`,checked:i,onChange:e=>a(e.target.checked)}),\`Public bucket (objects readable without auth)\`]}),o&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:o}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:u,disabled:c||!n,children:c?\`Creating…\`:\`Create\`})]})]})})}function ai(e){return e<1024?\`\${e} B\`:e<1024*1024?\`\${(e/1024).toFixed(1)} KB\`:\`\${(e/1024/1024).toFixed(1)} MB\`}function oi(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)([]),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)([]),[c,l]=(0,_.useState)(!0);return(0,_.useEffect)(()=>{Promise.all([A.stats(),A.migrations(),A.functions(),A.triggers()]).then(([e,n,i,o])=>{t(e),r(n),a(i),s(o)}).finally(()=>l(!1))},[]),c?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`h-full overflow-auto p-6\`,children:[(0,j.jsx)(\`h1\`,{className:\`text-sm font-semibold text-neutral-300\`,children:\`Database\`}),e&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(\`p\`,{className:\`mt-1 font-mono text-xs text-neutral-500\`,children:e.version}),(0,j.jsxs)(\`div\`,{className:\`mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6\`,children:[(0,j.jsx)(si,{label:\`Tables\`,value:e.tables}),(0,j.jsx)(si,{label:\`Users\`,value:e.users}),(0,j.jsx)(si,{label:\`Buckets\`,value:e.buckets}),(0,j.jsx)(si,{label:\`Objects\`,value:e.objects}),(0,j.jsx)(si,{label:\`Migrations\`,value:e.migrations}),(0,j.jsx)(si,{label:\`DB size\`,value:e.dbSize})]})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Migrations\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Version\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Applied\`})]})}),(0,j.jsx)(\`tbody\`,{children:n.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.version}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono\`,children:e.name||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:(e.applied_at||\`\`).slice(0,19).replace(\`T\`,\` \`)})]},e.version))})]}),n.length===0&&(0,j.jsx)(Kr,{children:\`No migrations applied.\`})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Functions\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Arguments\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Returns\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Language\`})]})}),(0,j.jsx)(\`tbody\`,{children:i.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-200\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`max-w-[280px] truncate px-4 py-1.5 font-mono text-[11px] text-neutral-500\`,children:e.args||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.returns}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:e.language})]},e.name))})]}),i.length===0&&(0,j.jsx)(Kr,{children:\`No user functions.\`})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Triggers\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Table\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Timing\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Events\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Function\`})]})}),(0,j.jsx)(\`tbody\`,{children:o.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-200\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.table}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:e.timing}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:(e.events||[]).join(\`, \`)}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-[11px] text-neutral-500\`,children:e.function})]},e.table+e.name))})]}),o.length===0&&(0,j.jsx)(Kr,{children:\`No triggers.\`})]})]})}function si({label:e,value:t}){return(0,j.jsxs)(\`div\`,{className:\`rounded-lg border border-neutral-800 bg-[#191919] p-3\`,children:[(0,j.jsx)(\`div\`,{className:\`text-lg font-semibold text-neutral-100\`,children:t}),(0,j.jsx)(\`div\`,{className:\`text-xs text-neutral-500\`,children:e})]})}function ci(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(!0),[i,a]=(0,_.useState)(!1);async function o(){t(await A.policies())}(0,_.useEffect)(()=>{o().finally(()=>r(!1))},[]);async function s(e){confirm(\`Drop policy "\${e.name}" on \${e.table}?\`)&&(await A.dropPolicy(e.table,e.name),await o())}if(n)return(0,j.jsx)(Gr,{});let c=new Map;for(let t of e)c.has(t.table)||c.set(t.table,[]),c.get(t.table).push(t);return(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(me,{size:15,className:\`text-brand\`}),(0,j.jsx)(\`span\`,{className:\`text-sm font-semibold\`,children:\`RLS Policies\`}),(0,j.jsx)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:e.length}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>a(!0),children:[(0,j.jsx)(de,{size:13}),\` New policy\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-4\`,children:[[...c].map(([e,t])=>(0,j.jsxs)(\`div\`,{className:\`mb-5\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-1.5 font-mono text-[13px] text-neutral-300\`,children:e}),(0,j.jsx)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:(0,j.jsxs)(\`table\`,{className:\`w-full text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919] text-neutral-400\`,children:(0,j.jsxs)(\`tr\`,{children:[(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Policy\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Command\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Roles\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`USING / CHECK\`}),(0,j.jsx)(\`th\`,{className:\`w-10\`})]})}),(0,j.jsx)(\`tbody\`,{children:t.map(e=>(0,j.jsxs)(\`tr\`,{className:\`group border-t border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:e.cmd}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 font-mono text-[11px] text-neutral-500\`,children:Array.isArray(e.roles)?e.roles.join(\`, \`):e.roles}),(0,j.jsx)(\`td\`,{className:\`max-w-[360px] truncate px-3 py-1.5 font-mono text-[11px] text-amber-300/70\`,children:[e.using_expr&&\`USING \${e.using_expr}\`,e.with_check&&\`CHECK \${e.with_check}\`].filter(Boolean).join(\` · \`)}),(0,j.jsx)(\`td\`,{className:\`px-2\`,children:(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:()=>s(e),children:(0,j.jsx)(ge,{size:13})})})]},e.name))})]})})]},e)),e.length===0&&(0,j.jsx)(Kr,{children:\`No policies. Create one, or enable RLS on a table first.\`})]}),i&&(0,j.jsx)(li,{onClose:()=>a(!1),onDone:async()=>{a(!1),await o()}})]})}function li({onClose:e,onDone:t}){let[n,r]=(0,_.useState)({table:\`\`,name:\`\`,command:\`ALL\`,roles:\`authenticated\`,using:\`\`,check:\`\`}),[i,a]=(0,_.useState)(\`\`),[o,s]=(0,_.useState)(!1),c=(e,t)=>r(n=>({...n,[e]:t}));async function l(){s(!0),a(\`\`);try{let e=await A.createPolicy(n);if(e.error)throw Error(e.error+(e.hint?\` (\${e.hint})\`:\`\`));t()}catch(e){a(e.message),s(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`New RLS policy\`,wide:!0,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{className:\`grid grid-cols-2 gap-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Table\`}),(0,j.jsx)(Vr,{value:n.table,onChange:e=>c(\`table\`,e.target.value),placeholder:\`todos\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Policy name\`}),(0,j.jsx)(Vr,{value:n.name,onChange:e=>c(\`name\`,e.target.value),placeholder:\`own rows\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Command\`}),(0,j.jsx)(\`select\`,{className:\`h-8 w-full rounded-md border border-neutral-700 bg-neutral-900 px-2 text-[13px]\`,value:n.command,onChange:e=>c(\`command\`,e.target.value),children:[\`ALL\`,\`SELECT\`,\`INSERT\`,\`UPDATE\`,\`DELETE\`].map(e=>(0,j.jsx)(\`option\`,{children:e},e))})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Roles\`}),(0,j.jsx)(Vr,{value:n.roles,onChange:e=>c(\`roles\`,e.target.value),placeholder:\`authenticated\`})]})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`USING expression (read/visibility)\`}),(0,j.jsx)(Vr,{value:n.using,onChange:e=>c(\`using\`,e.target.value),placeholder:\`user_id = auth.uid()\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`WITH CHECK expression (writes)\`}),(0,j.jsx)(Vr,{value:n.check,onChange:e=>c(\`check\`,e.target.value),placeholder:\`user_id = auth.uid()\`})]}),i&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:i}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:l,disabled:o||!n.table||!n.name,children:o?\`Creating…\`:\`Create policy\`})]})]})})}var ui={info:\`text-neutral-500\`,warn:\`text-amber-400\`,error:\`text-red-400\`};function di(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)(!0),i=(0,_.useRef)(null);async function a(){try{t(await A.logs())}catch{}}return(0,_.useEffect)(()=>{if(a(),n)return i.current=setInterval(a,2e3),()=>{i.current&&clearInterval(i.current)}},[n]),e===null?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between border-b border-neutral-800 px-6 py-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(\`h1\`,{className:\`text-sm font-semibold text-neutral-300\`,children:\`Logs\`}),(0,j.jsx)(\`p\`,{className:\`text-xs text-neutral-500\`,children:\`Recent requests and server events\`})]}),(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2\`,children:[(0,j.jsx)(N,{className:n?\`\`:\`opacity-60\`,onClick:()=>r(e=>!e),title:n?\`Pause auto-refresh\`:\`Resume auto-refresh\`,children:n?\`⏸ Live\`:\`▶ Paused\`}),(0,j.jsx)(N,{onClick:a,children:\`Refresh\`}),(0,j.jsx)(N,{onClick:async()=>{await A.clearLogs(),a()},children:\`Clear\`})]})]}),(0,j.jsx)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-3 font-mono text-[12.5px] leading-relaxed\`,children:e.length===0?(0,j.jsx)(Kr,{children:\`No logs yet. Make a request or trigger an email/webhook.\`}):e.map((e,t)=>(0,j.jsxs)(\`div\`,{className:\`flex gap-3 whitespace-pre-wrap break-words px-3 py-0.5 hover:bg-neutral-800/40\`,children:[(0,j.jsx)(\`span\`,{className:\`shrink-0 tabular-nums text-neutral-600\`,children:e.ts.slice(11,23)}),(0,j.jsx)(\`span\`,{className:\`shrink-0 uppercase \`+ui[e.level],children:e.level}),(0,j.jsx)(\`span\`,{className:\`text-neutral-300\`,children:e.msg})]},t))})]})}var fi=[{id:\`table\`,label:\`Table Editor\`,icon:he},{id:\`sql\`,label:\`SQL Editor\`,icon:D},{id:\`auth\`,label:\`Authentication\`,icon:ue},{id:\`policies\`,label:\`RLS Policies\`,icon:me},{id:\`storage\`,label:\`Storage\`,icon:le},{id:\`database\`,label:\`Database\`,icon:ce},{id:\`logs\`,label:\`Logs\`,icon:pe}];function pi({size:e=24}){return(0,j.jsxs)(\`svg\`,{width:e,height:e,viewBox:\`0 0 120 120\`,children:[(0,j.jsx)(\`path\`,{d:\`M24 34v56c0 8.8 16.1 16 36 16s36-7.2 36-16V34\`,fill:\`#2a9d6f\`}),(0,j.jsx)(\`ellipse\`,{cx:\`60\`,cy:\`34\`,rx:\`36\`,ry:\`16\`,fill:\`#3ecf8e\`})]})}function mi(){let[e,t]=(0,_.useState)(!!be()),[n,r]=(0,_.useState)(\`table\`);return(0,_.useEffect)(()=>{be()&&A.ping().catch(()=>{Se(),t(!1)})},[]),e?(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`nav\`,{className:\`flex w-14 shrink-0 flex-col items-center gap-1 border-r border-neutral-800 bg-[#171717] py-3\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-3\`,children:(0,j.jsx)(pi,{})}),fi.map(e=>{let t=e.icon,i=n===e.id;return(0,j.jsxs)(\`button\`,{title:e.label,onClick:()=>r(e.id),className:\`group relative flex size-9 items-center justify-center rounded-md transition-colors \`+(i?\`bg-neutral-800 text-brand\`:\`text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200\`),children:[(0,j.jsx)(t,{size:18}),(0,j.jsx)(\`span\`,{className:\`pointer-events-none absolute left-11 z-20 hidden whitespace-nowrap rounded-md border border-neutral-700 bg-neutral-900 px-2 py-1 text-xs text-neutral-200 group-hover:block\`,children:e.label})]},e.id)}),(0,j.jsx)(\`button\`,{title:\`Log out\`,onClick:()=>{Se(),t(!1)},className:\`mt-auto flex size-9 items-center justify-center rounded-md text-neutral-600 hover:bg-neutral-800 hover:text-neutral-300\`,children:(0,j.jsx)(oe,{size:18})})]}),(0,j.jsxs)(\`main\`,{className:\`min-w-0 flex-1\`,children:[n===\`table\`&&(0,j.jsx)(Jr,{}),n===\`sql\`&&(0,j.jsx)(Qr,{}),n===\`auth\`&&(0,j.jsx)(ei,{}),n===\`policies\`&&(0,j.jsx)(ci,{}),n===\`storage\`&&(0,j.jsx)(ri,{}),n===\`database\`&&(0,j.jsx)(oi,{}),n===\`logs\`&&(0,j.jsx)(di,{})]})]}):(0,j.jsx)(hi,{onOk:()=>t(!0)})}function hi({onOk:e}){let[t,n]=(0,_.useState)(\`\`),[r,i]=(0,_.useState)(\`\`),[a,o]=(0,_.useState)(!1);async function s(){o(!0),i(\`\`),xe(t.trim());try{await A.ping(),e()}catch(e){Se(),i(\`Invalid service_role key: \`+e.message)}finally{o(!1)}}return(0,j.jsx)(\`div\`,{className:\`flex h-full items-center justify-center\`,children:(0,j.jsxs)(\`div\`,{className:\`w-[400px] text-center\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-4 flex justify-center\`,children:(0,j.jsx)(pi,{size:48})}),(0,j.jsx)(\`h1\`,{className:\`text-lg font-semibold\`,children:\`tinbase studio\`}),(0,j.jsxs)(\`p\`,{className:\`mt-1 text-[13px] text-neutral-500\`,children:[\`Sign in with the \`,(0,j.jsx)(\`span\`,{className:\`text-neutral-300\`,children:\`service_role\`}),\` key printed when tinbase started.\`]}),(0,j.jsxs)(\`div\`,{className:\`mt-5 space-y-3 text-left\`,children:[(0,j.jsx)(Vr,{type:\`password\`,placeholder:\`service_role key (eyJ…)\`,value:t,onChange:e=>n(e.target.value),onKeyDown:e=>e.key===\`Enter\`&&s()}),(0,j.jsx)(N,{className:\`w-full\`,onClick:s,disabled:a||!t.trim(),children:a?\`Checking…\`:\`Continue\`}),r&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:r})]})]})})}(0,v.createRoot)(document.getElementById(\`root\`)).render((0,j.jsx)(_.StrictMode,{children:(0,j.jsx)(mi,{})}));</script>
|
|
64
|
+
\`},Xn=0,Zn=[];function Qn(e){var t=_.useRef([]),n=_.useRef([0,0]),r=_.useRef(),i=_.useState(Xn++)[0],a=_.useState(bn)[0],o=_.useRef(e);_.useEffect(function(){o.current=e},[e]),_.useEffect(function(){if(e.inert){document.body.classList.add(\`block-interactivity-\${i}\`);var t=Jt([e.lockRef.current],(e.shards||[]).map(qn),!0).filter(Boolean);return t.forEach(function(e){return e.classList.add(\`allow-interactivity-\${i}\`)}),function(){document.body.classList.remove(\`block-interactivity-\${i}\`),t.forEach(function(e){return e.classList.remove(\`allow-interactivity-\${i}\`)})}}},[e.inert,e.lockRef.current,e.shards]);var s=_.useCallback(function(e,t){if(\`touches\`in e&&e.touches.length===2||e.type===\`wheel\`&&e.ctrlKey)return!o.current.allowPinchZoom;var i=Gn(e),a=n.current,s=\`deltaX\`in e?e.deltaX:a[0]-i[0],c=\`deltaY\`in e?e.deltaY:a[1]-i[1],l,u=e.target,d=Math.abs(s)>Math.abs(c)?\`h\`:\`v\`;if(\`touches\`in e&&d===\`h\`&&u.type===\`range\`)return!1;var f=window.getSelection(),p=f&&f.anchorNode;if(p&&(p===u||p.contains(u)))return!1;var m=Rn(d,u);if(!m)return!0;if(m?l=d:(l=d===\`v\`?\`h\`:\`v\`,m=Rn(d,u)),!m)return!1;if(!r.current&&\`changedTouches\`in e&&(s||c)&&(r.current=l),!l)return!0;var h=r.current||l;return Wn(h,t,e,h===\`h\`?s:c,!0)},[]),c=_.useCallback(function(e){var n=e;if(!(!Zn.length||Zn[Zn.length-1]!==a)){var r=\`deltaY\`in n?Kn(n):Gn(n),i=t.current.filter(function(e){return e.name===n.type&&(e.target===n.target||n.target===e.shadowParent)&&Jn(e.delta,r)})[0];if(i&&i.should){n.cancelable&&n.preventDefault();return}if(!i){var c=(o.current.shards||[]).map(qn).filter(Boolean).filter(function(e){return e.contains(n.target)});(c.length>0?s(n,c[0]):!o.current.noIsolation)&&n.cancelable&&n.preventDefault()}}},[]),l=_.useCallback(function(e,n,r,i){var a={name:e,delta:n,target:r,should:i,shadowParent:$n(r)};t.current.push(a),setTimeout(function(){t.current=t.current.filter(function(e){return e!==a})},1)},[]),u=_.useCallback(function(e){n.current=Gn(e),r.current=void 0},[]),d=_.useCallback(function(t){l(t.type,Kn(t),t.target,s(t,e.lockRef.current))},[]),f=_.useCallback(function(t){l(t.type,Gn(t),t.target,s(t,e.lockRef.current))},[]);_.useEffect(function(){return Zn.push(a),e.setCallbacks({onScrollCapture:d,onWheelCapture:d,onTouchMoveCapture:f}),document.addEventListener(\`wheel\`,c,Nn),document.addEventListener(\`touchmove\`,c,Nn),document.addEventListener(\`touchstart\`,u,Nn),function(){Zn=Zn.filter(function(e){return e!==a}),document.removeEventListener(\`wheel\`,c,Nn),document.removeEventListener(\`touchmove\`,c,Nn),document.removeEventListener(\`touchstart\`,u,Nn)}},[]);var p=e.removeScrollBar,m=e.inert;return _.createElement(_.Fragment,null,m?_.createElement(a,{styles:Yn(i)}):null,p?_.createElement(An,{noRelative:e.noRelative,gapMode:e.gapMode}):null)}function $n(e){for(var t=null;e!==null;)e instanceof ShadowRoot&&(t=e.host,e=e.host),e=e.parentNode;return t}var er=ln(un,Qn),tr=_.forwardRef(function(e,t){return _.createElement(fn,Kt({},e,{ref:t,sideCar:er}))});tr.classNames=fn.classNames;var nr=function(e){return typeof document>\`u\`?null:(Array.isArray(e)?e[0]:e).ownerDocument.body},rr=new WeakMap,ir=new WeakMap,ar={},or=0,sr=function(e){return e&&(e.host||sr(e.parentNode))},cr=function(e,t){return t.map(function(t){if(e.contains(t))return t;var n=sr(t);return n&&e.contains(n)?n:(console.error(\`aria-hidden\`,t,\`in not contained inside\`,e,\`. Doing nothing\`),null)}).filter(function(e){return!!e})},lr=function(e,t,n,r){var i=cr(t,Array.isArray(e)?e:[e]);ar[n]||(ar[n]=new WeakMap);var a=ar[n],o=[],s=new Set,c=new Set(i),l=function(e){!e||s.has(e)||(s.add(e),l(e.parentNode))};i.forEach(l);var u=function(e){!e||c.has(e)||Array.prototype.forEach.call(e.children,function(e){if(s.has(e))u(e);else try{var t=e.getAttribute(r),i=t!==null&&t!==\`false\`,c=(rr.get(e)||0)+1,l=(a.get(e)||0)+1;rr.set(e,c),a.set(e,l),o.push(e),c===1&&i&&ir.set(e,!0),l===1&&e.setAttribute(n,\`true\`),i||e.setAttribute(r,\`true\`)}catch(t){console.error(\`aria-hidden: cannot operate on \`,e,t)}})};return u(t),s.clear(),or++,function(){o.forEach(function(e){var t=rr.get(e)-1,i=a.get(e)-1;rr.set(e,t),a.set(e,i),t||(ir.has(e)||e.removeAttribute(r),ir.delete(e)),i||e.removeAttribute(n)}),or--,or||(rr=new WeakMap,rr=new WeakMap,ir=new WeakMap,ar={})}},ur=function(e,t,n){n===void 0&&(n=\`data-aria-hidden\`);var r=Array.from(Array.isArray(e)?e:[e]),i=t||nr(e);return i?(r.push.apply(r,Array.from(i.querySelectorAll(\`[aria-live], script\`))),lr(r,i,n,\`aria-hidden\`)):function(){return null}},dr=\`Dialog\`,[fr,pr]=ke(dr),[mr,hr]=fr(dr),gr=e=>{let{__scopeDialog:t,children:n,open:r,defaultOpen:i,onOpenChange:a,modal:o=!0}=e,s=_.useRef(null),c=_.useRef(null),[l,u]=ze({prop:r,defaultProp:i??!1,onChange:a,caller:dr});return(0,j.jsx)(mr,{scope:t,triggerRef:s,contentRef:c,contentId:Pe(),titleId:Pe(),descriptionId:Pe(),open:l,onOpenChange:u,onOpenToggle:_.useCallback(()=>u(e=>!e),[u]),modal:o,children:n})};gr.displayName=dr;var _r=\`DialogTrigger\`,vr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(_r,n),a=De(t,i.triggerRef);return(0,j.jsx)(tt.button,{type:\`button\`,"aria-haspopup":\`dialog\`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":Rr(i.open),...r,ref:a,onClick:we(e.onClick,i.onOpenToggle)})});vr.displayName=_r;var yr=\`DialogPortal\`,[br,xr]=fr(yr,{forceMount:void 0}),Sr=e=>{let{__scopeDialog:t,forceMount:n,children:r,container:i}=e,a=hr(yr,t);return(0,j.jsx)(br,{scope:t,forceMount:n,children:_.Children.map(r,e=>(0,j.jsx)(It,{present:n||a.open,children:(0,j.jsx)(Pt,{asChild:!0,container:i,children:e})}))})};Sr.displayName=yr;var Cr=\`DialogOverlay\`,wr=_.forwardRef((e,t)=>{let n=xr(Cr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=hr(Cr,e.__scopeDialog);return a.modal?(0,j.jsx)(It,{present:r||a.open,children:(0,j.jsx)(Er,{...i,ref:t})}):null});wr.displayName=Cr;var Tr=Ue(\`DialogOverlay.RemoveScroll\`),Er=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Cr,n),a=De(t,pt());return(0,j.jsx)(tr,{as:Tr,allowPinchZoom:!0,shards:[i.contentRef],children:(0,j.jsx)(tt.div,{"data-state":Rr(i.open),...r,ref:a,style:{pointerEvents:\`auto\`,...r.style}})})}),Dr=\`DialogContent\`,Or=_.forwardRef((e,t)=>{let n=xr(Dr,e.__scopeDialog),{forceMount:r=n.forceMount,...i}=e,a=hr(Dr,e.__scopeDialog);return(0,j.jsx)(It,{present:r||a.open,children:a.modal?(0,j.jsx)(kr,{...i,ref:t}):(0,j.jsx)(Ar,{...i,ref:t})})});Or.displayName=Dr;var kr=_.forwardRef((e,t)=>{let n=hr(Dr,e.__scopeDialog),r=_.useRef(null),i=De(t,n.contentRef,r);return _.useEffect(()=>{let e=r.current;if(e)return ur(e)},[]),(0,j.jsx)(jr,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:n.open,onCloseAutoFocus:we(e.onCloseAutoFocus,e=>{e.preventDefault(),n.triggerRef.current?.focus()}),onPointerDownOutside:we(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0;(t.button===2||n)&&e.preventDefault()}),onFocusOutside:we(e.onFocusOutside,e=>e.preventDefault())})}),Ar=_.forwardRef((e,t)=>{let n=hr(Dr,e.__scopeDialog),r=_.useRef(!1),i=_.useRef(!1);return(0,j.jsx)(jr,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===\`pointerdown\`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===\`focusin\`&&i.current&&t.preventDefault()}})}),jr=_.forwardRef((e,t)=>{let{__scopeDialog:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,...o}=e,s=hr(Dr,n);return Wt(),(0,j.jsx)(j.Fragment,{children:(0,j.jsx)(St,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,j.jsx)(ut,{role:\`dialog\`,id:s.contentId,"aria-describedby":s.descriptionId,"aria-labelledby":s.titleId,"data-state":Rr(s.open),...o,ref:t,deferPointerDownOutside:!0,onDismiss:()=>s.onOpenChange(!1)})})})}),Mr=\`DialogTitle\`,Nr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Mr,n);return(0,j.jsx)(tt.h2,{id:i.titleId,...r,ref:t})});Nr.displayName=Mr;var Pr=\`DialogDescription\`,Fr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Pr,n);return(0,j.jsx)(tt.p,{id:i.descriptionId,...r,ref:t})});Fr.displayName=Pr;var Ir=\`DialogClose\`,Lr=_.forwardRef((e,t)=>{let{__scopeDialog:n,...r}=e,i=hr(Ir,n);return(0,j.jsx)(tt.button,{type:\`button\`,...r,ref:t,onClick:we(e.onClick,()=>i.onOpenChange(!1))})});Lr.displayName=Ir;function Rr(e){return e?\`open\`:\`closed\`}function zr(e){var t,n,r=\`\`;if(typeof e==\`string\`||typeof e==\`number\`)r+=e;else if(typeof e==\`object\`)if(Array.isArray(e)){var i=e.length;for(t=0;t<i;t++)e[t]&&(n=zr(e[t]))&&(r&&(r+=\` \`),r+=n)}else for(n in e)e[n]&&(r&&(r+=\` \`),r+=n);return r}function Br(){for(var e,t,n=0,r=\`\`,i=arguments.length;n<i;n++)(e=arguments[n])&&(t=zr(e))&&(r&&(r+=\` \`),r+=t);return r}function N({variant:e=\`default\`,size:t=\`sm\`,className:n,...r}){return(0,j.jsx)(\`button\`,{className:Br(\`inline-flex items-center justify-center gap-1.5 rounded-md transition-colors disabled:opacity-50 disabled:pointer-events-none\`,t===\`sm\`?\`h-8 px-3 text-[13px]\`:\`h-6 px-2 text-xs\`,{default:\`bg-emerald-600 text-white hover:bg-emerald-500 font-medium\`,outline:\`border border-neutral-700 text-neutral-200 hover:bg-neutral-800 hover:border-neutral-600\`,ghost:\`text-neutral-300 hover:bg-neutral-800\`,danger:\`border border-red-900/60 text-red-400 hover:bg-red-950/40\`}[e],n),...r})}function Vr({className:e,...t}){return(0,j.jsx)(\`input\`,{className:Br(\`h-8 w-full rounded-md border border-neutral-700 bg-neutral-900 px-2.5 text-[13px] text-neutral-100\`,\`placeholder:text-neutral-600 focus:border-brand focus:outline-none\`,e),...t})}function Hr({className:e,...t}){return(0,j.jsx)(\`textarea\`,{className:Br(\`w-full rounded-md border border-neutral-700 bg-neutral-900 p-3 font-mono text-[13px] text-neutral-100\`,\`placeholder:text-neutral-600 focus:border-brand focus:outline-none\`,e),...t})}function Ur({children:e}){return(0,j.jsx)(\`label\`,{className:\`mb-1 block text-xs font-medium text-neutral-400\`,children:e})}function Wr({open:e,onClose:t,title:n,children:r,wide:i}){return(0,j.jsx)(gr,{open:e,onOpenChange:e=>!e&&t(),children:(0,j.jsxs)(Sr,{children:[(0,j.jsx)(wr,{className:\`fixed inset-0 z-50 bg-black/60 backdrop-blur-sm\`}),(0,j.jsxs)(Or,{className:Br(\`fixed left-1/2 top-1/2 z-50 -translate-x-1/2 -translate-y-1/2 rounded-lg border border-neutral-700 bg-neutral-850 bg-[#1f1f1f] shadow-2xl\`,\`max-h-[85vh] overflow-auto\`,i?\`w-[640px]\`:\`w-[440px]\`),children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between border-b border-neutral-800 px-4 py-3\`,children:[(0,j.jsx)(Nr,{className:\`text-sm font-semibold\`,children:n}),(0,j.jsx)(Lr,{className:\`text-neutral-500 hover:text-neutral-200\`,children:(0,j.jsx)(ve,{size:16})})]}),(0,j.jsx)(\`div\`,{className:\`p-4\`,children:r})]})]})})}function Gr(){return(0,j.jsx)(\`div\`,{className:\`flex items-center justify-center py-16 text-neutral-500\`,children:(0,j.jsx)(\`div\`,{className:\`size-5 animate-spin rounded-full border-2 border-neutral-700 border-t-brand\`})})}function Kr({children:e}){return(0,j.jsx)(\`div\`,{className:\`py-16 text-center text-[13px] text-neutral-500\`,children:e})}var qr=50;function Jr(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(0),[c,l]=(0,_.useState)(0),[u,d]=(0,_.useState)(!0),[f,p]=(0,_.useState)(null),[m,h]=(0,_.useState)(!1),[g,v]=(0,_.useState)(\`\`),y=e.find(e=>e.name===n)||null,b=(0,_.useCallback)(async()=>{let e=await A.tables();t(e),r(t=>t??e[0]?.name??null)},[]);(0,_.useEffect)(()=>{b().finally(()=>d(!1))},[b]);let x=(0,_.useCallback)(async()=>{if(!n||!y)return;let e=y.primaryKey[0]?\`\${y.primaryKey[0]}.asc\`:void 0,{rows:t,total:r}=await A.rows(n,{limit:qr,offset:c*qr,order:e});a(t),s(r)},[n,c,y]);(0,_.useEffect)(()=>{x()},[x]);function ee(e){let t={};for(let n of y.primaryKey)t[n]=e[n];return t}async function S(e){if(!y?.primaryKey.length)return alert(\`Cannot delete: table has no primary key\`);if(confirm(\`Delete this row?\`))try{await A.deleteRow(n,ee(e)),await x(),await b()}catch(e){alert(e.message)}}return u?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex w-60 shrink-0 flex-col border-r border-neutral-800 bg-[#191919]\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between px-3 py-2.5 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:[\`Tables \`,(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:e.length})]}),(0,j.jsxs)(\`div\`,{className:\`flex-1 overflow-auto\`,children:[e.map(e=>(0,j.jsxs)(\`button\`,{onClick:()=>{r(e.name),l(0)},className:\`flex w-full items-center justify-between px-3 py-1.5 text-left text-[13px] \`+(n===e.name?\`bg-neutral-800 text-neutral-100\`:\`text-neutral-400 hover:bg-neutral-850 hover:bg-neutral-800/50\`),children:[(0,j.jsx)(\`span\`,{className:\`truncate font-mono\`,children:e.name}),(0,j.jsx)(\`span\`,{className:\`text-[11px] text-neutral-600\`,children:e.rowCount})]},e.name)),e.length===0&&(0,j.jsx)(Kr,{children:\`No tables. Create one in the SQL Editor.\`})]})]}),(0,j.jsx)(\`div\`,{className:\`flex min-w-0 flex-1 flex-col\`,children:y?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-sm\`,children:y.name}),(0,j.jsxs)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:[o,\` rows\`]}),(0,j.jsxs)(\`div\`,{className:\`ml-auto flex items-center gap-1.5\`,children:[(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,onClick:()=>void x(),children:(0,j.jsx)(fe,{size:13})}),(0,j.jsxs)(N,{size:\`xs\`,onClick:()=>h(!0),children:[(0,j.jsx)(de,{size:13}),\` Insert\`]})]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 z-10 bg-[#1c1c1c]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800\`,children:[(0,j.jsx)(\`th\`,{className:\`w-16 px-2 py-1.5\`}),y.columns.map(e=>(0,j.jsxs)(\`th\`,{className:\`whitespace-nowrap px-3 py-1.5 text-left font-medium text-neutral-400\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-neutral-200\`,children:e.name}),e.isPrimaryKey&&(0,j.jsx)(\`span\`,{className:\`ml-1 text-[10px] text-brand\`,children:\`PK\`}),(0,j.jsx)(\`span\`,{className:\`ml-1.5 text-[11px] font-normal text-neutral-600\`,children:e.type})]},e.name))]})}),(0,j.jsx)(\`tbody\`,{children:i.map((e,t)=>(0,j.jsxs)(\`tr\`,{className:\`group border-b border-neutral-850 border-neutral-800/60 hover:bg-neutral-800/30\`,children:[(0,j.jsx)(\`td\`,{className:\`px-2\`,children:(0,j.jsxs)(\`div\`,{className:\`flex gap-0.5 opacity-0 group-hover:opacity-100\`,children:[(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-brand\`,onClick:()=>p(e),children:(0,j.jsx)(T,{size:13})}),(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-red-400\`,onClick:()=>S(e),children:(0,j.jsx)(ge,{size:13})})]})}),y.columns.map(t=>(0,j.jsx)(\`td\`,{className:\`max-w-[320px] truncate px-3 py-1.5 font-mono text-neutral-300\`,children:(0,j.jsx)(Yr,{value:e[t.name]})},t.name))]},t))})]}),i.length===0&&(0,j.jsx)(Kr,{children:\`No rows yet.\`})]}),(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-t border-neutral-800 px-4 py-2 text-xs text-neutral-500\`,children:[(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,disabled:c===0,onClick:()=>l(e=>e-1),children:(0,j.jsx)(ie,{size:13})}),(0,j.jsxs)(\`span\`,{children:[o===0?0:c*qr+1,\`–\`,Math.min((c+1)*qr,o),\` of \`,o]}),(0,j.jsx)(N,{variant:\`ghost\`,size:\`xs\`,disabled:(c+1)*qr>=o,onClick:()=>l(e=>e+1),children:(0,j.jsx)(ae,{size:13})})]})]}):(0,j.jsx)(Kr,{children:\`Select a table.\`})}),f&&y&&(0,j.jsx)(Xr,{table:y,initial:f,onClose:()=>p(null),onSave:async e=>{await A.updateRow(n,ee(f),e),p(null),await x()}}),m&&y&&(0,j.jsx)(Xr,{table:y,initial:{},isNew:!0,onClose:()=>h(!1),onSave:async e=>{await A.insertRow(n,e),h(!1),await x(),await b()}}),g&&(0,j.jsx)(\`p\`,{className:\`text-red-400\`,children:g})]})}function Yr({value:e}){return e==null?(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:\`NULL\`}):typeof e==\`boolean\`?(0,j.jsx)(\`span\`,{className:\`text-blue-400\`,children:String(e)}):typeof e==\`object\`?(0,j.jsx)(\`span\`,{className:\`text-amber-300/80\`,children:JSON.stringify(e)}):(0,j.jsx)(j.Fragment,{children:String(e)})}function Xr({table:e,initial:t,isNew:n,onClose:r,onSave:i}){let a=e.columns.filter(e=>!(n&&e.isPrimaryKey&&e.hasDefault)),[o,s]=(0,_.useState)(()=>{let e={};for(let n of a){let r=t[n.name];e[n.name]=r==null?\`\`:typeof r==\`object\`?JSON.stringify(r):String(r)}return e}),[c,l]=(0,_.useState)({}),[u,d]=(0,_.useState)(\`\`),[f,p]=(0,_.useState)(!1);async function m(){p(!0),d(\`\`);let e={};for(let t of a){if(!n&&!c[t.name])continue;let r=o[t.name];if(r===\`\`&&(t.nullable||t.hasDefault)){n||(e[t.name]=null);continue}e[t.name]=Zr(r,t.type)}try{await i(e)}catch(e){d(e.message),p(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:r,title:n?\`Insert into \${e.name}\`:\`Edit row in \${e.name}\`,wide:!0,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[a.map(e=>(0,j.jsxs)(\`div\`,{children:[(0,j.jsxs)(Ur,{children:[e.name,(0,j.jsxs)(\`span\`,{className:\`ml-1.5 font-normal text-neutral-600\`,children:[e.type,e.isPrimaryKey?\` · PK\`:\`\`,!e.nullable&&!e.hasDefault?\` · required\`:\`\`]})]}),(0,j.jsx)(Vr,{value:o[e.name]??\`\`,placeholder:e.hasDefault?\`default\`:e.nullable?\`NULL\`:\`\`,onChange:t=>{s(n=>({...n,[e.name]:t.target.value})),l(t=>({...t,[e.name]:!0}))}})]},e.name)),u&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:u}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2 pt-1\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:r,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:m,disabled:f,children:f?\`Saving…\`:n?\`Insert\`:\`Save\`})]})]})})}function Zr(e,t){if(t===\`bool\`)return e===\`true\`||e===\`t\`||e===\`1\`;if([\`int2\`,\`int4\`,\`int8\`,\`float4\`,\`float8\`,\`numeric\`].includes(t)){let t=Number(e);return Number.isNaN(t)?e:t}if(t===\`json\`||t===\`jsonb\`)try{return JSON.parse(e)}catch{return e}if(t.startsWith(\`_\`))try{return JSON.parse(e)}catch{return e}return e}function Qr(){let[e,t]=(0,_.useState)(\`select * from \`),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)(!1);async function o(){a(!0),r(await A.sql(e)),a(!1)}let s=n?.rows?.[0]?Object.keys(n.rows[0]):[];return(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`border-b border-neutral-800 p-3\`,children:[(0,j.jsx)(Hr,{className:\`h-40\`,value:e,onChange:e=>t(e.target.value),onKeyDown:e=>(e.metaKey||e.ctrlKey)&&e.key===\`Enter\`&&o(),spellCheck:!1,placeholder:\`select * from … (⌘⏎ to run)\`}),(0,j.jsxs)(\`div\`,{className:\`mt-2 flex items-center gap-3\`,children:[(0,j.jsxs)(N,{onClick:o,disabled:i,children:[(0,j.jsx)(E,{size:13}),\` Run\`]}),(0,j.jsx)(\`span\`,{className:\`text-xs text-neutral-500\`,children:\`⌘⏎\`}),n&&(0,j.jsx)(\`span\`,{className:\`ml-auto text-xs text-neutral-500\`,children:n.ok?\`\${n.rowCount} rows · \${n.ms} ms\${n.affectedRows==null?\`\`:\` · \${n.affectedRows} affected\`}\`:\`\`})]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[!n&&(0,j.jsx)(Kr,{children:\`Run a query to see results.\`}),n&&!n.ok&&(0,j.jsxs)(\`div\`,{className:\`m-4 rounded-md border border-red-900/50 bg-red-950/30 p-3 text-[13px] text-red-300\`,children:[(0,j.jsx)(\`div\`,{className:\`font-mono\`,children:n.error}),n.code&&(0,j.jsxs)(\`div\`,{className:\`mt-1 text-xs text-red-400/70\`,children:[\`SQLSTATE \`,n.code]}),n.hint&&(0,j.jsxs)(\`div\`,{className:\`mt-1 text-xs text-neutral-400\`,children:[\`Hint: \`,n.hint]})]}),n?.ok&&s.length>0&&(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 bg-[#1c1c1c]\`,children:(0,j.jsx)(\`tr\`,{className:\`border-b border-neutral-800\`,children:s.map(e=>(0,j.jsx)(\`th\`,{className:\`whitespace-nowrap px-3 py-1.5 text-left font-mono font-medium text-neutral-300\`,children:e},e))})}),(0,j.jsx)(\`tbody\`,{children:n.rows.map((e,t)=>(0,j.jsx)(\`tr\`,{className:\`border-b border-neutral-800/60 hover:bg-neutral-800/30\`,children:s.map(t=>(0,j.jsx)(\`td\`,{className:\`max-w-[360px] truncate px-3 py-1.5 font-mono text-neutral-300\`,children:$r(e[t])},t))},t))})]}),n?.ok&&s.length===0&&(0,j.jsxs)(Kr,{children:[\`Statement executed\`,n.affectedRows==null?\`\`:\` · \${n.affectedRows} rows affected\`,\`.\`]})]})]})}function $r(e){return e==null?(0,j.jsx)(\`span\`,{className:\`text-neutral-600\`,children:\`NULL\`}):typeof e==\`object\`?JSON.stringify(e):String(e)}function ei(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(!0),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)(null);async function c(){t(await A.users())}(0,_.useEffect)(()=>{c().finally(()=>r(!1))},[]);async function l(e){confirm(\`Delete \${e.email||e.id}?\`)&&(await A.deleteUser(e.id),await c())}return n?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`text-sm font-semibold\`,children:\`Users\`}),(0,j.jsx)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:e.length}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>a(!0),children:[(0,j.jsx)(de,{size:13}),\` Add user\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`sticky top-0 bg-[#1c1c1c]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-1.5 font-medium\`,children:\`Email\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`UID\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Provider\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Created\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 font-medium\`,children:\`Last sign in\`}),(0,j.jsx)(\`th\`,{className:\`w-20\`})]})}),(0,j.jsx)(\`tbody\`,{children:e.map(e=>(0,j.jsxs)(\`tr\`,{className:\`group border-b border-neutral-800/60 hover:bg-neutral-800/30\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5\`,children:e.email||(0,j.jsx)(\`span\`,{className:\`text-neutral-500\`,children:e.is_anonymous?\`anonymous\`:\`—\`})}),(0,j.jsxs)(\`td\`,{className:\`px-3 py-1.5 font-mono text-[11px] text-neutral-500\`,children:[e.id.slice(0,8),\`…\`]}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:e.app_metadata?.provider||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:(e.created_at||\`\`).slice(0,10)}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:(e.last_sign_in_at||\`\`).slice(0,16).replace(\`T\`,\` \`)}),(0,j.jsx)(\`td\`,{className:\`px-3\`,children:(0,j.jsxs)(\`div\`,{className:\`flex gap-0.5 opacity-0 group-hover:opacity-100\`,children:[(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-brand\`,title:\`Reset password\`,onClick:()=>s(e),children:(0,j.jsx)(ue,{size:13})}),(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 hover:text-red-400\`,onClick:()=>l(e),children:(0,j.jsx)(ge,{size:13})})]})})]},e.id))})]}),e.length===0&&(0,j.jsx)(Kr,{children:\`No users yet.\`})]}),i&&(0,j.jsx)(ti,{onClose:()=>a(!1),onDone:async()=>{a(!1),await c()}}),o&&(0,j.jsx)(ni,{user:o,onClose:()=>s(null),onDone:async()=>{s(null),await c()}})]})}function ti({onClose:e,onDone:t}){let[n,r]=(0,_.useState)(\`\`),[i,a]=(0,_.useState)(\`\`),[o,s]=(0,_.useState)(\`\`),[c,l]=(0,_.useState)(!1);async function u(){l(!0),s(\`\`);try{await A.createUser({email:n,password:i||void 0,email_confirm:!0}),t()}catch(e){s(e.message),l(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`Add user\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Email\`}),(0,j.jsx)(Vr,{value:n,onChange:e=>r(e.target.value),placeholder:\`user@example.com\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Password (optional)\`}),(0,j.jsx)(Vr,{type:\`password\`,value:i,onChange:e=>a(e.target.value)})]}),o&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:o}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:u,disabled:c||!n,children:c?\`Creating…\`:\`Create\`})]})]})})}function ni({user:e,onClose:t,onDone:n}){let[r,i]=(0,_.useState)(\`\`),[a,o]=(0,_.useState)(\`\`),[s,c]=(0,_.useState)(!1);async function l(){c(!0),o(\`\`);try{await A.updateUser(e.id,{password:r}),n()}catch(e){o(e.message),c(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:t,title:\`Reset password · \${e.email||e.id}\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`New password\`}),(0,j.jsx)(Vr,{type:\`password\`,value:r,onChange:e=>i(e.target.value)})]}),a&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:a}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:t,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:l,disabled:s||r.length<6,children:s?\`Saving…\`:\`Update\`})]})]})})}function ri(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(null),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)(!0),[c,l]=(0,_.useState)(!1),u=(0,_.useRef)(null);async function d(){let e=await A.buckets();t(e),r(t=>t??e[0]?.id??null)}async function f(e){a(await A.listObjects(e))}(0,_.useEffect)(()=>{d().finally(()=>s(!1))},[]),(0,_.useEffect)(()=>{n&&f(n)},[n]);async function p(e){n&&(await A.uploadObject(n,e.name,e),await f(n))}async function m(e){!n||!confirm(\`Delete \${e}?\`)||(await A.removeObject(n,e),await f(n))}async function h(e){if(confirm(\`Delete bucket "\${e}"? It must be empty.\`))try{await A.deleteBucket(e),r(null),await d()}catch(e){alert(e.message)}}return o?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex w-60 shrink-0 flex-col border-r border-neutral-800 bg-[#191919]\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between px-3 py-2.5 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:[\`Buckets\`,(0,j.jsx)(\`button\`,{className:\`text-neutral-400 hover:text-brand\`,onClick:()=>l(!0),children:(0,j.jsx)(de,{size:14})})]}),(0,j.jsxs)(\`div\`,{className:\`flex-1 overflow-auto\`,children:[e.map(e=>(0,j.jsxs)(\`button\`,{onClick:()=>r(e.id),className:\`group flex w-full items-center justify-between px-3 py-1.5 text-left text-[13px] \`+(n===e.id?\`bg-neutral-800 text-neutral-100\`:\`text-neutral-400 hover:bg-neutral-800/50\`),children:[(0,j.jsxs)(\`span\`,{className:\`truncate\`,children:[e.id,e.public&&(0,j.jsx)(\`span\`,{className:\`ml-1.5 text-[10px] text-brand\`,children:\`public\`})]}),(0,j.jsx)(ge,{size:13,className:\`opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:t=>{t.stopPropagation(),h(e.id)}})]},e.id)),e.length===0&&(0,j.jsx)(Kr,{children:\`No buckets.\`})]})]}),(0,j.jsx)(\`div\`,{className:\`flex min-w-0 flex-1 flex-col\`,children:n?(0,j.jsxs)(j.Fragment,{children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(\`span\`,{className:\`font-mono text-sm\`,children:n}),(0,j.jsx)(\`input\`,{ref:u,type:\`file\`,className:\`hidden\`,onChange:e=>e.target.files?.[0]&&p(e.target.files[0])}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>u.current?.click(),children:[(0,j.jsx)(_e,{size:13}),\` Upload\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-2\`,children:[i.map(e=>(0,j.jsxs)(\`div\`,{className:\`group flex items-center gap-2 rounded-md px-2 py-1.5 text-[13px] hover:bg-neutral-800/40\`,children:[(0,j.jsx)(se,{size:14,className:\`text-neutral-500\`}),(0,j.jsx)(\`span\`,{className:\`font-mono\`,children:e.name}),e.metadata?.size!=null&&(0,j.jsx)(\`span\`,{className:\`text-[11px] text-neutral-500\`,children:ai(e.metadata.size)}),(0,j.jsx)(\`button\`,{className:\`ml-auto p-1 text-neutral-500 opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:()=>m(e.name),children:(0,j.jsx)(ge,{size:13})})]},e.name)),i.length===0&&(0,j.jsx)(Kr,{children:\`Empty bucket. Upload a file.\`})]})]}):(0,j.jsx)(Kr,{children:\`Select or create a bucket.\`})}),c&&(0,j.jsx)(ii,{onClose:()=>l(!1),onDone:async()=>{l(!1),await d()}})]})}function ii({onClose:e,onDone:t}){let[n,r]=(0,_.useState)(\`\`),[i,a]=(0,_.useState)(!1),[o,s]=(0,_.useState)(\`\`),[c,l]=(0,_.useState)(!1);async function u(){l(!0),s(\`\`);try{await A.createBucket({id:n,name:n,public:i}),t()}catch(e){s(e.message),l(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`New bucket\`,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Name\`}),(0,j.jsx)(Vr,{value:n,onChange:e=>r(e.target.value),placeholder:\`avatars\`})]}),(0,j.jsxs)(\`label\`,{className:\`flex items-center gap-2 text-[13px] text-neutral-300\`,children:[(0,j.jsx)(\`input\`,{type:\`checkbox\`,checked:i,onChange:e=>a(e.target.checked)}),\`Public bucket (objects readable without auth)\`]}),o&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:o}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:u,disabled:c||!n,children:c?\`Creating…\`:\`Create\`})]})]})})}function ai(e){return e<1024?\`\${e} B\`:e<1024*1024?\`\${(e/1024).toFixed(1)} KB\`:\`\${(e/1024/1024).toFixed(1)} MB\`}function oi(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)([]),[i,a]=(0,_.useState)([]),[o,s]=(0,_.useState)([]),[c,l]=(0,_.useState)(!0);return(0,_.useEffect)(()=>{Promise.all([A.stats(),A.migrations(),A.functions(),A.triggers()]).then(([e,n,i,o])=>{t(e),r(n),a(i),s(o)}).finally(()=>l(!1))},[]),c?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`h-full overflow-auto p-6\`,children:[(0,j.jsx)(\`h1\`,{className:\`text-sm font-semibold text-neutral-300\`,children:\`Database\`}),e&&(0,j.jsxs)(j.Fragment,{children:[(0,j.jsx)(\`p\`,{className:\`mt-1 font-mono text-xs text-neutral-500\`,children:e.version}),(0,j.jsxs)(\`div\`,{className:\`mt-4 grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-6\`,children:[(0,j.jsx)(si,{label:\`Tables\`,value:e.tables}),(0,j.jsx)(si,{label:\`Users\`,value:e.users}),(0,j.jsx)(si,{label:\`Buckets\`,value:e.buckets}),(0,j.jsx)(si,{label:\`Objects\`,value:e.objects}),(0,j.jsx)(si,{label:\`Migrations\`,value:e.migrations}),(0,j.jsx)(si,{label:\`DB size\`,value:e.dbSize})]})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Migrations\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Version\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Applied\`})]})}),(0,j.jsx)(\`tbody\`,{children:n.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.version}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono\`,children:e.name||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:(e.applied_at||\`\`).slice(0,19).replace(\`T\`,\` \`)})]},e.version))})]}),n.length===0&&(0,j.jsx)(Kr,{children:\`No migrations applied.\`})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Functions\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Arguments\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Returns\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Language\`})]})}),(0,j.jsx)(\`tbody\`,{children:i.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-200\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`max-w-[280px] truncate px-4 py-1.5 font-mono text-[11px] text-neutral-500\`,children:e.args||\`—\`}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.returns}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:e.language})]},e.name))})]}),i.length===0&&(0,j.jsx)(Kr,{children:\`No user functions.\`})]}),(0,j.jsx)(\`h2\`,{className:\`mb-2 mt-8 text-xs font-semibold uppercase tracking-wide text-neutral-500\`,children:\`Triggers\`}),(0,j.jsxs)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:[(0,j.jsxs)(\`table\`,{className:\`w-full border-collapse text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919]\`,children:(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800 text-left text-neutral-400\`,children:[(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Name\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Table\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Timing\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Events\`}),(0,j.jsx)(\`th\`,{className:\`px-4 py-2 font-medium\`,children:\`Function\`})]})}),(0,j.jsx)(\`tbody\`,{children:o.map(e=>(0,j.jsxs)(\`tr\`,{className:\`border-b border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-200\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-neutral-400\`,children:e.table}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:e.timing}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 text-neutral-400\`,children:(e.events||[]).join(\`, \`)}),(0,j.jsx)(\`td\`,{className:\`px-4 py-1.5 font-mono text-[11px] text-neutral-500\`,children:e.function})]},e.table+e.name))})]}),o.length===0&&(0,j.jsx)(Kr,{children:\`No triggers.\`})]})]})}function si({label:e,value:t}){return(0,j.jsxs)(\`div\`,{className:\`rounded-lg border border-neutral-800 bg-[#191919] p-3\`,children:[(0,j.jsx)(\`div\`,{className:\`text-lg font-semibold text-neutral-100\`,children:t}),(0,j.jsx)(\`div\`,{className:\`text-xs text-neutral-500\`,children:e})]})}function ci(){let[e,t]=(0,_.useState)([]),[n,r]=(0,_.useState)(!0),[i,a]=(0,_.useState)(!1);async function o(){t(await A.policies())}(0,_.useEffect)(()=>{o().finally(()=>r(!1))},[]);async function s(e){confirm(\`Drop policy "\${e.name}" on \${e.table}?\`)&&(await A.dropPolicy(e.table,e.name),await o())}if(n)return(0,j.jsx)(Gr,{});let c=new Map;for(let t of e)c.has(t.table)||c.set(t.table,[]),c.get(t.table).push(t);return(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2 border-b border-neutral-800 px-4 py-2.5\`,children:[(0,j.jsx)(me,{size:15,className:\`text-brand\`}),(0,j.jsx)(\`span\`,{className:\`text-sm font-semibold\`,children:\`RLS Policies\`}),(0,j.jsx)(\`span\`,{className:\`rounded bg-neutral-800 px-1.5 py-0.5 text-[11px] text-neutral-400\`,children:e.length}),(0,j.jsxs)(N,{size:\`xs\`,className:\`ml-auto\`,onClick:()=>a(!0),children:[(0,j.jsx)(de,{size:13}),\` New policy\`]})]}),(0,j.jsxs)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-4\`,children:[[...c].map(([e,t])=>(0,j.jsxs)(\`div\`,{className:\`mb-5\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-1.5 font-mono text-[13px] text-neutral-300\`,children:e}),(0,j.jsx)(\`div\`,{className:\`overflow-hidden rounded-md border border-neutral-800\`,children:(0,j.jsxs)(\`table\`,{className:\`w-full text-[13px]\`,children:[(0,j.jsx)(\`thead\`,{className:\`bg-[#191919] text-neutral-400\`,children:(0,j.jsxs)(\`tr\`,{children:[(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Policy\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Command\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`Roles\`}),(0,j.jsx)(\`th\`,{className:\`px-3 py-1.5 text-left font-medium\`,children:\`USING / CHECK\`}),(0,j.jsx)(\`th\`,{className:\`w-10\`})]})}),(0,j.jsx)(\`tbody\`,{children:t.map(e=>(0,j.jsxs)(\`tr\`,{className:\`group border-t border-neutral-800/60\`,children:[(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5\`,children:e.name}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 text-neutral-400\`,children:e.cmd}),(0,j.jsx)(\`td\`,{className:\`px-3 py-1.5 font-mono text-[11px] text-neutral-500\`,children:Array.isArray(e.roles)?e.roles.join(\`, \`):e.roles}),(0,j.jsx)(\`td\`,{className:\`max-w-[360px] truncate px-3 py-1.5 font-mono text-[11px] text-amber-300/70\`,children:[e.using_expr&&\`USING \${e.using_expr}\`,e.with_check&&\`CHECK \${e.with_check}\`].filter(Boolean).join(\` · \`)}),(0,j.jsx)(\`td\`,{className:\`px-2\`,children:(0,j.jsx)(\`button\`,{className:\`p-1 text-neutral-500 opacity-0 hover:text-red-400 group-hover:opacity-100\`,onClick:()=>s(e),children:(0,j.jsx)(ge,{size:13})})})]},e.name))})]})})]},e)),e.length===0&&(0,j.jsx)(Kr,{children:\`No policies. Create one, or enable RLS on a table first.\`})]}),i&&(0,j.jsx)(li,{onClose:()=>a(!1),onDone:async()=>{a(!1),await o()}})]})}function li({onClose:e,onDone:t}){let[n,r]=(0,_.useState)({table:\`\`,name:\`\`,command:\`ALL\`,roles:\`authenticated\`,using:\`\`,check:\`\`}),[i,a]=(0,_.useState)(\`\`),[o,s]=(0,_.useState)(!1),c=(e,t)=>r(n=>({...n,[e]:t}));async function l(){s(!0),a(\`\`);try{let e=await A.createPolicy(n);if(e.error)throw Error(e.error+(e.hint?\` (\${e.hint})\`:\`\`));t()}catch(e){a(e.message),s(!1)}}return(0,j.jsx)(Wr,{open:!0,onClose:e,title:\`New RLS policy\`,wide:!0,children:(0,j.jsxs)(\`div\`,{className:\`space-y-3\`,children:[(0,j.jsxs)(\`div\`,{className:\`grid grid-cols-2 gap-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Table\`}),(0,j.jsx)(Vr,{value:n.table,onChange:e=>c(\`table\`,e.target.value),placeholder:\`todos\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Policy name\`}),(0,j.jsx)(Vr,{value:n.name,onChange:e=>c(\`name\`,e.target.value),placeholder:\`own rows\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Command\`}),(0,j.jsx)(\`select\`,{className:\`h-8 w-full rounded-md border border-neutral-700 bg-neutral-900 px-2 text-[13px]\`,value:n.command,onChange:e=>c(\`command\`,e.target.value),children:[\`ALL\`,\`SELECT\`,\`INSERT\`,\`UPDATE\`,\`DELETE\`].map(e=>(0,j.jsx)(\`option\`,{children:e},e))})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`Roles\`}),(0,j.jsx)(Vr,{value:n.roles,onChange:e=>c(\`roles\`,e.target.value),placeholder:\`authenticated\`})]})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`USING expression (read/visibility)\`}),(0,j.jsx)(Vr,{value:n.using,onChange:e=>c(\`using\`,e.target.value),placeholder:\`user_id = auth.uid()\`})]}),(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(Ur,{children:\`WITH CHECK expression (writes)\`}),(0,j.jsx)(Vr,{value:n.check,onChange:e=>c(\`check\`,e.target.value),placeholder:\`user_id = auth.uid()\`})]}),i&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:i}),(0,j.jsxs)(\`div\`,{className:\`flex justify-end gap-2\`,children:[(0,j.jsx)(N,{variant:\`outline\`,onClick:e,children:\`Cancel\`}),(0,j.jsx)(N,{onClick:l,disabled:o||!n.table||!n.name,children:o?\`Creating…\`:\`Create policy\`})]})]})})}var ui={info:\`text-neutral-500\`,warn:\`text-amber-400\`,error:\`text-red-400\`};function di(){let[e,t]=(0,_.useState)(null),[n,r]=(0,_.useState)(!0),i=(0,_.useRef)(null);async function a(){try{t(await A.logs())}catch{}}return(0,_.useEffect)(()=>{if(a(),n)return i.current=setInterval(a,2e3),()=>{i.current&&clearInterval(i.current)}},[n]),e===null?(0,j.jsx)(Gr,{}):(0,j.jsxs)(\`div\`,{className:\`flex h-full flex-col\`,children:[(0,j.jsxs)(\`div\`,{className:\`flex items-center justify-between border-b border-neutral-800 px-6 py-3\`,children:[(0,j.jsxs)(\`div\`,{children:[(0,j.jsx)(\`h1\`,{className:\`text-sm font-semibold text-neutral-300\`,children:\`Logs\`}),(0,j.jsx)(\`p\`,{className:\`text-xs text-neutral-500\`,children:\`Recent requests and server events\`})]}),(0,j.jsxs)(\`div\`,{className:\`flex items-center gap-2\`,children:[(0,j.jsx)(N,{className:n?\`\`:\`opacity-60\`,onClick:()=>r(e=>!e),title:n?\`Pause auto-refresh\`:\`Resume auto-refresh\`,children:n?\`⏸ Live\`:\`▶ Paused\`}),(0,j.jsx)(N,{onClick:a,children:\`Refresh\`}),(0,j.jsx)(N,{onClick:async()=>{await A.clearLogs(),a()},children:\`Clear\`})]})]}),(0,j.jsx)(\`div\`,{className:\`min-h-0 flex-1 overflow-auto p-3 font-mono text-[12.5px] leading-relaxed\`,children:e.length===0?(0,j.jsx)(Kr,{children:\`No logs yet. Make a request or trigger an email/webhook.\`}):e.map((e,t)=>(0,j.jsxs)(\`div\`,{className:\`flex gap-3 whitespace-pre-wrap break-words px-3 py-0.5 hover:bg-neutral-800/40\`,children:[(0,j.jsx)(\`span\`,{className:\`shrink-0 tabular-nums text-neutral-600\`,children:e.ts.slice(11,23)}),(0,j.jsx)(\`span\`,{className:\`shrink-0 uppercase \`+ui[e.level],children:e.level}),(0,j.jsx)(\`span\`,{className:\`text-neutral-300\`,children:e.msg})]},t))})]})}var fi=[{id:\`table\`,label:\`Table Editor\`,icon:he},{id:\`sql\`,label:\`SQL Editor\`,icon:D},{id:\`auth\`,label:\`Authentication\`,icon:ue},{id:\`policies\`,label:\`RLS Policies\`,icon:me},{id:\`storage\`,label:\`Storage\`,icon:le},{id:\`database\`,label:\`Database\`,icon:ce},{id:\`logs\`,label:\`Logs\`,icon:pe}];function pi({size:e=24}){return(0,j.jsxs)(\`svg\`,{width:e,height:e,viewBox:\`0 0 120 120\`,fill:\`none\`,children:[(0,j.jsxs)(\`defs\`,{children:[(0,j.jsxs)(\`linearGradient\`,{id:\`tbLogoBody\`,x1:\`30\`,y1:\`30\`,x2:\`92\`,y2:\`102\`,gradientUnits:\`userSpaceOnUse\`,children:[(0,j.jsx)(\`stop\`,{stopColor:\`#10b981\`}),(0,j.jsx)(\`stop\`,{offset:\`1\`,stopColor:\`#047857\`})]}),(0,j.jsxs)(\`linearGradient\`,{id:\`tbLogoLid\`,x1:\`26\`,y1:\`19\`,x2:\`94\`,y2:\`49\`,gradientUnits:\`userSpaceOnUse\`,children:[(0,j.jsx)(\`stop\`,{stopColor:\`#6ee7b7\`}),(0,j.jsx)(\`stop\`,{offset:\`1\`,stopColor:\`#34d399\`})]})]}),(0,j.jsx)(\`path\`,{d:\`M26 34v52c0 8.4 15.2 15.2 34 15.2s34-6.8 34-15.2V34Z\`,fill:\`url(#tbLogoBody)\`}),(0,j.jsx)(\`path\`,{d:\`M26 60c0 8.4 15.2 15.2 34 15.2S94 68.4 94 60\`,stroke:\`#6ee7b7\`,strokeWidth:\`3\`,fill:\`none\`}),(0,j.jsx)(\`ellipse\`,{cx:\`60\`,cy:\`34\`,rx:\`34\`,ry:\`15\`,fill:\`url(#tbLogoLid)\`}),(0,j.jsx)(\`ellipse\`,{cx:\`60\`,cy:\`34\`,rx:\`25\`,ry:\`10\`,fill:\`none\`,stroke:\`#059669\`,strokeOpacity:\`0.5\`,strokeWidth:\`2.5\`})]})}function mi(){let[e,t]=(0,_.useState)(!!be()),[n,r]=(0,_.useState)(\`table\`);return(0,_.useEffect)(()=>{be()&&A.ping().catch(()=>{Se(),t(!1)})},[]),e?(0,j.jsxs)(\`div\`,{className:\`flex h-full\`,children:[(0,j.jsxs)(\`nav\`,{className:\`flex w-14 shrink-0 flex-col items-center gap-1 border-r border-neutral-800 bg-[#171717] py-3\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-3\`,children:(0,j.jsx)(pi,{})}),fi.map(e=>{let t=e.icon,i=n===e.id;return(0,j.jsxs)(\`button\`,{title:e.label,onClick:()=>r(e.id),className:\`group relative flex size-9 items-center justify-center rounded-md transition-colors \`+(i?\`bg-neutral-800 text-brand\`:\`text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200\`),children:[(0,j.jsx)(t,{size:18}),(0,j.jsx)(\`span\`,{className:\`pointer-events-none absolute left-11 z-20 hidden whitespace-nowrap rounded-md border border-neutral-700 bg-neutral-900 px-2 py-1 text-xs text-neutral-200 group-hover:block\`,children:e.label})]},e.id)}),(0,j.jsx)(\`button\`,{title:\`Log out\`,onClick:()=>{Se(),t(!1)},className:\`mt-auto flex size-9 items-center justify-center rounded-md text-neutral-600 hover:bg-neutral-800 hover:text-neutral-300\`,children:(0,j.jsx)(oe,{size:18})})]}),(0,j.jsxs)(\`main\`,{className:\`min-w-0 flex-1\`,children:[n===\`table\`&&(0,j.jsx)(Jr,{}),n===\`sql\`&&(0,j.jsx)(Qr,{}),n===\`auth\`&&(0,j.jsx)(ei,{}),n===\`policies\`&&(0,j.jsx)(ci,{}),n===\`storage\`&&(0,j.jsx)(ri,{}),n===\`database\`&&(0,j.jsx)(oi,{}),n===\`logs\`&&(0,j.jsx)(di,{})]})]}):(0,j.jsx)(hi,{onOk:()=>t(!0)})}function hi({onOk:e}){let[t,n]=(0,_.useState)(\`\`),[r,i]=(0,_.useState)(\`\`),[a,o]=(0,_.useState)(!1);async function s(){o(!0),i(\`\`),xe(t.trim());try{await A.ping(),e()}catch(e){Se(),i(\`Invalid service_role key: \`+e.message)}finally{o(!1)}}return(0,j.jsx)(\`div\`,{className:\`flex h-full items-center justify-center\`,children:(0,j.jsxs)(\`div\`,{className:\`w-[400px] text-center\`,children:[(0,j.jsx)(\`div\`,{className:\`mb-4 flex justify-center\`,children:(0,j.jsx)(pi,{size:48})}),(0,j.jsx)(\`h1\`,{className:\`text-lg font-semibold\`,children:\`tinbase studio\`}),(0,j.jsxs)(\`p\`,{className:\`mt-1 text-[13px] text-neutral-500\`,children:[\`Sign in with the \`,(0,j.jsx)(\`span\`,{className:\`text-neutral-300\`,children:\`service_role\`}),\` key printed when tinbase started.\`]}),(0,j.jsxs)(\`div\`,{className:\`mt-5 space-y-3 text-left\`,children:[(0,j.jsx)(Vr,{type:\`password\`,placeholder:\`service_role key (eyJ…)\`,value:t,onChange:e=>n(e.target.value),onKeyDown:e=>e.key===\`Enter\`&&s()}),(0,j.jsx)(N,{className:\`w-full\`,onClick:s,disabled:a||!t.trim(),children:a?\`Checking…\`:\`Continue\`}),r&&(0,j.jsx)(\`p\`,{className:\`text-xs text-red-400\`,children:r})]})]})})}(0,v.createRoot)(document.getElementById(\`root\`)).render((0,j.jsx)(_.StrictMode,{children:(0,j.jsx)(mi,{})}));</script>
|
|
65
65
|
<style rel="stylesheet" crossorigin>/*! tailwindcss v4.3.2 | MIT License | https://tailwindcss.com */
|
|
66
66
|
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, -apple-system, sans-serif;--font-mono:ui-monospace, "SF Mono", Menlo, monospace;--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-900:oklch(39.6% .141 25.723);--color-red-950:oklch(25.8% .092 26.042);--color-amber-300:oklch(87.9% .169 91.605);--color-amber-400:oklch(82.8% .189 84.429);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--color-blue-400:oklch(70.7% .165 254.624);--color-neutral-100:oklch(97% 0 0);--color-neutral-200:oklch(92.2% 0 0);--color-neutral-300:oklch(87% 0 0);--color-neutral-400:oklch(70.8% 0 0);--color-neutral-500:oklch(55.6% 0 0);--color-neutral-600:oklch(43.9% 0 0);--color-neutral-700:oklch(37.1% 0 0);--color-neutral-800:oklch(26.9% 0 0);--color-neutral-900:oklch(20.5% 0 0);--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--animate-spin:spin 1s linear infinite;--blur-sm:8px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-brand:#3ecf8e}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.top-1\\/2{top:50%}.left-1\\/2{left:50%}.left-11{left:calc(var(--spacing) * 11)}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.m-4{margin:calc(var(--spacing) * 4)}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:var(--spacing)}.mb-1\\.5{margin-bottom:calc(var(--spacing) * 1.5)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.ml-1{margin-left:var(--spacing)}.ml-1\\.5{margin-left:calc(var(--spacing) * 1.5)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-40{height:calc(var(--spacing) * 40)}.h-full{height:100%}.max-h-\\[85vh\\]{max-height:85vh}.min-h-0{min-height:0}.w-10{width:calc(var(--spacing) * 10)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-60{width:calc(var(--spacing) * 60)}.w-\\[400px\\]{width:400px}.w-\\[440px\\]{width:440px}.w-\\[640px\\]{width:640px}.w-full{width:100%}.max-w-\\[280px\\]{max-width:280px}.max-w-\\[320px\\]{max-width:320px}.max-w-\\[360px\\]{max-width:360px}.min-w-0{min-width:0}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-x-1\\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.animate-spin{animation:var(--animate-spin)}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.gap-0\\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-neutral-700{border-color:var(--color-neutral-700)}.border-neutral-800{border-color:var(--color-neutral-800)}.border-neutral-800\\/60{border-color:#26262699}@supports (color:color-mix(in lab, red, red)){.border-neutral-800\\/60{border-color:color-mix(in oklab, var(--color-neutral-800) 60%, transparent)}}.border-red-900\\/50{border-color:#82181a80}@supports (color:color-mix(in lab, red, red)){.border-red-900\\/50{border-color:color-mix(in oklab, var(--color-red-900) 50%, transparent)}}.border-red-900\\/60{border-color:#82181a99}@supports (color:color-mix(in lab, red, red)){.border-red-900\\/60{border-color:color-mix(in oklab, var(--color-red-900) 60%, transparent)}}.border-t-brand{border-top-color:var(--color-brand)}.bg-\\[\\#1c1c1c\\]{background-color:#1c1c1c}.bg-\\[\\#1f1f1f\\]{background-color:#1f1f1f}.bg-\\[\\#171717\\]{background-color:#171717}.bg-\\[\\#191919\\]{background-color:#191919}.bg-black\\/60{background-color:#0009}@supports (color:color-mix(in lab, red, red)){.bg-black\\/60{background-color:color-mix(in oklab, var(--color-black) 60%, transparent)}}.bg-emerald-600{background-color:var(--color-emerald-600)}.bg-neutral-800{background-color:var(--color-neutral-800)}.bg-neutral-900{background-color:var(--color-neutral-900)}.bg-red-950\\/30{background-color:#4608094d}@supports (color:color-mix(in lab, red, red)){.bg-red-950\\/30{background-color:color-mix(in oklab, var(--color-red-950) 30%, transparent)}}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-1\\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-6{padding-inline:calc(var(--spacing) * 6)}.py-0\\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-16{padding-block:calc(var(--spacing) * 16)}.pt-1{padding-top:var(--spacing)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\\[10px\\]{font-size:10px}.text-\\[11px\\]{font-size:11px}.text-\\[12\\.5px\\]{font-size:12.5px}.text-\\[13px\\]{font-size:13px}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-amber-300\\/70{color:#ffd236b3}@supports (color:color-mix(in lab, red, red)){.text-amber-300\\/70{color:color-mix(in oklab, var(--color-amber-300) 70%, transparent)}}.text-amber-300\\/80{color:#ffd236cc}@supports (color:color-mix(in lab, red, red)){.text-amber-300\\/80{color:color-mix(in oklab, var(--color-amber-300) 80%, transparent)}}.text-amber-400{color:var(--color-amber-400)}.text-blue-400{color:var(--color-blue-400)}.text-brand{color:var(--color-brand)}.text-neutral-100{color:var(--color-neutral-100)}.text-neutral-200{color:var(--color-neutral-200)}.text-neutral-300{color:var(--color-neutral-300)}.text-neutral-400{color:var(--color-neutral-400)}.text-neutral-500{color:var(--color-neutral-500)}.text-neutral-600{color:var(--color-neutral-600)}.text-red-300{color:var(--color-red-300)}.text-red-400{color:var(--color-red-400)}.text-red-400\\/70{color:#ff6568b3}@supports (color:color-mix(in lab, red, red)){.text-red-400\\/70{color:color-mix(in oklab, var(--color-red-400) 70%, transparent)}}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.opacity-0{opacity:0}.opacity-60{opacity:.6}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.backdrop-blur-sm{--tw-backdrop-blur:blur(var(--blur-sm));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}@media (hover:hover){.group-hover\\:block:is(:where(.group):hover *){display:block}.group-hover\\:opacity-100:is(:where(.group):hover *){opacity:1}}.placeholder\\:text-neutral-600::placeholder{color:var(--color-neutral-600)}@media (hover:hover){.hover\\:border-neutral-600:hover{border-color:var(--color-neutral-600)}.hover\\:bg-emerald-500:hover{background-color:var(--color-emerald-500)}.hover\\:bg-neutral-800:hover{background-color:var(--color-neutral-800)}.hover\\:bg-neutral-800\\/30:hover{background-color:#2626264d}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-neutral-800\\/30:hover{background-color:color-mix(in oklab, var(--color-neutral-800) 30%, transparent)}}.hover\\:bg-neutral-800\\/40:hover{background-color:#26262666}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-neutral-800\\/40:hover{background-color:color-mix(in oklab, var(--color-neutral-800) 40%, transparent)}}.hover\\:bg-neutral-800\\/50:hover{background-color:#26262680}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-neutral-800\\/50:hover{background-color:color-mix(in oklab, var(--color-neutral-800) 50%, transparent)}}.hover\\:bg-red-950\\/40:hover{background-color:#46080966}@supports (color:color-mix(in lab, red, red)){.hover\\:bg-red-950\\/40:hover{background-color:color-mix(in oklab, var(--color-red-950) 40%, transparent)}}.hover\\:text-brand:hover{color:var(--color-brand)}.hover\\:text-neutral-200:hover{color:var(--color-neutral-200)}.hover\\:text-neutral-300:hover{color:var(--color-neutral-300)}.hover\\:text-red-400:hover{color:var(--color-red-400)}}.focus\\:border-brand:focus{border-color:var(--color-brand)}.focus\\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\\:pointer-events-none:disabled{pointer-events:none}.disabled\\:opacity-50:disabled{opacity:.5}@media (width>=40rem){.sm\\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}}@media (width>=64rem){.lg\\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}}}:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}html,body,#root{height:100%}body{color:#ededed;-webkit-font-smoothing:antialiased;background:#1c1c1c;margin:0;font-size:13px}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:#3a3a3a;border:2px solid #1c1c1c;border-radius:6px}::-webkit-scrollbar-thumb:hover{background:#4a4a4a}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}
|
|
67
67
|
/*$vite$:1*/</style>
|
package/dist/auth/handler.d.ts
CHANGED
|
@@ -44,10 +44,35 @@ export declare class AuthHandler {
|
|
|
44
44
|
private issueToken;
|
|
45
45
|
private sendOtp;
|
|
46
46
|
private sendRecovery;
|
|
47
|
+
/** Max wrong guesses for a one-time code before its tokens are invalidated. */
|
|
48
|
+
private static readonly MAX_OTP_ATTEMPTS;
|
|
47
49
|
private redeem;
|
|
48
50
|
private verifyToken;
|
|
49
51
|
private verifyLink;
|
|
50
52
|
private admin;
|
|
53
|
+
/**
|
|
54
|
+
* Append a security event to auth.audit_log_entries (GoTrue-compatible
|
|
55
|
+
* payload). Best-effort: a logging failure never breaks the request.
|
|
56
|
+
*/
|
|
57
|
+
private audit;
|
|
58
|
+
/**
|
|
59
|
+
* Export everything held about one user across the auth schema, for a GDPR
|
|
60
|
+
* right-of-access / portability request. Credentials (password hash, MFA
|
|
61
|
+
* secrets, raw token values) are deliberately omitted — they are not personal
|
|
62
|
+
* data to hand back and exporting them would leak secrets.
|
|
63
|
+
*/
|
|
64
|
+
private exportUser;
|
|
65
|
+
/**
|
|
66
|
+
* Erase a user (GDPR right to erasure). Deletes the user row; auth.identities,
|
|
67
|
+
* refresh_tokens, one_time_tokens, flow_state, and mfa_factors/challenges are
|
|
68
|
+
* removed by their ON DELETE CASCADE foreign keys. Returns a 404 if the user
|
|
69
|
+
* doesn't exist and a summary of what was erased.
|
|
70
|
+
*
|
|
71
|
+
* Note: storage.objects.owner has no FK to auth.users, so object rows/bytes
|
|
72
|
+
* owned by the user are not removed here — see COMPLIANCE.md for the
|
|
73
|
+
* storage-erasure step the operator must run.
|
|
74
|
+
*/
|
|
75
|
+
private eraseUser;
|
|
51
76
|
private enrollFactor;
|
|
52
77
|
private challengeFactor;
|
|
53
78
|
private verifyFactor;
|
package/dist/auth/handler.js
CHANGED
|
@@ -73,7 +73,7 @@ export class AuthHandler {
|
|
|
73
73
|
if (path === 'callback' && (method === 'GET' || method === 'POST')) {
|
|
74
74
|
return await this.oauth.callback(url, (userId) => this.sessionTokensFor(userId));
|
|
75
75
|
}
|
|
76
|
-
if (path.startsWith('admin/
|
|
76
|
+
if (path.startsWith('admin/'))
|
|
77
77
|
return await this.admin(req, ctx, path, method);
|
|
78
78
|
return authError(404, 'not_found', `unknown auth endpoint: ${path}`);
|
|
79
79
|
}
|
|
@@ -110,7 +110,9 @@ export class AuthHandler {
|
|
|
110
110
|
values ('authenticated', 'authenticated', $1, $2, now(), now(),
|
|
111
111
|
'{"provider":"email","providers":["email"]}', $3)
|
|
112
112
|
returning *`, [email, hashed, JSON.stringify(body.data ?? {})]);
|
|
113
|
-
|
|
113
|
+
const newUser = res.rows[0];
|
|
114
|
+
await this.audit('user_signedup', { actorId: newUser.id, actorEmail: email });
|
|
115
|
+
return json(200, await this.sessionFor(newUser));
|
|
114
116
|
}
|
|
115
117
|
async token(req, url) {
|
|
116
118
|
const grantType = url.searchParams.get('grant_type');
|
|
@@ -120,9 +122,11 @@ export class AuthHandler {
|
|
|
120
122
|
const res = await this.db.query(`select * from auth.users where email = $1`, [email]);
|
|
121
123
|
const user = res.rows[0];
|
|
122
124
|
if (!user || !user.encrypted_password || !(await verifyPassword(body.password ?? '', user.encrypted_password))) {
|
|
125
|
+
await this.audit('login_failed', { actorEmail: email, traits: { grant_type: 'password' } });
|
|
123
126
|
return authError(400, 'invalid_credentials', 'Invalid login credentials');
|
|
124
127
|
}
|
|
125
128
|
await this.db.query(`update auth.users set last_sign_in_at = now() where id = $1`, [user.id]);
|
|
129
|
+
await this.audit('login', { actorId: user.id, actorEmail: user.email, traits: { grant_type: 'password' } });
|
|
126
130
|
return json(200, await this.sessionFor(user));
|
|
127
131
|
}
|
|
128
132
|
if (grantType === 'refresh_token') {
|
|
@@ -211,6 +215,7 @@ export class AuthHandler {
|
|
|
211
215
|
const user = await this.userFromBearer(req);
|
|
212
216
|
if (user) {
|
|
213
217
|
await this.db.query(`update auth.refresh_tokens set revoked = true where user_id = $1`, [user.id]);
|
|
218
|
+
await this.audit('logout', { actorId: user.id, actorEmail: user.email });
|
|
214
219
|
}
|
|
215
220
|
return new Response(null, { status: 204 });
|
|
216
221
|
}
|
|
@@ -255,14 +260,27 @@ export class AuthHandler {
|
|
|
255
260
|
return authError(400, 'validation_failed', 'email is required');
|
|
256
261
|
return this.issueToken(body.email, 'recovery', false);
|
|
257
262
|
}
|
|
263
|
+
/** Max wrong guesses for a one-time code before its tokens are invalidated. */
|
|
264
|
+
static MAX_OTP_ATTEMPTS = 5;
|
|
258
265
|
async redeem(token, types, email) {
|
|
266
|
+
const normalizedEmail = email?.toLowerCase().trim() ?? null;
|
|
259
267
|
const res = await this.db.query(`delete from auth.one_time_tokens
|
|
260
268
|
where token = $1 and token_type = any($2::text[])
|
|
261
269
|
and ($3::text is null or email = $3) and expires_at > now()
|
|
262
|
-
|
|
270
|
+
and attempts < $4
|
|
271
|
+
returning user_id, email`, [token, `{${types.join(',')}}`, normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
|
|
263
272
|
const row = res.rows[0];
|
|
264
|
-
if (!row)
|
|
273
|
+
if (!row) {
|
|
274
|
+
// Wrong/expired code: count the failed guess against the live tokens for
|
|
275
|
+
// this email, and burn them once the attempt cap is hit (brute-force
|
|
276
|
+
// lockout for the 6-digit OTP). Requires the email to scope the counter.
|
|
277
|
+
if (normalizedEmail) {
|
|
278
|
+
await this.db.query(`update auth.one_time_tokens set attempts = attempts + 1
|
|
279
|
+
where email = $1 and token_type = any($2::text[]) and expires_at > now()`, [normalizedEmail, `{${types.join(',')}}`]);
|
|
280
|
+
await this.db.query(`delete from auth.one_time_tokens where email = $1 and attempts >= $2`, [normalizedEmail, AuthHandler.MAX_OTP_ATTEMPTS]);
|
|
281
|
+
}
|
|
265
282
|
return null;
|
|
283
|
+
}
|
|
266
284
|
await this.db.query(`delete from auth.one_time_tokens where email = $1`, [row.email]);
|
|
267
285
|
const ures = await this.db.query(`update auth.users set email_confirmed_at = coalesce(email_confirmed_at, now()), last_sign_in_at = now()
|
|
268
286
|
where id = $1 returning *`, [row.user_id]);
|
|
@@ -272,7 +290,10 @@ export class AuthHandler {
|
|
|
272
290
|
const body = (await req.json().catch(() => ({})));
|
|
273
291
|
if (!body.token)
|
|
274
292
|
return authError(400, 'validation_failed', 'token is required');
|
|
275
|
-
|
|
293
|
+
// A recovery (password-reset) token must be redeemed with type=recovery
|
|
294
|
+
// explicitly — never fold it into the default set, or a guessed login OTP
|
|
295
|
+
// could mint a recovery session.
|
|
296
|
+
const types = body.type === 'recovery' ? ['recovery'] : body.type === 'magiclink' ? ['magiclink'] : ['otp', 'magiclink'];
|
|
276
297
|
const user = await this.redeem(body.token, types, body.email);
|
|
277
298
|
if (!user)
|
|
278
299
|
return authError(403, 'otp_expired', 'Token has expired or is invalid');
|
|
@@ -296,6 +317,15 @@ export class AuthHandler {
|
|
|
296
317
|
return authError(403, 'insufficient_permissions', 'Admin endpoints require the service_role key');
|
|
297
318
|
}
|
|
298
319
|
const idMatch = path.match(/^admin\/users\/([0-9a-f-]{36})$/);
|
|
320
|
+
const exportMatch = path.match(/^admin\/users\/([0-9a-f-]{36})\/export$/);
|
|
321
|
+
if (path === 'admin/audit' && method === 'GET') {
|
|
322
|
+
const res = await this.db.query(`select id, payload, created_at, ip_address from auth.audit_log_entries
|
|
323
|
+
order by created_at desc limit 200`);
|
|
324
|
+
return json(200, { entries: res.rows });
|
|
325
|
+
}
|
|
326
|
+
if (exportMatch && method === 'GET') {
|
|
327
|
+
return await this.exportUser(exportMatch[1]);
|
|
328
|
+
}
|
|
299
329
|
if (path === 'admin/users' && method === 'GET') {
|
|
300
330
|
const res = await this.db.query(`select * from auth.users order by created_at desc limit 1000`);
|
|
301
331
|
return json(200, { users: res.rows.map((u) => this.userJson(u)), aud: 'authenticated' });
|
|
@@ -354,11 +384,96 @@ export class AuthHandler {
|
|
|
354
384
|
return json(200, this.userJson(res.rows[0]));
|
|
355
385
|
}
|
|
356
386
|
if (idMatch && method === 'DELETE') {
|
|
357
|
-
await this.
|
|
358
|
-
return json(200, {});
|
|
387
|
+
return await this.eraseUser(idMatch[1]);
|
|
359
388
|
}
|
|
360
389
|
return authError(404, 'not_found', `unknown admin endpoint`);
|
|
361
390
|
}
|
|
391
|
+
// ── audit trail ───────────────────────────────────────────────────────
|
|
392
|
+
/**
|
|
393
|
+
* Append a security event to auth.audit_log_entries (GoTrue-compatible
|
|
394
|
+
* payload). Best-effort: a logging failure never breaks the request.
|
|
395
|
+
*/
|
|
396
|
+
async audit(action, opts = {}) {
|
|
397
|
+
try {
|
|
398
|
+
const payload = {
|
|
399
|
+
action,
|
|
400
|
+
actor_id: opts.actorId ?? null,
|
|
401
|
+
actor_username: opts.actorEmail ?? null,
|
|
402
|
+
log_type: opts.type ?? 'account',
|
|
403
|
+
traits: opts.traits ?? {},
|
|
404
|
+
timestamp: new Date().toISOString(),
|
|
405
|
+
};
|
|
406
|
+
await this.db.query(`insert into auth.audit_log_entries (payload) values ($1::jsonb)`, [JSON.stringify(payload)]);
|
|
407
|
+
}
|
|
408
|
+
catch {
|
|
409
|
+
// audit logging is best-effort
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
// ── GDPR: data-subject access (export) ────────────────────────────────
|
|
413
|
+
/**
|
|
414
|
+
* Export everything held about one user across the auth schema, for a GDPR
|
|
415
|
+
* right-of-access / portability request. Credentials (password hash, MFA
|
|
416
|
+
* secrets, raw token values) are deliberately omitted — they are not personal
|
|
417
|
+
* data to hand back and exporting them would leak secrets.
|
|
418
|
+
*/
|
|
419
|
+
async exportUser(userId) {
|
|
420
|
+
const ures = await this.db.query(`select * from auth.users where id = $1`, [userId]);
|
|
421
|
+
if (ures.rows.length === 0)
|
|
422
|
+
return authError(404, 'user_not_found', 'User not found');
|
|
423
|
+
const user = ures.rows[0];
|
|
424
|
+
const identities = await this.db.query(`select id, provider, provider_id, identity_data, last_sign_in_at, created_at, updated_at
|
|
425
|
+
from auth.identities where user_id = $1`, [userId]);
|
|
426
|
+
const sessions = await this.db.query(`select id, parent, session_id, revoked, created_at, updated_at
|
|
427
|
+
from auth.refresh_tokens where user_id = $1`, [userId]);
|
|
428
|
+
const factors = await this.db.query(`select id, friendly_name, factor_type, status, created_at, updated_at
|
|
429
|
+
from auth.mfa_factors where user_id = $1`, [userId]);
|
|
430
|
+
// strip credential/token columns from the raw record before returning it
|
|
431
|
+
const SENSITIVE = [
|
|
432
|
+
'encrypted_password',
|
|
433
|
+
'confirmation_token',
|
|
434
|
+
'recovery_token',
|
|
435
|
+
'email_change_token_new',
|
|
436
|
+
'email_change_token_current',
|
|
437
|
+
'phone_change_token',
|
|
438
|
+
'reauthentication_token',
|
|
439
|
+
];
|
|
440
|
+
const userSafe = Object.fromEntries(Object.entries(user).filter(([k]) => !SENSITIVE.includes(k)));
|
|
441
|
+
await this.audit('user_data_exported', { actorId: userId, type: 'admin' });
|
|
442
|
+
return json(200, {
|
|
443
|
+
exported_at: new Date().toISOString(),
|
|
444
|
+
user: this.userJson(user),
|
|
445
|
+
user_record: userSafe,
|
|
446
|
+
identities: identities.rows,
|
|
447
|
+
sessions: sessions.rows,
|
|
448
|
+
mfa_factors: factors.rows,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Erase a user (GDPR right to erasure). Deletes the user row; auth.identities,
|
|
453
|
+
* refresh_tokens, one_time_tokens, flow_state, and mfa_factors/challenges are
|
|
454
|
+
* removed by their ON DELETE CASCADE foreign keys. Returns a 404 if the user
|
|
455
|
+
* doesn't exist and a summary of what was erased.
|
|
456
|
+
*
|
|
457
|
+
* Note: storage.objects.owner has no FK to auth.users, so object rows/bytes
|
|
458
|
+
* owned by the user are not removed here — see COMPLIANCE.md for the
|
|
459
|
+
* storage-erasure step the operator must run.
|
|
460
|
+
*/
|
|
461
|
+
async eraseUser(userId) {
|
|
462
|
+
const before = await this.db.query(`select
|
|
463
|
+
(select count(*) from auth.identities where user_id = $1)::int as identities,
|
|
464
|
+
(select count(*) from auth.refresh_tokens where user_id = $1)::int as sessions,
|
|
465
|
+
(select count(*) from auth.mfa_factors where user_id = $1)::int as factors`, [userId]);
|
|
466
|
+
const del = await this.db.query(`delete from auth.users where id = $1 returning id`, [userId]);
|
|
467
|
+
if (del.rows.length === 0)
|
|
468
|
+
return authError(404, 'user_not_found', 'User not found');
|
|
469
|
+
const c = before.rows[0];
|
|
470
|
+
await this.audit('user_deleted', { actorId: userId, type: 'admin', traits: { erased: true } });
|
|
471
|
+
return json(200, {
|
|
472
|
+
erased: true,
|
|
473
|
+
user_id: userId,
|
|
474
|
+
cascaded: { identities: c.identities, sessions: c.sessions, mfa_factors: c.factors },
|
|
475
|
+
});
|
|
476
|
+
}
|
|
362
477
|
// ── MFA (TOTP) ────────────────────────────────────────────────────────
|
|
363
478
|
async enrollFactor(req) {
|
|
364
479
|
const user = await this.userFromBearer(req);
|
|
@@ -423,13 +538,17 @@ export class AuthHandler {
|
|
|
423
538
|
const factor = fr.rows[0];
|
|
424
539
|
if (!factor)
|
|
425
540
|
return authError(404, 'mfa_factor_not_found', 'MFA factor not found');
|
|
426
|
-
const cr = await this.db.query(`select id, expires_at from auth.mfa_challenges where id = $1 and factor_id = $2`, [body.challenge_id ?? '', factorId]);
|
|
541
|
+
const cr = await this.db.query(`select id, expires_at, verified_at from auth.mfa_challenges where id = $1 and factor_id = $2`, [body.challenge_id ?? '', factorId]);
|
|
427
542
|
const challenge = cr.rows[0];
|
|
428
543
|
if (!challenge)
|
|
429
544
|
return authError(404, 'mfa_challenge_not_found', 'MFA challenge not found');
|
|
430
545
|
if (new Date(challenge.expires_at).getTime() < Date.now()) {
|
|
431
546
|
return authError(422, 'mfa_challenge_expired', 'MFA challenge has expired, verify against another one');
|
|
432
547
|
}
|
|
548
|
+
// a challenge is single-use: once verified it cannot be replayed
|
|
549
|
+
if (challenge.verified_at) {
|
|
550
|
+
return authError(422, 'mfa_verification_failed', 'This challenge has already been verified');
|
|
551
|
+
}
|
|
433
552
|
if (!(await verifyTotp(factor.secret, body.code ?? ''))) {
|
|
434
553
|
return authError(422, 'mfa_verification_failed', 'Invalid TOTP code entered');
|
|
435
554
|
}
|