silosdk 0.0.0 → 0.0.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/README.md +3 -1
- package/dist/_virtual/rolldown_runtime.cjs +29 -0
- package/dist/cli/d1.cjs +93 -0
- package/dist/cli/d1.mjs +92 -0
- package/dist/cli/index.cjs +93 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +94 -0
- package/dist/cli/init.cjs +134 -0
- package/dist/cli/init.mjs +133 -0
- package/dist/cli/kv.cjs +63 -0
- package/dist/cli/kv.mjs +60 -0
- package/dist/cli/r2.cjs +83 -0
- package/dist/cli/r2.mjs +82 -0
- package/dist/cli/wrangler.cjs +93 -0
- package/dist/cli/wrangler.mjs +89 -0
- package/dist/local/adapters/cloudflare.cjs +200 -0
- package/dist/local/adapters/cloudflare.d.cts +50 -0
- package/dist/local/adapters/cloudflare.d.mts +50 -0
- package/dist/local/adapters/cloudflare.mjs +200 -0
- package/dist/local/auth-context.cjs +14 -0
- package/dist/local/auth-context.d.cts +7 -0
- package/dist/local/auth-context.d.mts +7 -0
- package/dist/local/auth-context.mjs +12 -0
- package/dist/local/auth.cjs +109 -0
- package/dist/local/auth.d.cts +26 -0
- package/dist/local/auth.d.mts +26 -0
- package/dist/local/auth.mjs +99 -0
- package/dist/local/commit.cjs +350 -0
- package/dist/local/commit.d.cts +59 -0
- package/dist/local/commit.d.mts +59 -0
- package/dist/local/commit.mjs +349 -0
- package/dist/local/config.cjs +17 -0
- package/dist/local/config.mjs +15 -0
- package/dist/local/index.cjs +16 -0
- package/dist/local/index.d.cts +10 -0
- package/dist/local/index.d.mts +10 -0
- package/dist/local/index.mjs +9 -0
- package/dist/local/provider.cjs +204 -0
- package/dist/local/provider.d.cts +25 -0
- package/dist/local/provider.d.mts +25 -0
- package/dist/local/provider.mjs +203 -0
- package/dist/local/query-store.cjs +276 -0
- package/dist/local/query-store.mjs +274 -0
- package/dist/local/storage.cjs +71 -0
- package/dist/local/storage.d.cts +7 -0
- package/dist/local/storage.d.mts +7 -0
- package/dist/local/storage.mjs +68 -0
- package/dist/local/sync.cjs +124 -0
- package/dist/local/sync.d.cts +36 -0
- package/dist/local/sync.d.mts +36 -0
- package/dist/local/sync.mjs +122 -0
- package/dist/local/view.cjs +257 -0
- package/dist/local/view.d.cts +24 -0
- package/dist/local/view.d.mts +24 -0
- package/dist/local/view.mjs +254 -0
- package/dist/package.cjs +11 -0
- package/dist/package.mjs +5 -0
- package/dist/schema/index.cjs +276 -0
- package/dist/schema/index.d.cts +207 -0
- package/dist/schema/index.d.mts +207 -0
- package/dist/schema/index.mjs +265 -0
- package/dist/server/auth.cjs +132 -0
- package/dist/server/auth.d.cts +49 -0
- package/dist/server/auth.d.mts +49 -0
- package/dist/server/auth.mjs +122 -0
- package/dist/server/d1.cjs +120 -0
- package/dist/server/d1.mjs +116 -0
- package/dist/server/do.cjs +132 -0
- package/dist/server/do.d.cts +21 -0
- package/dist/server/do.d.mts +21 -0
- package/dist/server/do.mjs +131 -0
- package/dist/server/index.cjs +355 -0
- package/dist/server/index.d.cts +65 -0
- package/dist/server/index.d.mts +65 -0
- package/dist/server/index.mjs +348 -0
- package/dist/server/protect.cjs +34 -0
- package/dist/server/protect.d.cts +32 -0
- package/dist/server/protect.d.mts +32 -0
- package/dist/server/protect.mjs +33 -0
- package/dist/server/r2.cjs +58 -0
- package/dist/server/r2.d.cts +4 -0
- package/dist/server/r2.d.mts +4 -0
- package/dist/server/r2.mjs +53 -0
- package/package.json +55 -2
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
const require_schema_index = require('../schema/index.cjs');
|
|
2
|
+
const require_do = require('./do.cjs');
|
|
3
|
+
const require_d1 = require('./d1.cjs');
|
|
4
|
+
const require_r2 = require('./r2.cjs');
|
|
5
|
+
const require_auth = require('./auth.cjs');
|
|
6
|
+
const require_protect = require('./protect.cjs');
|
|
7
|
+
|
|
8
|
+
//#region src/server/index.ts
|
|
9
|
+
async function d1EnsureAuthSchema(db) {
|
|
10
|
+
await db.batch([db.prepare(`CREATE TABLE IF NOT EXISTS silo_users (
|
|
11
|
+
id TEXT PRIMARY KEY,
|
|
12
|
+
email TEXT UNIQUE,
|
|
13
|
+
role TEXT NOT NULL DEFAULT 'user',
|
|
14
|
+
createdAt TEXT NOT NULL
|
|
15
|
+
)`), db.prepare(`CREATE TABLE IF NOT EXISTS silo_otps (
|
|
16
|
+
email TEXT PRIMARY KEY,
|
|
17
|
+
code TEXT NOT NULL,
|
|
18
|
+
expiresAt TEXT NOT NULL
|
|
19
|
+
)`)]);
|
|
20
|
+
}
|
|
21
|
+
function createServer(config) {
|
|
22
|
+
const { auth, views, storage } = config;
|
|
23
|
+
const storageConfig = storage == null ? null : storage === true ? {} : storage || null;
|
|
24
|
+
const storageMaxSize = storageConfig?.maxSize ?? 50 * 1024 * 1024;
|
|
25
|
+
const protectedViews = /* @__PURE__ */ new Map();
|
|
26
|
+
for (const v of views) if ("_rules" in v) protectedViews.set(v.name, v);
|
|
27
|
+
let schemaReady = false;
|
|
28
|
+
async function ensureSchema(db) {
|
|
29
|
+
if (schemaReady) return;
|
|
30
|
+
await require_d1.d1EnsureSchema(db);
|
|
31
|
+
if (auth && Array.isArray(auth._providers)) await d1EnsureAuthSchema(db);
|
|
32
|
+
schemaReady = true;
|
|
33
|
+
}
|
|
34
|
+
function checkRolePermission(viewName, operation, role) {
|
|
35
|
+
if (!auth?._roles) return true;
|
|
36
|
+
const roleDef = auth._roles[role];
|
|
37
|
+
if (!roleDef) return false;
|
|
38
|
+
return (roleDef[viewName] ?? roleDef.default)[operation];
|
|
39
|
+
}
|
|
40
|
+
async function resolveUser(request, env) {
|
|
41
|
+
const url = new URL(request.url);
|
|
42
|
+
const authHeader = request.headers.get("Authorization");
|
|
43
|
+
const token = (authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null) ?? url.searchParams.get("token");
|
|
44
|
+
if (auth) {
|
|
45
|
+
if (!token) return null;
|
|
46
|
+
if (!Array.isArray(auth._providers) && auth._providers._type === "custom") {
|
|
47
|
+
const resolved = await auth._providers.resolve(token);
|
|
48
|
+
if (!resolved) return null;
|
|
49
|
+
return {
|
|
50
|
+
id: resolved.id,
|
|
51
|
+
email: null,
|
|
52
|
+
role: resolved.role ?? "user",
|
|
53
|
+
isAnonymous: false
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const user = await require_auth.resolveSession(env.SILO_SESSIONS, token);
|
|
57
|
+
if (user && url.pathname === "/sync" && user.isAnonymous) return null;
|
|
58
|
+
return user;
|
|
59
|
+
}
|
|
60
|
+
const devUser = request.headers.get("X-Dev-User");
|
|
61
|
+
return devUser ? {
|
|
62
|
+
id: devUser,
|
|
63
|
+
email: null,
|
|
64
|
+
role: "user",
|
|
65
|
+
isAnonymous: false
|
|
66
|
+
} : {
|
|
67
|
+
id: "dev",
|
|
68
|
+
email: null,
|
|
69
|
+
role: "user",
|
|
70
|
+
isAnonymous: false
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
SyncObject: require_do.SyncObject,
|
|
75
|
+
async fetch(request, env) {
|
|
76
|
+
await ensureSchema(env.SILO_DB);
|
|
77
|
+
const url = new URL(request.url);
|
|
78
|
+
const { pathname } = url;
|
|
79
|
+
if (pathname === "/health") return json({ ok: true });
|
|
80
|
+
if (auth && Array.isArray(auth._providers) && pathname.startsWith("/auth/")) return handleAuthRoute(pathname, request, env, auth);
|
|
81
|
+
if (storageConfig && pathname === "/storage/upload" && request.method === "POST") {
|
|
82
|
+
const user = await resolveUser(request, env);
|
|
83
|
+
if (!user || user.isAnonymous) return new Response("Unauthorized", { status: 401 });
|
|
84
|
+
const scope = request.headers.get("X-Asset-Scope") === "public" ? "public" : "private";
|
|
85
|
+
const contentLengthHeader = request.headers.get("Content-Length");
|
|
86
|
+
const declaredSize = contentLengthHeader ? Number(contentLengthHeader) : 0;
|
|
87
|
+
if (Number.isFinite(declaredSize) && declaredSize > storageMaxSize) return new Response("Payload too large", { status: 413 });
|
|
88
|
+
if (!request.body) return new Response("Missing request body", { status: 400 });
|
|
89
|
+
const contentType = request.headers.get("Content-Type") ?? "application/octet-stream";
|
|
90
|
+
const name = request.headers.get("X-File-Name") ?? void 0;
|
|
91
|
+
const uploaded = await require_r2.r2Put(env.SILO_STORAGE, {
|
|
92
|
+
scope,
|
|
93
|
+
userId: user.id,
|
|
94
|
+
body: request.body,
|
|
95
|
+
name,
|
|
96
|
+
contentType,
|
|
97
|
+
size: Number.isFinite(declaredSize) && declaredSize > 0 ? declaredSize : void 0
|
|
98
|
+
});
|
|
99
|
+
if (storageConfig.onUpload) {
|
|
100
|
+
if (!await storageConfig.onUpload({
|
|
101
|
+
user,
|
|
102
|
+
key: uploaded.key,
|
|
103
|
+
contentType: uploaded.contentType ?? contentType,
|
|
104
|
+
size: uploaded.size ?? 0,
|
|
105
|
+
scope,
|
|
106
|
+
env
|
|
107
|
+
})) {
|
|
108
|
+
await require_r2.r2Delete(env.SILO_STORAGE, uploaded.key);
|
|
109
|
+
return new Response("Forbidden", { status: 403 });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return json(uploaded);
|
|
113
|
+
}
|
|
114
|
+
const storageMatch = pathname.match(/^\/storage\/(.+)$/);
|
|
115
|
+
if (storageConfig && storageMatch && request.method === "GET") {
|
|
116
|
+
const key = decodeURIComponent(storageMatch[1]);
|
|
117
|
+
const parsed = require_r2.parseAssetKeyScope(key);
|
|
118
|
+
if (!parsed) return new Response("Not found", { status: 404 });
|
|
119
|
+
if (parsed.scope === "private") {
|
|
120
|
+
const user = await resolveUser(request, env);
|
|
121
|
+
if (!user) return new Response("Unauthorized", { status: 401 });
|
|
122
|
+
if (!require_r2.isAssetOwner(key, user.id)) return new Response("Forbidden", { status: 403 });
|
|
123
|
+
} else if (!storageConfig.public) {
|
|
124
|
+
if (!await resolveUser(request, env)) return new Response("Unauthorized", { status: 401 });
|
|
125
|
+
}
|
|
126
|
+
const obj = await require_r2.r2Get(env.SILO_STORAGE, key);
|
|
127
|
+
if (!obj || !obj.body) return new Response("Not found", { status: 404 });
|
|
128
|
+
const headers = new Headers();
|
|
129
|
+
if (obj.httpMetadata?.contentType) headers.set("Content-Type", obj.httpMetadata.contentType);
|
|
130
|
+
else headers.set("Content-Type", "application/octet-stream");
|
|
131
|
+
return new Response(obj.body, {
|
|
132
|
+
status: 200,
|
|
133
|
+
headers
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (storageConfig && storageMatch && request.method === "DELETE") {
|
|
137
|
+
const user = await resolveUser(request, env);
|
|
138
|
+
if (!user) return new Response("Unauthorized", { status: 401 });
|
|
139
|
+
const key = decodeURIComponent(storageMatch[1]);
|
|
140
|
+
if (!require_r2.parseAssetKeyScope(key)) return new Response("Not found", { status: 404 });
|
|
141
|
+
if (!require_r2.isAssetOwner(key, user.id)) return new Response("Forbidden", { status: 403 });
|
|
142
|
+
await require_r2.r2Delete(env.SILO_STORAGE, key);
|
|
143
|
+
return new Response(null, { status: 204 });
|
|
144
|
+
}
|
|
145
|
+
const publicMatch = pathname.match(/^\/public\/([^/]+)$/);
|
|
146
|
+
if (publicMatch) {
|
|
147
|
+
const viewName = decodeURIComponent(publicMatch[1]);
|
|
148
|
+
const protected_ = protectedViews.get(viewName);
|
|
149
|
+
const hasReadRules = protected_?._rules.read != null;
|
|
150
|
+
const hasWriteRules = protected_?._rules.write != null;
|
|
151
|
+
if (request.method === "GET") {
|
|
152
|
+
const whereParam = url.searchParams.get("where");
|
|
153
|
+
const orderParam = url.searchParams.get("order");
|
|
154
|
+
let where;
|
|
155
|
+
let order;
|
|
156
|
+
if (whereParam) try {
|
|
157
|
+
const parsed = JSON.parse(whereParam);
|
|
158
|
+
if (parsed && typeof parsed === "object") where = parsed;
|
|
159
|
+
} catch {
|
|
160
|
+
return new Response("Invalid where clause", { status: 400 });
|
|
161
|
+
}
|
|
162
|
+
if (orderParam) try {
|
|
163
|
+
const parsed = JSON.parse(orderParam);
|
|
164
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) order = parsed;
|
|
165
|
+
} catch {
|
|
166
|
+
return new Response("Invalid order clause", { status: 400 });
|
|
167
|
+
}
|
|
168
|
+
if (!hasReadRules && !checkRolePermission(viewName, "read", "anon")) {
|
|
169
|
+
const id$1 = url.searchParams.get("id");
|
|
170
|
+
if (id$1) {
|
|
171
|
+
const row = await require_d1.d1GetRow(env.SILO_DB, viewName, { id: id$1 });
|
|
172
|
+
if (!row) return new Response("Not found", { status: 404 });
|
|
173
|
+
return json(row);
|
|
174
|
+
} else {
|
|
175
|
+
const take = url.searchParams.get("take");
|
|
176
|
+
const options = {};
|
|
177
|
+
if (take) options.take = Number(take);
|
|
178
|
+
if (where) options.where = where;
|
|
179
|
+
if (order) options.order = order;
|
|
180
|
+
const pv = url.searchParams.get("parentView");
|
|
181
|
+
const pi = url.searchParams.get("parentId");
|
|
182
|
+
if (pv && pi) {
|
|
183
|
+
options.parentView = pv;
|
|
184
|
+
options.parentId = pi;
|
|
185
|
+
}
|
|
186
|
+
const cv = url.searchParams.get("childView");
|
|
187
|
+
const ci = url.searchParams.get("childId");
|
|
188
|
+
if (cv && ci) {
|
|
189
|
+
options.childView = cv;
|
|
190
|
+
options.childId = ci;
|
|
191
|
+
}
|
|
192
|
+
return json(await require_d1.d1GetRows(env.SILO_DB, viewName, options));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const user = await resolveUser(request, env);
|
|
196
|
+
if (!user && (hasReadRules || hasWriteRules)) return new Response("Unauthorized", { status: 401 });
|
|
197
|
+
if (hasReadRules) {
|
|
198
|
+
const query = where && !Array.isArray(where) ? { ...where } : {};
|
|
199
|
+
if (!await protected_._rules.read({
|
|
200
|
+
user,
|
|
201
|
+
env,
|
|
202
|
+
query
|
|
203
|
+
})) return new Response("Forbidden", { status: 403 });
|
|
204
|
+
} else if (!checkRolePermission(viewName, "read", user?.role ?? "anon")) return new Response("Forbidden", { status: 403 });
|
|
205
|
+
const id = url.searchParams.get("id");
|
|
206
|
+
if (id) {
|
|
207
|
+
const row = await require_d1.d1GetRow(env.SILO_DB, viewName, { id });
|
|
208
|
+
if (!row) return new Response("Not found", { status: 404 });
|
|
209
|
+
return json(row);
|
|
210
|
+
} else {
|
|
211
|
+
const take = url.searchParams.get("take");
|
|
212
|
+
const options = {};
|
|
213
|
+
if (take) options.take = Number(take);
|
|
214
|
+
if (where) options.where = where;
|
|
215
|
+
if (order) options.order = order;
|
|
216
|
+
const pv = url.searchParams.get("parentView");
|
|
217
|
+
const pi = url.searchParams.get("parentId");
|
|
218
|
+
if (pv && pi) {
|
|
219
|
+
options.parentView = pv;
|
|
220
|
+
options.parentId = pi;
|
|
221
|
+
}
|
|
222
|
+
const cv = url.searchParams.get("childView");
|
|
223
|
+
const ci = url.searchParams.get("childId");
|
|
224
|
+
if (cv && ci) {
|
|
225
|
+
options.childView = cv;
|
|
226
|
+
options.childId = ci;
|
|
227
|
+
}
|
|
228
|
+
return json(await require_d1.d1GetRows(env.SILO_DB, viewName, options));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (request.method === "POST") {
|
|
232
|
+
const user = await resolveUser(request, env);
|
|
233
|
+
if (!user) return new Response("Unauthorized", { status: 401 });
|
|
234
|
+
let body;
|
|
235
|
+
try {
|
|
236
|
+
body = await request.json();
|
|
237
|
+
} catch {
|
|
238
|
+
return new Response("Invalid JSON", { status: 400 });
|
|
239
|
+
}
|
|
240
|
+
if (!Array.isArray(body?.ops)) return new Response("Expected { ops: [...] }", { status: 400 });
|
|
241
|
+
if (user.isAnonymous && !protected_?._rules.write) return json({
|
|
242
|
+
error: "Anonymous users cannot write to public DB",
|
|
243
|
+
code: "ANON_WRITE_FORBIDDEN"
|
|
244
|
+
}, 403);
|
|
245
|
+
if (protected_?._rules.write) {
|
|
246
|
+
for (const op of body.ops) if (!await protected_._rules.write({
|
|
247
|
+
user,
|
|
248
|
+
env,
|
|
249
|
+
op
|
|
250
|
+
})) return new Response("Forbidden", { status: 403 });
|
|
251
|
+
} else if (!checkRolePermission(viewName, "write", user.role)) return new Response("Forbidden", { status: 403 });
|
|
252
|
+
await require_d1.d1ApplyOps(env.SILO_DB, body.ops);
|
|
253
|
+
return new Response(null, { status: 204 });
|
|
254
|
+
}
|
|
255
|
+
return new Response("Method not allowed", { status: 405 });
|
|
256
|
+
}
|
|
257
|
+
if (pathname === "/sync" && request.headers.get("Upgrade") === "websocket") {
|
|
258
|
+
const user = await resolveUser(request, env);
|
|
259
|
+
if (!user) return new Response("Unauthorized", { status: 401 });
|
|
260
|
+
const doId = env.SILO_SYNC.idFromName(user.id);
|
|
261
|
+
return env.SILO_SYNC.get(doId).fetch(request);
|
|
262
|
+
}
|
|
263
|
+
return new Response("Not found", { status: 404 });
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
async function handleAuthRoute(pathname, request, env, authInst) {
|
|
267
|
+
const sessionDuration = authInst._sessionDuration;
|
|
268
|
+
const otpProv = authInst._providers.find((p) => p._type === "otp");
|
|
269
|
+
if (pathname === "/auth/anonymous" && request.method === "POST") {
|
|
270
|
+
const { token, userId } = await require_auth.createAnonymousUser(env.SILO_DB, env.SILO_SESSIONS, sessionDuration, authInst._defaultRole);
|
|
271
|
+
return json({
|
|
272
|
+
token,
|
|
273
|
+
user: {
|
|
274
|
+
id: userId,
|
|
275
|
+
email: null,
|
|
276
|
+
role: authInst._defaultRole,
|
|
277
|
+
isAnonymous: true
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (pathname === "/auth/request" && request.method === "POST") {
|
|
282
|
+
if (!otpProv) return new Response("OTP auth not enabled", { status: 400 });
|
|
283
|
+
let body;
|
|
284
|
+
try {
|
|
285
|
+
body = await request.json();
|
|
286
|
+
} catch {
|
|
287
|
+
return new Response("Invalid JSON", { status: 400 });
|
|
288
|
+
}
|
|
289
|
+
if (!body.email) return new Response("Missing email", { status: 400 });
|
|
290
|
+
const code = await require_auth.createOtp(env.SILO_DB, body.email);
|
|
291
|
+
await otpProv.config.sendOTP(body.email, code, env);
|
|
292
|
+
return json({ ok: true });
|
|
293
|
+
}
|
|
294
|
+
if (pathname === "/auth/verify" && request.method === "POST") {
|
|
295
|
+
let body;
|
|
296
|
+
try {
|
|
297
|
+
body = await request.json();
|
|
298
|
+
} catch {
|
|
299
|
+
return new Response("Invalid JSON", { status: 400 });
|
|
300
|
+
}
|
|
301
|
+
if (!body.email || !body.otp) return new Response("Missing email or otp", { status: 400 });
|
|
302
|
+
const { valid } = await require_auth.verifyOtp(env.SILO_DB, body.email, body.otp);
|
|
303
|
+
if (!valid) return new Response("Invalid or expired code", { status: 401 });
|
|
304
|
+
const user = await require_auth.findOrCreateUser(env.SILO_DB, body.email, { role: authInst._defaultRole });
|
|
305
|
+
return json({
|
|
306
|
+
token: await require_auth.createSession(env.SILO_SESSIONS, user.id, user.email, user.role, sessionDuration),
|
|
307
|
+
user: {
|
|
308
|
+
id: user.id,
|
|
309
|
+
email: user.email,
|
|
310
|
+
role: user.role,
|
|
311
|
+
isAnonymous: false
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (pathname === "/auth/signout" && request.method === "POST") {
|
|
316
|
+
const authHeader = request.headers.get("Authorization");
|
|
317
|
+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
318
|
+
if (token) await require_auth.deleteSession(env.SILO_SESSIONS, token);
|
|
319
|
+
return json({ ok: true });
|
|
320
|
+
}
|
|
321
|
+
if (pathname === "/auth/me" && request.method === "GET") {
|
|
322
|
+
const user = await resolveSessionFromRequest(request, env);
|
|
323
|
+
if (!user) return new Response("Unauthorized", { status: 401 });
|
|
324
|
+
return json({
|
|
325
|
+
id: user.id,
|
|
326
|
+
email: user.email,
|
|
327
|
+
role: user.role,
|
|
328
|
+
isAnonymous: user.isAnonymous
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
return new Response("Not found", { status: 404 });
|
|
332
|
+
}
|
|
333
|
+
async function resolveSessionFromRequest(request, env) {
|
|
334
|
+
const authHeader = request.headers.get("Authorization");
|
|
335
|
+
const token = authHeader?.startsWith("Bearer ") ? authHeader.slice(7) : null;
|
|
336
|
+
if (!token) return null;
|
|
337
|
+
return require_auth.resolveSession(env.SILO_SESSIONS, token);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function json(data, status = 200) {
|
|
341
|
+
return new Response(JSON.stringify(data), {
|
|
342
|
+
status,
|
|
343
|
+
headers: { "Content-Type": "application/json" }
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
//#endregion
|
|
348
|
+
exports.SyncObject = require_do.SyncObject;
|
|
349
|
+
exports.createAuth = require_auth.createAuth;
|
|
350
|
+
exports.createServer = createServer;
|
|
351
|
+
exports.customProvider = require_auth.customProvider;
|
|
352
|
+
exports.otpProvider = require_auth.otpProvider;
|
|
353
|
+
exports.protect = require_protect.protect;
|
|
354
|
+
exports.resolveSession = require_auth.resolveSession;
|
|
355
|
+
exports.view = require_schema_index.view;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AssetRef, Infer, ProtectedView, View, view } from "../schema/index.cjs";
|
|
2
|
+
import { SyncObject } from "./do.cjs";
|
|
3
|
+
import { AssetScope } from "./r2.cjs";
|
|
4
|
+
import { AuthInstance, RoleDefinition, RolesConfig, ViewPermissions, createAuth, customProvider, otpProvider, resolveSession } from "./auth.cjs";
|
|
5
|
+
import { protect } from "./protect.cjs";
|
|
6
|
+
|
|
7
|
+
//#region src/server/index.d.ts
|
|
8
|
+
|
|
9
|
+
interface Env {
|
|
10
|
+
SILO_SYNC: DurableObjectNamespace;
|
|
11
|
+
SILO_DB: D1Database;
|
|
12
|
+
/** Required when using built-in auth (Phase 4). */
|
|
13
|
+
SILO_SESSIONS: KVNamespace;
|
|
14
|
+
/** Required when storage is enabled. */
|
|
15
|
+
SILO_STORAGE: R2Bucket;
|
|
16
|
+
}
|
|
17
|
+
type StorageConfig = {
|
|
18
|
+
/**
|
|
19
|
+
* If true, public-scope assets can be read without auth.
|
|
20
|
+
* Private-scope assets always require owner auth.
|
|
21
|
+
*/
|
|
22
|
+
public?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Max upload payload in bytes. Defaults to 50MB.
|
|
25
|
+
*/
|
|
26
|
+
maxSize?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional upload gate. Return false to reject.
|
|
29
|
+
*/
|
|
30
|
+
onUpload?: (ctx: {
|
|
31
|
+
user: {
|
|
32
|
+
id: string;
|
|
33
|
+
email: string | null;
|
|
34
|
+
role: string;
|
|
35
|
+
isAnonymous: boolean;
|
|
36
|
+
};
|
|
37
|
+
key: string;
|
|
38
|
+
contentType: string;
|
|
39
|
+
size: number;
|
|
40
|
+
scope: AssetScope;
|
|
41
|
+
env: Env;
|
|
42
|
+
}) => boolean | Promise<boolean>;
|
|
43
|
+
};
|
|
44
|
+
type ServerConfig = {
|
|
45
|
+
/**
|
|
46
|
+
* View definitions to serve. Pass a `ProtectedView` (from `protect()`) to
|
|
47
|
+
* apply per-view access-control rules.
|
|
48
|
+
*/
|
|
49
|
+
views: Array<View | ProtectedView>;
|
|
50
|
+
/**
|
|
51
|
+
* Auth configuration. Use `createAuth()` to configure built-in passwordless
|
|
52
|
+
* auth or BYO token validation. Omit for dev/open mode.
|
|
53
|
+
*/
|
|
54
|
+
auth?: AuthInstance;
|
|
55
|
+
/**
|
|
56
|
+
* Object storage configuration (R2). Omit to disable storage routes.
|
|
57
|
+
*/
|
|
58
|
+
storage?: boolean | StorageConfig;
|
|
59
|
+
};
|
|
60
|
+
declare function createServer(config: ServerConfig): {
|
|
61
|
+
SyncObject: typeof SyncObject;
|
|
62
|
+
fetch(request: Request, env: Env): Promise<Response>;
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { type AssetRef, type AuthInstance, type Infer, type ProtectedView, type RoleDefinition, type RolesConfig, ServerConfig, StorageConfig, SyncObject, type View, type ViewPermissions, createAuth, createServer, customProvider, otpProvider, protect, resolveSession, view };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { AssetRef, Infer, ProtectedView, View, view } from "../schema/index.mjs";
|
|
2
|
+
import { SyncObject } from "./do.mjs";
|
|
3
|
+
import { AssetScope } from "./r2.mjs";
|
|
4
|
+
import { AuthInstance, RoleDefinition, RolesConfig, ViewPermissions, createAuth, customProvider, otpProvider, resolveSession } from "./auth.mjs";
|
|
5
|
+
import { protect } from "./protect.mjs";
|
|
6
|
+
|
|
7
|
+
//#region src/server/index.d.ts
|
|
8
|
+
|
|
9
|
+
interface Env {
|
|
10
|
+
SILO_SYNC: DurableObjectNamespace;
|
|
11
|
+
SILO_DB: D1Database;
|
|
12
|
+
/** Required when using built-in auth (Phase 4). */
|
|
13
|
+
SILO_SESSIONS: KVNamespace;
|
|
14
|
+
/** Required when storage is enabled. */
|
|
15
|
+
SILO_STORAGE: R2Bucket;
|
|
16
|
+
}
|
|
17
|
+
type StorageConfig = {
|
|
18
|
+
/**
|
|
19
|
+
* If true, public-scope assets can be read without auth.
|
|
20
|
+
* Private-scope assets always require owner auth.
|
|
21
|
+
*/
|
|
22
|
+
public?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Max upload payload in bytes. Defaults to 50MB.
|
|
25
|
+
*/
|
|
26
|
+
maxSize?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Optional upload gate. Return false to reject.
|
|
29
|
+
*/
|
|
30
|
+
onUpload?: (ctx: {
|
|
31
|
+
user: {
|
|
32
|
+
id: string;
|
|
33
|
+
email: string | null;
|
|
34
|
+
role: string;
|
|
35
|
+
isAnonymous: boolean;
|
|
36
|
+
};
|
|
37
|
+
key: string;
|
|
38
|
+
contentType: string;
|
|
39
|
+
size: number;
|
|
40
|
+
scope: AssetScope;
|
|
41
|
+
env: Env;
|
|
42
|
+
}) => boolean | Promise<boolean>;
|
|
43
|
+
};
|
|
44
|
+
type ServerConfig = {
|
|
45
|
+
/**
|
|
46
|
+
* View definitions to serve. Pass a `ProtectedView` (from `protect()`) to
|
|
47
|
+
* apply per-view access-control rules.
|
|
48
|
+
*/
|
|
49
|
+
views: Array<View | ProtectedView>;
|
|
50
|
+
/**
|
|
51
|
+
* Auth configuration. Use `createAuth()` to configure built-in passwordless
|
|
52
|
+
* auth or BYO token validation. Omit for dev/open mode.
|
|
53
|
+
*/
|
|
54
|
+
auth?: AuthInstance;
|
|
55
|
+
/**
|
|
56
|
+
* Object storage configuration (R2). Omit to disable storage routes.
|
|
57
|
+
*/
|
|
58
|
+
storage?: boolean | StorageConfig;
|
|
59
|
+
};
|
|
60
|
+
declare function createServer(config: ServerConfig): {
|
|
61
|
+
SyncObject: typeof SyncObject;
|
|
62
|
+
fetch(request: Request, env: Env): Promise<Response>;
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { type AssetRef, type AuthInstance, type Infer, type ProtectedView, type RoleDefinition, type RolesConfig, ServerConfig, StorageConfig, SyncObject, type View, type ViewPermissions, createAuth, createServer, customProvider, otpProvider, protect, resolveSession, view };
|