@edge-base/server 0.2.9 → 0.3.1
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/admin-build/_app/immutable/chunks/{BfhqI0W8.js → BSIzoJ5I.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CI1lKBjB.js → BeYswbvA.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DncT8xH5.js → BkrC_6Ss.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DBniE7GN.js → BunyH6GE.js} +1 -1
- package/admin-build/_app/immutable/chunks/{C5nMidnK.js → C-zXt-cq.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CPLcjNR9.js → C1Mycuvd.js} +1 -1
- package/admin-build/_app/immutable/chunks/{0M5txo3l.js → Cek51lkE.js} +3 -3
- package/admin-build/_app/immutable/chunks/{_xa30BOD.js → D0mNYcxN.js} +1 -1
- package/admin-build/_app/immutable/chunks/D3hdqsfp.js +1 -0
- package/admin-build/_app/immutable/chunks/{39YMyjjq.js → DF40xjpj.js} +1 -1
- package/admin-build/_app/immutable/chunks/{YGUb3X-Q.js → DOOhHO-v.js} +1 -1
- package/admin-build/_app/immutable/chunks/DdjFeYwG.js +1 -0
- package/admin-build/_app/immutable/chunks/{DllVADtt.js → Dvlxm2Iq.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BN-pHKcH.js → Xm1dHMTE.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DUhE3Ynq.js → aPulNooa.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DBkor_jM.js → dZsKGnWZ.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.BW-Ac3jY.js → app.j1WHBlZX.js} +2 -2
- package/admin-build/_app/immutable/entry/start.BgMeGq-q.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.CRSQPtos.js → 0.Oo7ZpbR-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.HmStNbZP.js → 1.zgMnKHns.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.BVlz13nr.js → 10.u5mkRiUe.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.VwQdbWwH.js → 11.9ynAgaxV.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.BFAAgi1f.js → 12.M_EqzJ6s.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.DDdauC84.js → 13.D_NQ9q60.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.D_rXaafJ.js → 14.-vENIJ2w.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.rfFjjiZc.js → 15.DoMCZhGv.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.DDEpGJ4Z.js → 16.6BmnzScq.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.ChQWNtRN.js → 17.q6QQ9N_J.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.Rir_JwlF.js → 18.DUhJ6dKj.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.CF76nxk1.js → 19.CvkvPlGP.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.JQer3KPM.js → 20.cACSbRIl.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.DZ7G8rMF.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.DUw7X6z_.js → 22.BsQ6xeKr.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.DS3svA-a.js → 23.CNW3mLz5.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.BYOsQM6B.js → 24.CReqtEOy.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.n0pyDBRv.js → 25.BuxVhqlb.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.C-Oo8sYK.js → 26.C-LzPf62.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.BfxbFNkk.js → 27.Dl5RL_tB.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.DyHSN1q7.js → 28.CWfSQgjH.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.CUAmZbCc.js → 29.wUSfh2eQ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.KQm4VJJg.js → 3.D_laBXeK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.CQdPre5F.js → 30.DcMhZyQ0.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.BP6raetq.js → 31.CSwhNsNi.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.Glf2jJHB.js → 4.Cq28vPI2.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.BdFtj_-X.js → 5.BTT9B3oI.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.JfnwWq8s.js → 6.iCmmzXZp.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.C-rw9qDh.js → 7.qY5v7qqJ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.CA7r1Sjs.js → 8.C6p4RvgK.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.B2jgJDkH.js → 9.f548N7zh.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/openapi.json +305 -3
- package/package.json +3 -3
- package/src/__tests__/auth-token.test.ts +142 -0
- package/src/__tests__/email-provider.test.ts +82 -0
- package/src/__tests__/functions-context.test.ts +87 -0
- package/src/__tests__/functions-d1-proxy.test.ts +70 -0
- package/src/__tests__/functions-route.test.ts +15 -0
- package/src/__tests__/room-access-policy.test.ts +26 -0
- package/src/__tests__/room-handler-context.test.ts +2 -0
- package/src/__tests__/smoke-skip-report.test.ts +1 -1
- package/src/durable-objects/room-runtime-base.ts +16 -5
- package/src/durable-objects/rooms-do.ts +4 -1
- package/src/lib/auth-d1-service.ts +37 -7
- package/src/lib/auth-token.ts +66 -0
- package/src/lib/email-provider.ts +115 -5
- package/src/lib/functions.ts +91 -1
- package/src/lib/internal-transport.ts +15 -1
- package/src/lib/jwt.ts +2 -0
- package/src/routes/admin.ts +8 -1
- package/src/routes/auth.ts +111 -19
- package/src/routes/room.ts +36 -2
- package/src/routes/storage.ts +446 -62
- package/src/types.ts +10 -0
- package/admin-build/_app/immutable/chunks/B0zJ5_Wk.js +0 -1
- package/admin-build/_app/immutable/chunks/DZo4s3wn.js +0 -1
- package/admin-build/_app/immutable/entry/start.Bcf0SjPV.js +0 -1
- package/admin-build/_app/immutable/nodes/21.CzGYxjpu.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import"../chunks/CWj6FrbW.js";import{o as U}from"../chunks/Bn2NtlTj.js";import{p as j,a as z,f as G,c as b,g as t,s as m,r as g,u as n,b as y,d as D,t as J}from"../chunks/BdTBlfLy.js";import{d as K,s as N,a as O}from"../chunks/DtZk82gG.js";import{a as Q,t as V,e as W,i as X}from"../chunks/
|
|
1
|
+
import"../chunks/CWj6FrbW.js";import{o as U}from"../chunks/Bn2NtlTj.js";import{p as j,a as z,f as G,c as b,g as t,s as m,r as g,u as n,b as y,d as D,t as J}from"../chunks/BdTBlfLy.js";import{d as K,s as N,a as O}from"../chunks/DtZk82gG.js";import{a as Q,t as V,e as W,i as X}from"../chunks/BkrC_6Ss.js";import{a as F,f as I}from"../chunks/DEELgv7K.js";import{d as Y,s as Z}from"../chunks/CjcrXziO.js";import{P as tt}from"../chunks/BkZCgsc3.js";import{a as et}from"../chunks/Q2nPFxS6.js";import{M as _,T as at}from"../chunks/DOOhHO-v.js";import{D as rt,T as nt}from"../chunks/dZsKGnWZ.js";var st=I("<button> </button>"),ot=I('<div class="range-selector"></div>'),lt=I('<div class="analytics-grid"><!> <!> <!> <!></div> <div class="analytics-chart"><!></div> <div class="analytics-bottom"><!> <!></div>',1);function yt(w,A){j(A,!0);let s=D(!0),o=D(null),p=D("24h");const M=[{value:"1h",label:"1H"},{value:"6h",label:"6H"},{value:"24h",label:"24H"},{value:"7d",label:"7D"},{value:"30d",label:"30D"}],T=n(()=>()=>{switch(t(p)){case"1h":return"minute";case"6h":case"24h":return"hour";default:return"day"}});async function $(){try{const e=await Q.fetch(`data/analytics?range=${t(p)}&category=function&metric=overview&groupBy=${t(T)()}`);y(o,e,!0)}catch(e){V(Y(e,"Failed to load functions analytics."))}finally{y(s,!1)}}function k(e){y(p,e,!0),y(s,!0),$()}U(()=>{$();const e=setInterval($,3e4);return()=>clearInterval(e)});const H=n(()=>()=>{var c;if(!((c=t(o))!=null&&c.summary))return 0;const{totalRequests:e,totalErrors:a}=t(o).summary;return e>0?a/e*100:0}),B=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.timeSeries)||[]).map(a=>({timestamp:a.timestamp,value:a.requests??a.value??0}))}),C=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.breakdown)||[]).map(a=>({label:a.label||"other",value:a.count}))}),E=n(()=>()=>{var e;return(((e=t(o))==null?void 0:e.topItems)||[]).map(a=>({label:a.label,count:a.count,secondary:`${Math.round(a.avgLatency)}ms · ${a.errorRate.toFixed(1)}% 5xx`}))});tt(w,{title:"Functions Analytics",description:"Serverless function execution and performance metrics",get docsHref(){return et},actions:a=>{var c=ot();W(c,21,()=>M,X,(f,d)=>{var l=st();let v;var h=b(l,!0);g(l),J(()=>{v=Z(l,1,"range-btn",null,v,{"range-btn--active":t(p)===t(d).value}),N(h,t(d).label)}),O("click",l,()=>k(t(d).value)),F(f,l)}),g(c),F(a,c)},children:(a,c)=>{var f=lt(),d=G(f),l=b(d);{let r=n(()=>{var i,u;return((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.totalRequests)??0});_(l,{label:"Invocations",get value(){return t(r)},get loading(){return t(s)}})}var v=m(l,2);{let r=n(()=>{var i,u;return((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.uniqueUsers)??0});_(v,{label:"Unique Users",get value(){return t(r)},get loading(){return t(s)}})}var h=m(v,2);{let r=n(()=>t(H)().toFixed(1));_(h,{label:"5xx Rate",get value(){return t(r)},unit:"%",get loading(){return t(s)}})}var L=m(h,2);{let r=n(()=>{var i,u;return Math.round(((u=(i=t(o))==null?void 0:i.summary)==null?void 0:u.avgLatency)??0)});_(L,{label:"Avg Duration",get value(){return t(r)},unit:"ms",get loading(){return t(s)}})}g(d);var x=m(d,2),P=b(x);{let r=n(()=>t(B)());at(P,{get data(){return t(r)},type:"bar",height:240,label:"Function invocations over time",get loading(){return t(s)},color:"#dc2626"})}g(x);var R=m(x,2),q=b(R);{let r=n(()=>t(C)());rt(q,{get items(){return t(r)},title:"Functions by invocation count",get loading(){return t(s)}})}var S=m(q,2);{let r=n(()=>t(E)());nt(S,{get items(){return t(r)},title:"Top Functions",get loading(){return t(s)}})}g(R),F(a,f)}}),z()}K(["click"]);export{yt as component};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"
|
|
1
|
+
{"version":"1783089130568"}
|
package/admin-build/index.html
CHANGED
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>EdgeBase Admin</title>
|
|
7
7
|
<link rel="icon" href="/admin/favicon.svg" type="image/svg+xml" />
|
|
8
|
-
<link href="/admin/_app/immutable/entry/start.
|
|
9
|
-
<link href="/admin/_app/immutable/chunks/
|
|
8
|
+
<link href="/admin/_app/immutable/entry/start.BgMeGq-q.js" rel="modulepreload">
|
|
9
|
+
<link href="/admin/_app/immutable/chunks/DdjFeYwG.js" rel="modulepreload">
|
|
10
10
|
<link href="/admin/_app/immutable/chunks/BdTBlfLy.js" rel="modulepreload">
|
|
11
11
|
<link href="/admin/_app/immutable/chunks/Bn2NtlTj.js" rel="modulepreload">
|
|
12
|
-
<link href="/admin/_app/immutable/chunks/
|
|
13
|
-
<link href="/admin/_app/immutable/entry/app.
|
|
12
|
+
<link href="/admin/_app/immutable/chunks/D3hdqsfp.js" rel="modulepreload">
|
|
13
|
+
<link href="/admin/_app/immutable/entry/app.j1WHBlZX.js" rel="modulepreload">
|
|
14
14
|
<link href="/admin/_app/immutable/chunks/B2bEC_Hm.js" rel="modulepreload">
|
|
15
15
|
<link href="/admin/_app/immutable/chunks/Bb0e0sAP.js" rel="modulepreload">
|
|
16
16
|
<link href="/admin/_app/immutable/chunks/DtZk82gG.js" rel="modulepreload">
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<div style="display: contents">
|
|
27
27
|
<script>
|
|
28
28
|
{
|
|
29
|
-
|
|
29
|
+
__sveltekit_xb7lyo = {
|
|
30
30
|
base: "/admin",
|
|
31
31
|
assets: "/admin"
|
|
32
32
|
};
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
const element = document.currentScript.parentElement;
|
|
35
35
|
|
|
36
36
|
Promise.all([
|
|
37
|
-
import("/admin/_app/immutable/entry/start.
|
|
38
|
-
import("/admin/_app/immutable/entry/app.
|
|
37
|
+
import("/admin/_app/immutable/entry/start.BgMeGq-q.js"),
|
|
38
|
+
import("/admin/_app/immutable/entry/app.j1WHBlZX.js")
|
|
39
39
|
]).then(([kit, app]) => {
|
|
40
40
|
kit.start(app, element);
|
|
41
41
|
});
|
package/openapi.json
CHANGED
|
@@ -1846,6 +1846,100 @@
|
|
|
1846
1846
|
}
|
|
1847
1847
|
}
|
|
1848
1848
|
},
|
|
1849
|
+
"/api/auth/mfa/recovery-codes": {
|
|
1850
|
+
"post": {
|
|
1851
|
+
"operationId": "authMfaRecoveryCodesRegenerate",
|
|
1852
|
+
"tags": [
|
|
1853
|
+
"client"
|
|
1854
|
+
],
|
|
1855
|
+
"summary": "Regenerate MFA recovery codes",
|
|
1856
|
+
"requestBody": {
|
|
1857
|
+
"required": false,
|
|
1858
|
+
"content": {
|
|
1859
|
+
"application/json": {
|
|
1860
|
+
"schema": {
|
|
1861
|
+
"type": "object",
|
|
1862
|
+
"properties": {
|
|
1863
|
+
"password": {
|
|
1864
|
+
"type": "string"
|
|
1865
|
+
},
|
|
1866
|
+
"code": {
|
|
1867
|
+
"type": "string"
|
|
1868
|
+
}
|
|
1869
|
+
},
|
|
1870
|
+
"additionalProperties": {}
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1875
|
+
"responses": {
|
|
1876
|
+
"200": {
|
|
1877
|
+
"description": "Success",
|
|
1878
|
+
"content": {
|
|
1879
|
+
"application/json": {
|
|
1880
|
+
"schema": {
|
|
1881
|
+
"type": "object",
|
|
1882
|
+
"additionalProperties": {},
|
|
1883
|
+
"description": "JSON response"
|
|
1884
|
+
}
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
},
|
|
1888
|
+
"400": {
|
|
1889
|
+
"description": "Bad request",
|
|
1890
|
+
"content": {
|
|
1891
|
+
"application/json": {
|
|
1892
|
+
"schema": {
|
|
1893
|
+
"type": "object",
|
|
1894
|
+
"properties": {
|
|
1895
|
+
"code": {
|
|
1896
|
+
"type": "number",
|
|
1897
|
+
"description": "HTTP status code",
|
|
1898
|
+
"example": 400
|
|
1899
|
+
},
|
|
1900
|
+
"message": {
|
|
1901
|
+
"type": "string",
|
|
1902
|
+
"description": "Human-readable message",
|
|
1903
|
+
"example": "Invalid request body"
|
|
1904
|
+
}
|
|
1905
|
+
},
|
|
1906
|
+
"required": [
|
|
1907
|
+
"code",
|
|
1908
|
+
"message"
|
|
1909
|
+
]
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
},
|
|
1914
|
+
"401": {
|
|
1915
|
+
"description": "Authentication required or verification failed",
|
|
1916
|
+
"content": {
|
|
1917
|
+
"application/json": {
|
|
1918
|
+
"schema": {
|
|
1919
|
+
"type": "object",
|
|
1920
|
+
"properties": {
|
|
1921
|
+
"code": {
|
|
1922
|
+
"type": "number",
|
|
1923
|
+
"description": "HTTP status code",
|
|
1924
|
+
"example": 400
|
|
1925
|
+
},
|
|
1926
|
+
"message": {
|
|
1927
|
+
"type": "string",
|
|
1928
|
+
"description": "Human-readable message",
|
|
1929
|
+
"example": "Invalid request body"
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1932
|
+
"required": [
|
|
1933
|
+
"code",
|
|
1934
|
+
"message"
|
|
1935
|
+
]
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
},
|
|
1849
1943
|
"/api/auth/mfa/totp": {
|
|
1850
1944
|
"delete": {
|
|
1851
1945
|
"operationId": "authMfaTotpDelete",
|
|
@@ -8576,6 +8670,26 @@
|
|
|
8576
8670
|
"required": true,
|
|
8577
8671
|
"name": "bucket",
|
|
8578
8672
|
"in": "path"
|
|
8673
|
+
},
|
|
8674
|
+
{
|
|
8675
|
+
"schema": {
|
|
8676
|
+
"type": "string",
|
|
8677
|
+
"description": "Optional signed upload key echoed from a signed upload URL."
|
|
8678
|
+
},
|
|
8679
|
+
"required": false,
|
|
8680
|
+
"description": "Optional signed upload key echoed from a signed upload URL.",
|
|
8681
|
+
"name": "key",
|
|
8682
|
+
"in": "query"
|
|
8683
|
+
},
|
|
8684
|
+
{
|
|
8685
|
+
"schema": {
|
|
8686
|
+
"type": "string",
|
|
8687
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
8688
|
+
},
|
|
8689
|
+
"required": false,
|
|
8690
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
8691
|
+
"name": "token",
|
|
8692
|
+
"in": "query"
|
|
8579
8693
|
}
|
|
8580
8694
|
],
|
|
8581
8695
|
"requestBody": {
|
|
@@ -8654,6 +8768,32 @@
|
|
|
8654
8768
|
}
|
|
8655
8769
|
}
|
|
8656
8770
|
}
|
|
8771
|
+
},
|
|
8772
|
+
"413": {
|
|
8773
|
+
"description": "Payload too large",
|
|
8774
|
+
"content": {
|
|
8775
|
+
"application/json": {
|
|
8776
|
+
"schema": {
|
|
8777
|
+
"type": "object",
|
|
8778
|
+
"properties": {
|
|
8779
|
+
"code": {
|
|
8780
|
+
"type": "number",
|
|
8781
|
+
"description": "HTTP status code",
|
|
8782
|
+
"example": 400
|
|
8783
|
+
},
|
|
8784
|
+
"message": {
|
|
8785
|
+
"type": "string",
|
|
8786
|
+
"description": "Human-readable message",
|
|
8787
|
+
"example": "Invalid request body"
|
|
8788
|
+
}
|
|
8789
|
+
},
|
|
8790
|
+
"required": [
|
|
8791
|
+
"code",
|
|
8792
|
+
"message"
|
|
8793
|
+
]
|
|
8794
|
+
}
|
|
8795
|
+
}
|
|
8796
|
+
}
|
|
8657
8797
|
}
|
|
8658
8798
|
}
|
|
8659
8799
|
}
|
|
@@ -9026,6 +9166,9 @@
|
|
|
9026
9166
|
"200": {
|
|
9027
9167
|
"description": "File content"
|
|
9028
9168
|
},
|
|
9169
|
+
"206": {
|
|
9170
|
+
"description": "Partial file content"
|
|
9171
|
+
},
|
|
9029
9172
|
"403": {
|
|
9030
9173
|
"description": "Forbidden",
|
|
9031
9174
|
"content": {
|
|
@@ -9077,6 +9220,9 @@
|
|
|
9077
9220
|
}
|
|
9078
9221
|
}
|
|
9079
9222
|
}
|
|
9223
|
+
},
|
|
9224
|
+
"416": {
|
|
9225
|
+
"description": "Range not satisfiable"
|
|
9080
9226
|
}
|
|
9081
9227
|
}
|
|
9082
9228
|
},
|
|
@@ -9203,6 +9349,16 @@
|
|
|
9203
9349
|
"required": true,
|
|
9204
9350
|
"name": "key",
|
|
9205
9351
|
"in": "query"
|
|
9352
|
+
},
|
|
9353
|
+
{
|
|
9354
|
+
"schema": {
|
|
9355
|
+
"type": "string",
|
|
9356
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
9357
|
+
},
|
|
9358
|
+
"required": false,
|
|
9359
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
9360
|
+
"name": "token",
|
|
9361
|
+
"in": "query"
|
|
9206
9362
|
}
|
|
9207
9363
|
],
|
|
9208
9364
|
"responses": {
|
|
@@ -9803,6 +9959,26 @@
|
|
|
9803
9959
|
"required": true,
|
|
9804
9960
|
"name": "bucket",
|
|
9805
9961
|
"in": "path"
|
|
9962
|
+
},
|
|
9963
|
+
{
|
|
9964
|
+
"schema": {
|
|
9965
|
+
"type": "string",
|
|
9966
|
+
"description": "Optional signed upload key echoed from a signed upload URL."
|
|
9967
|
+
},
|
|
9968
|
+
"required": false,
|
|
9969
|
+
"description": "Optional signed upload key echoed from a signed upload URL.",
|
|
9970
|
+
"name": "key",
|
|
9971
|
+
"in": "query"
|
|
9972
|
+
},
|
|
9973
|
+
{
|
|
9974
|
+
"schema": {
|
|
9975
|
+
"type": "string",
|
|
9976
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
9977
|
+
},
|
|
9978
|
+
"required": false,
|
|
9979
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
9980
|
+
"name": "token",
|
|
9981
|
+
"in": "query"
|
|
9806
9982
|
}
|
|
9807
9983
|
],
|
|
9808
9984
|
"requestBody": {
|
|
@@ -9915,15 +10091,49 @@
|
|
|
9915
10091
|
"required": true,
|
|
9916
10092
|
"name": "bucket",
|
|
9917
10093
|
"in": "path"
|
|
10094
|
+
},
|
|
10095
|
+
{
|
|
10096
|
+
"schema": {
|
|
10097
|
+
"type": "string"
|
|
10098
|
+
},
|
|
10099
|
+
"required": true,
|
|
10100
|
+
"name": "uploadId",
|
|
10101
|
+
"in": "query"
|
|
10102
|
+
},
|
|
10103
|
+
{
|
|
10104
|
+
"schema": {
|
|
10105
|
+
"type": "string"
|
|
10106
|
+
},
|
|
10107
|
+
"required": true,
|
|
10108
|
+
"name": "partNumber",
|
|
10109
|
+
"in": "query"
|
|
10110
|
+
},
|
|
10111
|
+
{
|
|
10112
|
+
"schema": {
|
|
10113
|
+
"type": "string"
|
|
10114
|
+
},
|
|
10115
|
+
"required": true,
|
|
10116
|
+
"name": "key",
|
|
10117
|
+
"in": "query"
|
|
10118
|
+
},
|
|
10119
|
+
{
|
|
10120
|
+
"schema": {
|
|
10121
|
+
"type": "string",
|
|
10122
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
10123
|
+
},
|
|
10124
|
+
"required": false,
|
|
10125
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
10126
|
+
"name": "token",
|
|
10127
|
+
"in": "query"
|
|
9918
10128
|
}
|
|
9919
10129
|
],
|
|
9920
10130
|
"requestBody": {
|
|
9921
10131
|
"required": true,
|
|
9922
10132
|
"content": {
|
|
9923
|
-
"application/
|
|
10133
|
+
"application/octet-stream": {
|
|
9924
10134
|
"schema": {
|
|
9925
|
-
"type": "
|
|
9926
|
-
"
|
|
10135
|
+
"type": "string",
|
|
10136
|
+
"format": "binary"
|
|
9927
10137
|
}
|
|
9928
10138
|
}
|
|
9929
10139
|
}
|
|
@@ -9992,6 +10202,32 @@
|
|
|
9992
10202
|
}
|
|
9993
10203
|
}
|
|
9994
10204
|
}
|
|
10205
|
+
},
|
|
10206
|
+
"413": {
|
|
10207
|
+
"description": "Payload too large",
|
|
10208
|
+
"content": {
|
|
10209
|
+
"application/json": {
|
|
10210
|
+
"schema": {
|
|
10211
|
+
"type": "object",
|
|
10212
|
+
"properties": {
|
|
10213
|
+
"code": {
|
|
10214
|
+
"type": "number",
|
|
10215
|
+
"description": "HTTP status code",
|
|
10216
|
+
"example": 400
|
|
10217
|
+
},
|
|
10218
|
+
"message": {
|
|
10219
|
+
"type": "string",
|
|
10220
|
+
"description": "Human-readable message",
|
|
10221
|
+
"example": "Invalid request body"
|
|
10222
|
+
}
|
|
10223
|
+
},
|
|
10224
|
+
"required": [
|
|
10225
|
+
"code",
|
|
10226
|
+
"message"
|
|
10227
|
+
]
|
|
10228
|
+
}
|
|
10229
|
+
}
|
|
10230
|
+
}
|
|
9995
10231
|
}
|
|
9996
10232
|
}
|
|
9997
10233
|
}
|
|
@@ -10011,6 +10247,26 @@
|
|
|
10011
10247
|
"required": true,
|
|
10012
10248
|
"name": "bucket",
|
|
10013
10249
|
"in": "path"
|
|
10250
|
+
},
|
|
10251
|
+
{
|
|
10252
|
+
"schema": {
|
|
10253
|
+
"type": "string",
|
|
10254
|
+
"description": "Optional signed upload key echoed from a signed upload URL."
|
|
10255
|
+
},
|
|
10256
|
+
"required": false,
|
|
10257
|
+
"description": "Optional signed upload key echoed from a signed upload URL.",
|
|
10258
|
+
"name": "key",
|
|
10259
|
+
"in": "query"
|
|
10260
|
+
},
|
|
10261
|
+
{
|
|
10262
|
+
"schema": {
|
|
10263
|
+
"type": "string",
|
|
10264
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
10265
|
+
},
|
|
10266
|
+
"required": false,
|
|
10267
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
10268
|
+
"name": "token",
|
|
10269
|
+
"in": "query"
|
|
10014
10270
|
}
|
|
10015
10271
|
],
|
|
10016
10272
|
"requestBody": {
|
|
@@ -10118,6 +10374,32 @@
|
|
|
10118
10374
|
}
|
|
10119
10375
|
}
|
|
10120
10376
|
}
|
|
10377
|
+
},
|
|
10378
|
+
"413": {
|
|
10379
|
+
"description": "Payload too large",
|
|
10380
|
+
"content": {
|
|
10381
|
+
"application/json": {
|
|
10382
|
+
"schema": {
|
|
10383
|
+
"type": "object",
|
|
10384
|
+
"properties": {
|
|
10385
|
+
"code": {
|
|
10386
|
+
"type": "number",
|
|
10387
|
+
"description": "HTTP status code",
|
|
10388
|
+
"example": 400
|
|
10389
|
+
},
|
|
10390
|
+
"message": {
|
|
10391
|
+
"type": "string",
|
|
10392
|
+
"description": "Human-readable message",
|
|
10393
|
+
"example": "Invalid request body"
|
|
10394
|
+
}
|
|
10395
|
+
},
|
|
10396
|
+
"required": [
|
|
10397
|
+
"code",
|
|
10398
|
+
"message"
|
|
10399
|
+
]
|
|
10400
|
+
}
|
|
10401
|
+
}
|
|
10402
|
+
}
|
|
10121
10403
|
}
|
|
10122
10404
|
}
|
|
10123
10405
|
}
|
|
@@ -10137,6 +10419,26 @@
|
|
|
10137
10419
|
"required": true,
|
|
10138
10420
|
"name": "bucket",
|
|
10139
10421
|
"in": "path"
|
|
10422
|
+
},
|
|
10423
|
+
{
|
|
10424
|
+
"schema": {
|
|
10425
|
+
"type": "string",
|
|
10426
|
+
"description": "Optional signed upload key echoed from a signed upload URL."
|
|
10427
|
+
},
|
|
10428
|
+
"required": false,
|
|
10429
|
+
"description": "Optional signed upload key echoed from a signed upload URL.",
|
|
10430
|
+
"name": "key",
|
|
10431
|
+
"in": "query"
|
|
10432
|
+
},
|
|
10433
|
+
{
|
|
10434
|
+
"schema": {
|
|
10435
|
+
"type": "string",
|
|
10436
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants."
|
|
10437
|
+
},
|
|
10438
|
+
"required": false,
|
|
10439
|
+
"description": "Optional signed upload token for write-rule-bypassing upload grants.",
|
|
10440
|
+
"name": "token",
|
|
10441
|
+
"in": "query"
|
|
10140
10442
|
}
|
|
10141
10443
|
],
|
|
10142
10444
|
"requestBody": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-base/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "EdgeBase runtime assets consumed by the EdgeBase CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"jose": "^6.0.0",
|
|
35
35
|
"pg": "^8.16.3",
|
|
36
36
|
"zod": "^4.3.6",
|
|
37
|
-
"@edge-base/
|
|
38
|
-
"@edge-base/
|
|
37
|
+
"@edge-base/shared": "0.3.1",
|
|
38
|
+
"@edge-base/core": "0.3.1"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@cloudflare/vitest-pool-workers": "^0.8.71",
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import type { AuthDb } from '../lib/auth-db-adapter.js';
|
|
3
|
+
import {
|
|
4
|
+
createEmailToken,
|
|
5
|
+
deleteEmailToken,
|
|
6
|
+
getEmailToken,
|
|
7
|
+
getEmailTokenByType,
|
|
8
|
+
} from '../lib/auth-d1-service.js';
|
|
9
|
+
import {
|
|
10
|
+
authSecretLookupKeys,
|
|
11
|
+
hashAuthSecret,
|
|
12
|
+
hashOtpSecret,
|
|
13
|
+
verifyAuthSecret,
|
|
14
|
+
verifyOtpSecret,
|
|
15
|
+
} from '../lib/auth-token.js';
|
|
16
|
+
|
|
17
|
+
class MemoryAuthDb implements AuthDb {
|
|
18
|
+
readonly dialect = 'sqlite' as const;
|
|
19
|
+
rows: Array<Record<string, unknown>> = [];
|
|
20
|
+
|
|
21
|
+
async query<T = Record<string, unknown>>(): Promise<T[]> {
|
|
22
|
+
return this.rows as T[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async first<T = Record<string, unknown>>(_sql: string, params: unknown[] = []): Promise<T | null> {
|
|
26
|
+
const type = _sql.includes('AND type = ?') ? params[params.length - 1] : undefined;
|
|
27
|
+
const tokenKeys = type ? params.slice(0, -1) : params;
|
|
28
|
+
const row = this.rows.find((candidate) => {
|
|
29
|
+
return tokenKeys.includes(candidate.token) && (!type || candidate.type === type);
|
|
30
|
+
});
|
|
31
|
+
return (row ?? null) as T | null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async run(sql: string, params: unknown[] = []): Promise<void> {
|
|
35
|
+
if (sql.includes('INSERT INTO _email_tokens')) {
|
|
36
|
+
const [token, userId, type, expiresAt, createdAt] = params;
|
|
37
|
+
this.rows.push({ token, userId, type, expiresAt, createdAt });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (sql.includes('DELETE FROM _email_tokens WHERE token IN')) {
|
|
41
|
+
const keys = new Set(params);
|
|
42
|
+
this.rows = this.rows.filter((row) => !keys.has(row.token));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`Unexpected SQL in MemoryAuthDb: ${sql}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async batch(): Promise<void> {}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('auth-token helpers', () => {
|
|
52
|
+
it('hashes auth secrets and verifies them without storing the raw value', async () => {
|
|
53
|
+
const hash = await hashAuthSecret('123456');
|
|
54
|
+
|
|
55
|
+
expect(hash).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
56
|
+
expect(hash).not.toBe('123456');
|
|
57
|
+
await expect(verifyAuthSecret('123456', hash)).resolves.toBe(true);
|
|
58
|
+
await expect(verifyAuthSecret('000000', hash)).resolves.toBe(false);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('hashes short OTP secrets with a server-keyed HMAC', async () => {
|
|
62
|
+
const hash = await hashOtpSecret('123456', 'server-secret-a');
|
|
63
|
+
|
|
64
|
+
expect(hash).toMatch(/^hmac-sha256:[a-f0-9]{64}$/);
|
|
65
|
+
expect(hash).not.toBe('123456');
|
|
66
|
+
expect(hash).not.toBe(await hashAuthSecret('123456'));
|
|
67
|
+
await expect(verifyOtpSecret('123456', hash, 'server-secret-a')).resolves.toBe(true);
|
|
68
|
+
await expect(verifyOtpSecret('000000', hash, 'server-secret-a')).resolves.toBe(false);
|
|
69
|
+
await expect(verifyOtpSecret('123456', hash, 'server-secret-b')).resolves.toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('keeps short-lived legacy SHA-256 OTP hashes verifiable', async () => {
|
|
73
|
+
const legacyHash = await hashAuthSecret('123456');
|
|
74
|
+
|
|
75
|
+
await expect(verifyOtpSecret('123456', legacyHash, 'server-secret-a')).resolves.toBe(true);
|
|
76
|
+
await expect(verifyOtpSecret('000000', legacyHash, 'server-secret-a')).resolves.toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('builds hashed and legacy lookup keys for auth secrets', async () => {
|
|
80
|
+
const keys = await authSecretLookupKeys('raw-magic-link-token');
|
|
81
|
+
|
|
82
|
+
expect(keys).toHaveLength(2);
|
|
83
|
+
expect(keys[0]).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
84
|
+
expect(keys[1]).toBe('raw-magic-link-token');
|
|
85
|
+
await expect(authSecretLookupKeys(keys[0])).resolves.toEqual([keys[0]]);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('email token storage', () => {
|
|
90
|
+
it('stores hashed email tokens while allowing raw-token lookup and deletion', async () => {
|
|
91
|
+
const db = new MemoryAuthDb();
|
|
92
|
+
const expiresAt = new Date(Date.now() + 60_000).toISOString();
|
|
93
|
+
|
|
94
|
+
await createEmailToken(db, {
|
|
95
|
+
token: 'raw-magic-link-token',
|
|
96
|
+
userId: 'user-1',
|
|
97
|
+
type: 'magic-link',
|
|
98
|
+
expiresAt,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(db.rows).toHaveLength(1);
|
|
102
|
+
expect(db.rows[0].token).toMatch(/^sha256:[a-f0-9]{64}$/);
|
|
103
|
+
expect(db.rows[0].token).not.toBe('raw-magic-link-token');
|
|
104
|
+
|
|
105
|
+
const row = await getEmailToken(db, 'raw-magic-link-token');
|
|
106
|
+
expect(row?.userId).toBe('user-1');
|
|
107
|
+
|
|
108
|
+
await deleteEmailToken(db, 'raw-magic-link-token');
|
|
109
|
+
expect(db.rows).toHaveLength(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('still accepts and deletes legacy raw email tokens', async () => {
|
|
113
|
+
const db = new MemoryAuthDb();
|
|
114
|
+
db.rows.push({
|
|
115
|
+
token: 'legacy-verify-token',
|
|
116
|
+
userId: 'user-2',
|
|
117
|
+
type: 'verify',
|
|
118
|
+
expiresAt: new Date(Date.now() + 60_000).toISOString(),
|
|
119
|
+
createdAt: new Date().toISOString(),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const row = await getEmailTokenByType(db, 'legacy-verify-token', 'verify');
|
|
123
|
+
expect(row?.userId).toBe('user-2');
|
|
124
|
+
|
|
125
|
+
await deleteEmailToken(db, 'legacy-verify-token');
|
|
126
|
+
expect(db.rows).toHaveLength(0);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('cleans up expired hashed tokens on lookup', async () => {
|
|
130
|
+
const db = new MemoryAuthDb();
|
|
131
|
+
await createEmailToken(db, {
|
|
132
|
+
token: 'expired-token',
|
|
133
|
+
userId: 'user-3',
|
|
134
|
+
type: 'password-reset',
|
|
135
|
+
expiresAt: new Date(Date.now() - 60_000).toISOString(),
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const row = await getEmailToken(db, 'expired-token');
|
|
139
|
+
expect(row).toBeNull();
|
|
140
|
+
expect(db.rows).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
});
|