@supacloud/lite 0.33.2 → 0.34.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/CHANGELOG.md +15 -0
- package/README.md +85 -2
- package/deploy/README.md +110 -0
- package/deploy/caddy/Caddyfile +15 -0
- package/deploy/systemd/supacloud-lite.service +32 -0
- package/dist/cli.js +433 -49
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +355 -23
- package/dist/project-runtime.d.ts +2 -0
- package/dist/project-runtime.d.ts.map +1 -1
- package/dist/runtime/auth/proxy.d.ts +28 -0
- package/dist/runtime/auth/proxy.d.ts.map +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/node/bun-server.d.ts +2 -0
- package/dist/runtime/node/bun-server.d.ts.map +1 -1
- package/dist/runtime/node/native/readiness.d.ts +2 -0
- package/dist/runtime/node/native/readiness.d.ts.map +1 -1
- package/dist/runtime/node/production-readiness.d.ts +31 -0
- package/dist/runtime/node/production-readiness.d.ts.map +1 -0
- package/dist/runtime/realtime/engine.d.ts +22 -0
- package/dist/runtime/realtime/engine.d.ts.map +1 -1
- package/dist/runtime/storage/handler.d.ts +6 -0
- package/dist/runtime/storage/handler.d.ts.map +1 -1
- package/package.json +2 -1
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export { S3StorageDriver, type S3StorageClientLike, type S3StorageDriverOptions,
|
|
|
6
6
|
export type { EdgeFunction, FunctionContext } from './runtime/functions/handler.js';
|
|
7
7
|
export type { PgredisCacheBinding } from './runtime/functions/pgredis.js';
|
|
8
8
|
export { createSupAuthLiteIdentity, type ExternalIdentityVerifier, type VerifiedSupAuthContext } from './runtime/identity.js';
|
|
9
|
+
export { createExternalAuthProxy, type ExternalAuthProxy, type ExternalAuthProxyOptions, } from './runtime/auth/proxy.js';
|
|
9
10
|
export { inspectGraphql, type GraphqlCapability, type GraphqlOptions } from './runtime/graphql.js';
|
|
10
11
|
export type { RuntimeMode } from './runtime/functions/profile.js';
|
|
11
12
|
export { SUPACLOUD_LITE_VERSION } from './version.js';
|
|
@@ -14,6 +15,7 @@ export { serveBun } from './runtime/node/bun-server.js';
|
|
|
14
15
|
export type { RunningServer, ServerHandle, ServeOptions } from './runtime/node/bun-server.js';
|
|
15
16
|
export { createNativeEngine, ensurePostgres, isNativeEngineSupported, type NativeEngineOptions, } from './runtime/node/native/engine.js';
|
|
16
17
|
export { POWERSYNC_PUBLICATION, POWERSYNC_REPLICATION_ROLE, buildPowerSyncPostgresArgs, ensurePowerSyncReplicationCatalog, validatePowerSyncReplicationOptions, writePowerSyncHba, type NativeReplicationOptions, type NativeReplicationTlsOptions, type PowerSyncReplicationOptions, } from './runtime/node/native/replication.js';
|
|
18
|
+
export { productionReadiness, type ProductionReadiness, type ProductionReadinessInput, } from './runtime/node/production-readiness.js';
|
|
17
19
|
export { inspectPowerSyncReadiness, liteCapabilities, type LiteCapabilityStatus, type LiteDoctorReport, type PowerSyncReadiness, } from './runtime/node/native/readiness.js';
|
|
18
20
|
export { inspectPostgresExtensions, type ExtensionInventorySource, type ExtensionInventoryStatus, type PostgresExtension, type PostgresExtensionInventory, } from './runtime/node/native/extensions.js';
|
|
19
21
|
export { createProjectBackend, ensureProjectSecrets, mintProjectKeys, resolveDatabaseEngine, resolveNativeReplicationOptions, resolveStorageBackend, resolveProjectPaths, startProjectServer, } from './project-runtime.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,iBAAiB,EAClC,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,SAAS,EACT,OAAO,EACP,SAAS,GACV,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,aAAa,IAAI,mBAAmB,EACpC,oBAAoB,GACrB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,GACvB,MAAM,gCAAgC,CAAA;AACvC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AACnF,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAE,KAAK,wBAAwB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC7H,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAClG,YAAY,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,KAAK,mBAAmB,GACzB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,iCAAiC,EACjC,mCAAmC,EACnC,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,GACjC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACL,yBAAyB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,GAChC,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,+BAA+B,EAC/B,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,GACf,MAAM,sBAAsB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,IAAI,iBAAiB,EAClC,kBAAkB,EAClB,SAAS,EACT,aAAa,EACb,SAAS,EACT,OAAO,EACP,SAAS,GACV,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,aAAa,IAAI,mBAAmB,EACpC,oBAAoB,GACrB,MAAM,oBAAoB,CAAA;AAC3B,YAAY,EACV,UAAU,EACV,MAAM,EACN,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AACjE,OAAO,EACL,eAAe,EACf,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,GACvB,MAAM,gCAAgC,CAAA;AACvC,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAA;AACnF,YAAY,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACzE,OAAO,EAAE,yBAAyB,EAAE,KAAK,wBAAwB,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAC7H,OAAO,EACL,uBAAuB,EACvB,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,cAAc,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAClG,YAAY,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACvD,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAA;AAC7F,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,uBAAuB,EACvB,KAAK,mBAAmB,GACzB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,qBAAqB,EACrB,0BAA0B,EAC1B,0BAA0B,EAC1B,iCAAiC,EACjC,mCAAmC,EACnC,iBAAiB,EACjB,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,GACjC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EACL,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,GACxB,MAAM,oCAAoC,CAAA;AAC3C,OAAO,EACL,yBAAyB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,iBAAiB,EACtB,KAAK,0BAA0B,GAChC,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,eAAe,EACf,qBAAqB,EACrB,+BAA+B,EAC/B,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,cAAc,EACd,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAA;AACtB,YAAY,EACV,wBAAwB,EACxB,cAAc,EACd,cAAc,EACd,YAAY,EACZ,qBAAqB,EACrB,cAAc,EACd,kBAAkB,EAClB,oBAAoB,EACpB,cAAc,GACf,MAAM,sBAAsB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -88,7 +88,7 @@ function randomToken(bytes = 32) {
|
|
|
88
88
|
// package.json
|
|
89
89
|
var package_default = {
|
|
90
90
|
name: "@supacloud/lite",
|
|
91
|
-
version: "0.
|
|
91
|
+
version: "0.34.0",
|
|
92
92
|
description: "Bun-native, single-project Supabase-compatible backend powered by PGlite or native PostgreSQL",
|
|
93
93
|
type: "module",
|
|
94
94
|
license: "Apache-2.0",
|
|
@@ -108,6 +108,7 @@ var package_default = {
|
|
|
108
108
|
"dist",
|
|
109
109
|
"!dist/standalone",
|
|
110
110
|
"README.md",
|
|
111
|
+
"deploy",
|
|
111
112
|
"CHANGELOG.md",
|
|
112
113
|
"RELEASING.md",
|
|
113
114
|
"THIRD_PARTY_NOTICES.md",
|
|
@@ -9524,10 +9525,21 @@ async function sha256Hex2(text) {
|
|
|
9524
9525
|
}
|
|
9525
9526
|
|
|
9526
9527
|
// src/runtime/realtime/engine.ts
|
|
9528
|
+
function normalizeBroadcastTopic(raw) {
|
|
9529
|
+
const topic = raw.trim().replace(/^realtime:/, "");
|
|
9530
|
+
if (!topic)
|
|
9531
|
+
throw new Error("broadcast topic is required");
|
|
9532
|
+
return topic;
|
|
9533
|
+
}
|
|
9534
|
+
|
|
9527
9535
|
class RealtimeEngine {
|
|
9528
9536
|
db;
|
|
9529
9537
|
jwtSecret;
|
|
9530
9538
|
resolveExternalToken;
|
|
9539
|
+
static MAX_HTTP_BROADCAST_MESSAGES = 100;
|
|
9540
|
+
static MAX_HTTP_BROADCAST_TOPIC_BYTES = 256;
|
|
9541
|
+
static MAX_HTTP_BROADCAST_EVENT_BYTES = 256;
|
|
9542
|
+
static MAX_HTTP_BROADCAST_PAYLOAD_BYTES = 1024 * 1024;
|
|
9531
9543
|
connections = new Set;
|
|
9532
9544
|
bindingCounter = 1;
|
|
9533
9545
|
phxRefCounter = 1;
|
|
@@ -9574,6 +9586,47 @@ class RealtimeEngine {
|
|
|
9574
9586
|
return { read: false, write: false };
|
|
9575
9587
|
}
|
|
9576
9588
|
}
|
|
9589
|
+
async broadcastHttp(messages, ctx) {
|
|
9590
|
+
if (messages.length === 0)
|
|
9591
|
+
throw new Error("messages must not be empty");
|
|
9592
|
+
if (messages.length > RealtimeEngine.MAX_HTTP_BROADCAST_MESSAGES) {
|
|
9593
|
+
throw new Error(`messages must contain at most ${RealtimeEngine.MAX_HTTP_BROADCAST_MESSAGES} items`);
|
|
9594
|
+
}
|
|
9595
|
+
const normalized = [];
|
|
9596
|
+
for (const message of messages) {
|
|
9597
|
+
if (typeof message.topic !== "string")
|
|
9598
|
+
throw new Error("broadcast topic must be a string");
|
|
9599
|
+
if (typeof message.event !== "string")
|
|
9600
|
+
throw new Error("broadcast event must be a string");
|
|
9601
|
+
const topic = normalizeBroadcastTopic(message.topic);
|
|
9602
|
+
if (new TextEncoder().encode(topic).byteLength > RealtimeEngine.MAX_HTTP_BROADCAST_TOPIC_BYTES) {
|
|
9603
|
+
throw new Error("broadcast topic is too large");
|
|
9604
|
+
}
|
|
9605
|
+
const event = message.event.trim();
|
|
9606
|
+
if (!event)
|
|
9607
|
+
throw new Error("broadcast event is required");
|
|
9608
|
+
if (new TextEncoder().encode(event).byteLength > RealtimeEngine.MAX_HTTP_BROADCAST_EVENT_BYTES) {
|
|
9609
|
+
throw new Error("broadcast event is too large");
|
|
9610
|
+
}
|
|
9611
|
+
let payloadText;
|
|
9612
|
+
try {
|
|
9613
|
+
payloadText = JSON.stringify(message.payload ?? null);
|
|
9614
|
+
} catch {
|
|
9615
|
+
throw new Error("broadcast payload must be JSON serializable");
|
|
9616
|
+
}
|
|
9617
|
+
if (new TextEncoder().encode(payloadText).byteLength > RealtimeEngine.MAX_HTTP_BROADCAST_PAYLOAD_BYTES) {
|
|
9618
|
+
throw new Error("broadcast payload is too large");
|
|
9619
|
+
}
|
|
9620
|
+
if (message.private === true) {
|
|
9621
|
+
const access = await this.authorizePrivate(topic, ctx);
|
|
9622
|
+
if (!access.write)
|
|
9623
|
+
throw new Error(`not authorized to broadcast on private topic: ${topic}`);
|
|
9624
|
+
}
|
|
9625
|
+
normalized.push({ topic, event, payload: message.payload ?? null });
|
|
9626
|
+
}
|
|
9627
|
+
for (const message of normalized)
|
|
9628
|
+
this.dispatchDbBroadcast(message);
|
|
9629
|
+
}
|
|
9577
9630
|
dispatchDbBroadcast(msg) {
|
|
9578
9631
|
const full = `realtime:${msg.topic}`;
|
|
9579
9632
|
for (const conn of this.connections) {
|
|
@@ -11936,6 +11989,11 @@ class StorageHandler {
|
|
|
11936
11989
|
const rest = url.pathname.replace(/^\/storage\/v1\/?/, "").replace(/\/+$/, "");
|
|
11937
11990
|
const method = req.method.toUpperCase();
|
|
11938
11991
|
try {
|
|
11992
|
+
if (rest === "status" && method === "GET") {
|
|
11993
|
+
return json3(200, { status: "ok", name: "supacloud-lite-storage", version: SUPACLOUD_LITE_VERSION });
|
|
11994
|
+
}
|
|
11995
|
+
if (rest === "metrics" && method === "GET")
|
|
11996
|
+
return await this.metrics(ctx);
|
|
11939
11997
|
if (rest === "bucket" && method === "POST")
|
|
11940
11998
|
return await this.createBucket(req, ctx);
|
|
11941
11999
|
if (rest === "bucket" && method === "GET")
|
|
@@ -12056,6 +12114,37 @@ class StorageHandler {
|
|
|
12056
12114
|
release();
|
|
12057
12115
|
}
|
|
12058
12116
|
}
|
|
12117
|
+
async metrics(ctx) {
|
|
12118
|
+
const denied = this.requireService(ctx);
|
|
12119
|
+
if (denied)
|
|
12120
|
+
return denied;
|
|
12121
|
+
const [buckets, objects] = await Promise.all([
|
|
12122
|
+
this.db.query(`select count(*)::int as count from storage.buckets`),
|
|
12123
|
+
this.db.query(`select count(*)::int as count,
|
|
12124
|
+
coalesce(sum(case when metadata->>'size' ~ '^[0-9]+$' then (metadata->>'size')::bigint else 0 end), 0) as bytes
|
|
12125
|
+
from storage.objects`)
|
|
12126
|
+
]);
|
|
12127
|
+
const bucketCount = buckets.rows[0]?.count ?? 0;
|
|
12128
|
+
const objectCount = objects.rows[0]?.count ?? 0;
|
|
12129
|
+
const objectBytes = Number(objects.rows[0]?.bytes ?? 0);
|
|
12130
|
+
const body = [
|
|
12131
|
+
"# HELP storage_buckets_total Number of storage buckets.",
|
|
12132
|
+
"# TYPE storage_buckets_total gauge",
|
|
12133
|
+
`storage_buckets_total ${bucketCount}`,
|
|
12134
|
+
"# HELP storage_objects_total Number of storage object metadata rows.",
|
|
12135
|
+
"# TYPE storage_objects_total gauge",
|
|
12136
|
+
`storage_objects_total ${objectCount}`,
|
|
12137
|
+
"# HELP storage_object_bytes_total Sum of declared object sizes in bytes.",
|
|
12138
|
+
"# TYPE storage_object_bytes_total gauge",
|
|
12139
|
+
`storage_object_bytes_total ${Number.isFinite(objectBytes) ? objectBytes : 0}`,
|
|
12140
|
+
""
|
|
12141
|
+
].join(`
|
|
12142
|
+
`);
|
|
12143
|
+
return new Response(body, {
|
|
12144
|
+
status: 200,
|
|
12145
|
+
headers: { "content-type": "text/plain; version=0.0.4; charset=utf-8" }
|
|
12146
|
+
});
|
|
12147
|
+
}
|
|
12059
12148
|
requireService(ctx) {
|
|
12060
12149
|
if (ctx.role !== "service_role") {
|
|
12061
12150
|
return storageError(403, "Unauthorized", "Bucket management requires the service_role key");
|
|
@@ -14174,6 +14263,9 @@ ${msg.text}` : `[mail] to=${msg.to} subject="${msg.subject}"`));
|
|
|
14174
14263
|
if (smsInbox && (path === "/sms-inbox" || path.startsWith("/sms-inbox/"))) {
|
|
14175
14264
|
return withCors(smsInbox.serve(req, url));
|
|
14176
14265
|
}
|
|
14266
|
+
if (path === "/storage/v1/status" && req.method === "GET") {
|
|
14267
|
+
return withCors(await storage.handle(req, { role: "anon", claims: null }, url));
|
|
14268
|
+
}
|
|
14177
14269
|
if (path.startsWith("/storage/v1/object/public/") || path.startsWith("/storage/v1/object/sign/") || path.startsWith("/storage/v1/render/image/public/") || path.startsWith("/storage/v1/render/image/sign/")) {
|
|
14178
14270
|
if (req.method === "GET" || req.method === "HEAD") {
|
|
14179
14271
|
return withCors(await storage.handle(req, { role: "anon", claims: null }, url));
|
|
@@ -14209,6 +14301,50 @@ ${msg.text}` : `[mail] to=${msg.to} subject="${msg.subject}"`));
|
|
|
14209
14301
|
const ctx = await resolveContext(req, url);
|
|
14210
14302
|
if (ctx instanceof Response)
|
|
14211
14303
|
return ctx;
|
|
14304
|
+
if (path === "/realtime/v1/api/broadcast" || path === "/realtime/v1/api/broadcast/") {
|
|
14305
|
+
if (req.method !== "POST") {
|
|
14306
|
+
return withCors(new Response(JSON.stringify({ message: "Method not allowed" }), {
|
|
14307
|
+
status: 405,
|
|
14308
|
+
headers: { "content-type": "application/json", allow: "POST" }
|
|
14309
|
+
}));
|
|
14310
|
+
}
|
|
14311
|
+
const contentType = req.headers.get("content-type") ?? "";
|
|
14312
|
+
if (contentType && !contentType.includes("application/json")) {
|
|
14313
|
+
return withCors(new Response(JSON.stringify({ message: "Content-Type must be application/json" }), {
|
|
14314
|
+
status: 415,
|
|
14315
|
+
headers: { "content-type": "application/json" }
|
|
14316
|
+
}));
|
|
14317
|
+
}
|
|
14318
|
+
let body;
|
|
14319
|
+
try {
|
|
14320
|
+
body = await req.json();
|
|
14321
|
+
} catch {
|
|
14322
|
+
return withCors(new Response(JSON.stringify({ message: "Invalid JSON body" }), {
|
|
14323
|
+
status: 400,
|
|
14324
|
+
headers: { "content-type": "application/json" }
|
|
14325
|
+
}));
|
|
14326
|
+
}
|
|
14327
|
+
const messages = typeof body === "object" && body !== null && "messages" in body ? body.messages : undefined;
|
|
14328
|
+
if (!Array.isArray(messages)) {
|
|
14329
|
+
return withCors(new Response(JSON.stringify({ message: "messages must be an array" }), {
|
|
14330
|
+
status: 400,
|
|
14331
|
+
headers: { "content-type": "application/json" }
|
|
14332
|
+
}));
|
|
14333
|
+
}
|
|
14334
|
+
try {
|
|
14335
|
+
await realtime.broadcastHttp(messages, ctx);
|
|
14336
|
+
} catch (error) {
|
|
14337
|
+
const message = error instanceof Error ? error.message : "Broadcast failed";
|
|
14338
|
+
return withCors(new Response(JSON.stringify({ message }), {
|
|
14339
|
+
status: message.startsWith("not authorized") ? 403 : 400,
|
|
14340
|
+
headers: { "content-type": "application/json" }
|
|
14341
|
+
}));
|
|
14342
|
+
}
|
|
14343
|
+
return withCors(new Response(JSON.stringify({ message: "Broadcast sent" }), {
|
|
14344
|
+
status: 202,
|
|
14345
|
+
headers: { "content-type": "application/json" }
|
|
14346
|
+
}));
|
|
14347
|
+
}
|
|
14212
14348
|
if (path.startsWith("/rest/v1"))
|
|
14213
14349
|
return withCors(await rest.handle(req, ctx, url));
|
|
14214
14350
|
if (path === "/graphql/v1" || path === "/graphql/v1/")
|
|
@@ -14410,6 +14546,118 @@ class S3StorageDriver {
|
|
|
14410
14546
|
return `${this.prefix}${normalizeKey(key)}`;
|
|
14411
14547
|
}
|
|
14412
14548
|
}
|
|
14549
|
+
// src/runtime/auth/proxy.ts
|
|
14550
|
+
var ALLOWED_ROUTES = {
|
|
14551
|
+
"/auth/v1/oauth/token": new Set(["POST"]),
|
|
14552
|
+
"/auth/v1/oauth/userinfo": new Set(["GET"]),
|
|
14553
|
+
"/auth/v1/logout": new Set(["GET", "POST"])
|
|
14554
|
+
};
|
|
14555
|
+
var FORWARD_REQUEST_HEADERS = ["accept", "authorization", "content-type", "apikey"];
|
|
14556
|
+
var FORWARD_RESPONSE_HEADERS = [
|
|
14557
|
+
"content-type",
|
|
14558
|
+
"cache-control",
|
|
14559
|
+
"retry-after",
|
|
14560
|
+
"www-authenticate"
|
|
14561
|
+
];
|
|
14562
|
+
function proxyError(status, code, message, allow) {
|
|
14563
|
+
const headers = new Headers({
|
|
14564
|
+
"content-type": "application/json; charset=utf-8",
|
|
14565
|
+
"cache-control": "no-store"
|
|
14566
|
+
});
|
|
14567
|
+
if (allow)
|
|
14568
|
+
headers.set("allow", allow);
|
|
14569
|
+
return new Response(JSON.stringify({ code, message }), { status, headers });
|
|
14570
|
+
}
|
|
14571
|
+
function upstreamBase(issuer) {
|
|
14572
|
+
let url;
|
|
14573
|
+
try {
|
|
14574
|
+
url = new URL(issuer);
|
|
14575
|
+
} catch {
|
|
14576
|
+
throw new TypeError("External auth issuer must be an absolute URL");
|
|
14577
|
+
}
|
|
14578
|
+
const loopback = url.hostname === "127.0.0.1" || url.hostname === "localhost" || url.hostname === "[::1]" || url.hostname === "::1";
|
|
14579
|
+
if (url.protocol !== "https:" && !(url.protocol === "http:" && loopback)) {
|
|
14580
|
+
throw new TypeError("External auth issuer must use HTTPS (HTTP is allowed only on loopback)");
|
|
14581
|
+
}
|
|
14582
|
+
if (url.username || url.password || url.search || url.hash) {
|
|
14583
|
+
throw new TypeError("External auth issuer must not contain credentials, query or fragment");
|
|
14584
|
+
}
|
|
14585
|
+
if (url.pathname.replace(/\/+$/, "") !== "/auth/v1") {
|
|
14586
|
+
throw new TypeError("External auth issuer must end with /auth/v1");
|
|
14587
|
+
}
|
|
14588
|
+
return url;
|
|
14589
|
+
}
|
|
14590
|
+
function redirectOrigins(base, extra = []) {
|
|
14591
|
+
const origins = new Set([base.origin]);
|
|
14592
|
+
for (const value of extra) {
|
|
14593
|
+
let origin;
|
|
14594
|
+
try {
|
|
14595
|
+
origin = new URL(value);
|
|
14596
|
+
} catch {
|
|
14597
|
+
throw new TypeError("External auth redirect origin must be an absolute URL");
|
|
14598
|
+
}
|
|
14599
|
+
const loopback = origin.hostname === "127.0.0.1" || origin.hostname === "localhost" || origin.hostname === "[::1]" || origin.hostname === "::1";
|
|
14600
|
+
if (origin.protocol !== "https:" && !(origin.protocol === "http:" && loopback)) {
|
|
14601
|
+
throw new TypeError("External auth redirect origin must use HTTPS (HTTP is allowed only on loopback)");
|
|
14602
|
+
}
|
|
14603
|
+
if (origin.username || origin.password || origin.search || origin.hash) {
|
|
14604
|
+
throw new TypeError("External auth redirect origin must not contain credentials, query or fragment");
|
|
14605
|
+
}
|
|
14606
|
+
origins.add(origin.origin);
|
|
14607
|
+
}
|
|
14608
|
+
return origins;
|
|
14609
|
+
}
|
|
14610
|
+
function createExternalAuthProxy(options) {
|
|
14611
|
+
const base = upstreamBase(options.issuer);
|
|
14612
|
+
const allowedRedirects = redirectOrigins(base, options.allowedRedirectOrigins);
|
|
14613
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
14614
|
+
if (typeof fetcher !== "function")
|
|
14615
|
+
throw new TypeError("External auth proxy requires fetch");
|
|
14616
|
+
return async (request) => {
|
|
14617
|
+
const source = new URL(request.url);
|
|
14618
|
+
const methods = ALLOWED_ROUTES[source.pathname];
|
|
14619
|
+
if (!methods)
|
|
14620
|
+
return null;
|
|
14621
|
+
if (!methods.has(request.method)) {
|
|
14622
|
+
return proxyError(405, "AUTH_PROXY_METHOD_NOT_ALLOWED", "Auth proxy method is not allowed", [...methods].join(", "));
|
|
14623
|
+
}
|
|
14624
|
+
const target = new URL(source.pathname + source.search, base.origin);
|
|
14625
|
+
const headers = new Headers;
|
|
14626
|
+
for (const name of FORWARD_REQUEST_HEADERS) {
|
|
14627
|
+
const value = request.headers.get(name);
|
|
14628
|
+
if (value !== null)
|
|
14629
|
+
headers.set(name, value);
|
|
14630
|
+
}
|
|
14631
|
+
const body = request.method === "GET" || request.method === "HEAD" ? undefined : await request.arrayBuffer();
|
|
14632
|
+
let upstream;
|
|
14633
|
+
try {
|
|
14634
|
+
upstream = await fetcher(target, {
|
|
14635
|
+
method: request.method,
|
|
14636
|
+
headers,
|
|
14637
|
+
body,
|
|
14638
|
+
redirect: "manual"
|
|
14639
|
+
});
|
|
14640
|
+
} catch {
|
|
14641
|
+
return proxyError(503, "AUTH_PROXY_UNAVAILABLE", "Identity service is temporarily unavailable");
|
|
14642
|
+
}
|
|
14643
|
+
const responseHeaders = new Headers;
|
|
14644
|
+
for (const name of FORWARD_RESPONSE_HEADERS) {
|
|
14645
|
+
const value = upstream.headers.get(name);
|
|
14646
|
+
if (value !== null)
|
|
14647
|
+
responseHeaders.set(name, value);
|
|
14648
|
+
}
|
|
14649
|
+
const location = upstream.headers.get("location");
|
|
14650
|
+
if (location !== null) {
|
|
14651
|
+
const resolved = new URL(location, base.origin);
|
|
14652
|
+
if (!allowedRedirects.has(resolved.origin)) {
|
|
14653
|
+
return proxyError(502, "AUTH_PROXY_REDIRECT_REJECTED", "Identity service redirect was rejected");
|
|
14654
|
+
}
|
|
14655
|
+
responseHeaders.set("location", resolved.href);
|
|
14656
|
+
}
|
|
14657
|
+
responseHeaders.set("cache-control", "no-store");
|
|
14658
|
+
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
|
|
14659
|
+
};
|
|
14660
|
+
}
|
|
14413
14661
|
// src/runtime/node/fs-driver.ts
|
|
14414
14662
|
import { mkdir as mkdir2, readFile as readFile2, rename, rm, writeFile } from "fs/promises";
|
|
14415
14663
|
import { dirname as dirname2, isAbsolute, join, relative, resolve as resolvePath, sep } from "path";
|
|
@@ -14480,7 +14728,11 @@ async function serveBun(backend, opts = {}) {
|
|
|
14480
14728
|
const ip = srv.requestIP(req)?.address;
|
|
14481
14729
|
if (ip)
|
|
14482
14730
|
headers.set("x-supacloud-lite-remote-addr", ip);
|
|
14483
|
-
|
|
14731
|
+
const request = new Request(req, { headers });
|
|
14732
|
+
const hookResponse = await opts.beforeRequest?.(request);
|
|
14733
|
+
if (hookResponse)
|
|
14734
|
+
return hookResponse;
|
|
14735
|
+
return backend.fetch(request);
|
|
14484
14736
|
},
|
|
14485
14737
|
websocket: {
|
|
14486
14738
|
open(ws) {
|
|
@@ -15614,6 +15866,80 @@ function removeStalePidFile(pidPath) {
|
|
|
15614
15866
|
}
|
|
15615
15867
|
} catch {}
|
|
15616
15868
|
}
|
|
15869
|
+
// src/runtime/node/production-readiness.ts
|
|
15870
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve3 } from "path";
|
|
15871
|
+
var SNAPSHOT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
15872
|
+
function within(parent, child) {
|
|
15873
|
+
const rel = relative2(resolve3(parent), resolve3(child));
|
|
15874
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute2(rel);
|
|
15875
|
+
}
|
|
15876
|
+
function loopback(host) {
|
|
15877
|
+
return host === "127.0.0.1" || host === "localhost" || host === "::1" || host === "[::1]";
|
|
15878
|
+
}
|
|
15879
|
+
function absoluteHttpUrl(value) {
|
|
15880
|
+
try {
|
|
15881
|
+
const url = new URL(value);
|
|
15882
|
+
return (url.protocol === "https:" || url.protocol === "http:" && loopback(url.hostname)) && !url.username && !url.password;
|
|
15883
|
+
} catch {
|
|
15884
|
+
return false;
|
|
15885
|
+
}
|
|
15886
|
+
}
|
|
15887
|
+
function productionReadiness(input) {
|
|
15888
|
+
const blockers = [];
|
|
15889
|
+
const warnings = [];
|
|
15890
|
+
const now = input.now ?? Date.now();
|
|
15891
|
+
if (input.runtimeMode !== "strict") {
|
|
15892
|
+
blockers.push("RUNTIME_MODE_NOT_STRICT: production functions require strict runtime mode");
|
|
15893
|
+
}
|
|
15894
|
+
if (input.storageBackend === "memory") {
|
|
15895
|
+
blockers.push("MEMORY_STORAGE_NOT_DURABLE: memory storage loses objects on restart");
|
|
15896
|
+
}
|
|
15897
|
+
if (input.storageBackend === "fs") {
|
|
15898
|
+
warnings.push("LOCAL_STORAGE_REQUIRES_DURABLE_VOLUME_AND_BACKUP");
|
|
15899
|
+
}
|
|
15900
|
+
if (input.storageBackend === "s3") {
|
|
15901
|
+
warnings.push("S3_OBJECTS_NOT_INCLUDED_IN_SNAPSHOT: back up the bucket/prefix separately");
|
|
15902
|
+
}
|
|
15903
|
+
if (!loopback(input.host)) {
|
|
15904
|
+
warnings.push("DIRECT_NETWORK_BIND_WITHOUT_LITE_TLS: terminate TLS in a trusted proxy or restrict the network boundary");
|
|
15905
|
+
}
|
|
15906
|
+
if (input.engine === "pglite") {
|
|
15907
|
+
warnings.push("PGLITE_SINGLE_PROCESS_CAPACITY: validate the real schema, concurrency and Realtime load");
|
|
15908
|
+
}
|
|
15909
|
+
if (input.identityMode === "local") {
|
|
15910
|
+
warnings.push("LOCAL_AUTH_IN_PRODUCTION: own account lifecycle, confirmation and password reset");
|
|
15911
|
+
}
|
|
15912
|
+
if (input.identityMode === "external" && !input.externalAuthProxyIssuer) {
|
|
15913
|
+
warnings.push("EXTERNAL_AUTH_PROXY_NOT_CONFIGURED: cross-origin browser token/userinfo calls may fail CORS");
|
|
15914
|
+
}
|
|
15915
|
+
if (input.externalAuthProxyIssuer && !absoluteHttpUrl(input.externalAuthProxyIssuer)) {
|
|
15916
|
+
blockers.push("EXTERNAL_AUTH_PROXY_ISSUER_INVALID: use an HTTPS issuer, or HTTP only on loopback");
|
|
15917
|
+
}
|
|
15918
|
+
if (!input.siteUrl) {
|
|
15919
|
+
warnings.push("SITE_URL_NOT_CONFIGURED: set [auth].site_url for stable redirects");
|
|
15920
|
+
} else if (!absoluteHttpUrl(input.siteUrl)) {
|
|
15921
|
+
warnings.push("SITE_URL_NOT_HTTPS: production redirects should use HTTPS");
|
|
15922
|
+
}
|
|
15923
|
+
if (!input.apiUrl) {
|
|
15924
|
+
warnings.push("API_URL_NOT_CONFIGURED: set the public API URL used by Auth and Functions");
|
|
15925
|
+
} else if (!absoluteHttpUrl(input.apiUrl)) {
|
|
15926
|
+
warnings.push("API_URL_NOT_HTTPS: production API URLs should use HTTPS");
|
|
15927
|
+
}
|
|
15928
|
+
if (within(input.projectDir, input.stateDir)) {
|
|
15929
|
+
warnings.push("STATE_INSIDE_PROJECT: keep mutable state outside the reviewed project checkout");
|
|
15930
|
+
}
|
|
15931
|
+
if (input.dataDir && input.dataDir === input.storageDir) {
|
|
15932
|
+
blockers.push("DATABASE_AND_STORAGE_PATH_OVERLAP: use separate directories");
|
|
15933
|
+
} else if (input.dataDir && (within(input.dataDir, input.storageDir) || within(input.storageDir, input.dataDir))) {
|
|
15934
|
+
warnings.push("DATABASE_AND_STORAGE_PATH_NESTED: keep database and storage trees independent");
|
|
15935
|
+
}
|
|
15936
|
+
if (input.latestSnapshotAt === undefined) {
|
|
15937
|
+
warnings.push("NO_SNAPSHOT_FOUND: create and test a restore snapshot before promotion");
|
|
15938
|
+
} else if (now - input.latestSnapshotAt > SNAPSHOT_MAX_AGE_MS) {
|
|
15939
|
+
warnings.push("SNAPSHOT_STALE: newest snapshot is older than 7 days");
|
|
15940
|
+
}
|
|
15941
|
+
return { ready: blockers.length === 0, blockers, warnings };
|
|
15942
|
+
}
|
|
15617
15943
|
// src/runtime/node/native/readiness.ts
|
|
15618
15944
|
function liteCapabilities(engine, replicationProfile, authEnabled = true) {
|
|
15619
15945
|
const extensionSource = engine === "native" ? "native-postgresql" : "pglite-bundled";
|
|
@@ -15902,7 +16228,7 @@ async function inspectPostgresExtensions(engine, source) {
|
|
|
15902
16228
|
}
|
|
15903
16229
|
// src/project-runtime.ts
|
|
15904
16230
|
import { chmod, link, lstat as lstat2, mkdir as mkdir4, readFile as readFile6, realpath as realpath2, unlink as unlink2, writeFile as writeFile4 } from "fs/promises";
|
|
15905
|
-
import { dirname as dirname4, isAbsolute as
|
|
16231
|
+
import { dirname as dirname4, isAbsolute as isAbsolute3, join as join7, parse, relative as relative3, resolve as resolve4 } from "path";
|
|
15906
16232
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
15907
16233
|
|
|
15908
16234
|
// src/runtime/node/config-toml.ts
|
|
@@ -17093,7 +17419,7 @@ function isNotFound(error) {
|
|
|
17093
17419
|
|
|
17094
17420
|
// src/project-runtime.ts
|
|
17095
17421
|
function resolveProjectPaths(options = {}) {
|
|
17096
|
-
const projectDir =
|
|
17422
|
+
const projectDir = resolve4(options.projectDir ?? process.cwd());
|
|
17097
17423
|
const stateDir = resolvePath2(projectDir, options.stateDir ?? process.env.SUPACLOUD_LITE_STATE_DIR ?? ".supacloud-lite");
|
|
17098
17424
|
const databaseEngine = resolveDatabaseEngine(options.engine, options.memory);
|
|
17099
17425
|
const dataDir = options.memory ? undefined : resolvePath2(projectDir, options.dataDir ?? process.env.SUPACLOUD_LITE_DATA_DIR ?? join7(stateDir, databaseEngine === "native" ? "pgdata" : "db"));
|
|
@@ -17263,7 +17589,7 @@ async function loadProjectBindings(projectDir, config) {
|
|
|
17263
17589
|
const bindings = config.lite.migrationBindings;
|
|
17264
17590
|
if (!bindings)
|
|
17265
17591
|
return;
|
|
17266
|
-
const manifest = JSON.parse(await readFile6(
|
|
17592
|
+
const manifest = JSON.parse(await readFile6(resolve4(projectDir, bindings.manifest), "utf8"));
|
|
17267
17593
|
return {
|
|
17268
17594
|
manifest,
|
|
17269
17595
|
target: { environment: bindings.environment, projectRef: bindings.projectRef },
|
|
@@ -17273,7 +17599,7 @@ async function loadProjectBindings(projectDir, config) {
|
|
|
17273
17599
|
async function loadProjectIdentity(projectDir, module) {
|
|
17274
17600
|
if (!module)
|
|
17275
17601
|
return;
|
|
17276
|
-
const imported = await import(pathToFileURL2(
|
|
17602
|
+
const imported = await import(pathToFileURL2(resolve4(projectDir, module)).href);
|
|
17277
17603
|
if (!isRecord(imported) || typeof imported["default"] !== "function") {
|
|
17278
17604
|
throw new Error("Lite identity module must default-export an external identity verifier");
|
|
17279
17605
|
}
|
|
@@ -17339,7 +17665,11 @@ async function startProjectServer(options = {}) {
|
|
|
17339
17665
|
const resolvedOptions = options.port === 0 ? { ...options, port: await findEphemeralPort(options.host) } : options;
|
|
17340
17666
|
const project = await createProjectBackend({ ...resolvedOptions, startRuntimeServices: true });
|
|
17341
17667
|
try {
|
|
17342
|
-
const
|
|
17668
|
+
const beforeRequest = options.externalAuthProxyIssuer ? createExternalAuthProxy({
|
|
17669
|
+
issuer: options.externalAuthProxyIssuer,
|
|
17670
|
+
allowedRedirectOrigins: options.externalAuthProxyRedirectOrigins
|
|
17671
|
+
}) : undefined;
|
|
17672
|
+
const server = await serveBun(project.backend, { host: project.host, port: project.port, beforeRequest });
|
|
17343
17673
|
let closePromise = null;
|
|
17344
17674
|
return {
|
|
17345
17675
|
...project,
|
|
@@ -17381,7 +17711,7 @@ async function loadWebhooks(projectDir) {
|
|
|
17381
17711
|
}
|
|
17382
17712
|
}
|
|
17383
17713
|
function resolvePath2(projectDir, path) {
|
|
17384
|
-
return
|
|
17714
|
+
return isAbsolute3(path) ? path : resolve4(projectDir, path);
|
|
17385
17715
|
}
|
|
17386
17716
|
function randomHex(bytes) {
|
|
17387
17717
|
const value = crypto.getRandomValues(new Uint8Array(bytes));
|
|
@@ -17426,7 +17756,7 @@ async function findEphemeralPort(host = "127.0.0.1") {
|
|
|
17426
17756
|
}
|
|
17427
17757
|
// src/snapshot.ts
|
|
17428
17758
|
import { chmod as chmod2, copyFile, lstat as lstat3, mkdir as mkdir5, mkdtemp, readdir as readdir3, readFile as readFile7, rename as rename2, rm as rm4, writeFile as writeFile5 } from "fs/promises";
|
|
17429
|
-
import { dirname as dirname5, join as join8, parse as parse2, relative as
|
|
17759
|
+
import { dirname as dirname5, join as join8, parse as parse2, relative as relative4, resolve as resolve5, sep as sep2 } from "path";
|
|
17430
17760
|
import { create as createTar, extract as extractTar2 } from "tar";
|
|
17431
17761
|
var SNAPSHOT_FORMAT = "supacloud-lite-snapshot";
|
|
17432
17762
|
var SNAPSHOT_VERSION = 1;
|
|
@@ -17450,7 +17780,7 @@ async function createSnapshot(options) {
|
|
|
17450
17780
|
postgresMajor: await readPostgresMajor(paths.dataDir)
|
|
17451
17781
|
} : {}
|
|
17452
17782
|
};
|
|
17453
|
-
const output =
|
|
17783
|
+
const output = resolve5(options.output);
|
|
17454
17784
|
if (await existingInfo(output))
|
|
17455
17785
|
throw new Error(`snapshot output already exists: ${output}`);
|
|
17456
17786
|
await mkdir5(dirname5(output), { recursive: true });
|
|
@@ -17491,7 +17821,7 @@ async function restoreSnapshot(options) {
|
|
|
17491
17821
|
await mkdir5(payloadRoot, { recursive: true });
|
|
17492
17822
|
await extractTar2({
|
|
17493
17823
|
cwd: payloadRoot,
|
|
17494
|
-
file:
|
|
17824
|
+
file: resolve5(options.input),
|
|
17495
17825
|
preserveOwner: false,
|
|
17496
17826
|
preservePaths: false,
|
|
17497
17827
|
strict: true,
|
|
@@ -17534,10 +17864,10 @@ async function restoreSnapshot(options) {
|
|
|
17534
17864
|
await mkdir5(stateStage, { recursive: true });
|
|
17535
17865
|
await copyEntry(join8(payloadRoot, "secrets.json"), join8(stateStage, "secrets.json"));
|
|
17536
17866
|
if (paths.dataDir && isWithin(paths.stateDir, paths.dataDir)) {
|
|
17537
|
-
await copyEntry(join8(payloadRoot, "database"), join8(stateStage,
|
|
17867
|
+
await copyEntry(join8(payloadRoot, "database"), join8(stateStage, relative4(paths.stateDir, paths.dataDir)));
|
|
17538
17868
|
}
|
|
17539
17869
|
if (options.storageBackend === "fs" && isWithin(paths.stateDir, paths.storageDir)) {
|
|
17540
|
-
await copyEntry(join8(payloadRoot, "storage"), join8(stateStage,
|
|
17870
|
+
await copyEntry(join8(payloadRoot, "storage"), join8(stateStage, relative4(paths.stateDir, paths.storageDir)));
|
|
17541
17871
|
}
|
|
17542
17872
|
const swaps = [];
|
|
17543
17873
|
try {
|
|
@@ -17571,11 +17901,11 @@ async function restoreSnapshot(options) {
|
|
|
17571
17901
|
function normalizePaths(paths) {
|
|
17572
17902
|
return {
|
|
17573
17903
|
...paths,
|
|
17574
|
-
projectDir:
|
|
17575
|
-
stateDir:
|
|
17576
|
-
dataDir: paths.dataDir ?
|
|
17577
|
-
storageDir:
|
|
17578
|
-
secretsFile:
|
|
17904
|
+
projectDir: resolve5(paths.projectDir),
|
|
17905
|
+
stateDir: resolve5(paths.stateDir),
|
|
17906
|
+
dataDir: paths.dataDir ? resolve5(paths.dataDir) : undefined,
|
|
17907
|
+
storageDir: resolve5(paths.storageDir),
|
|
17908
|
+
secretsFile: resolve5(paths.secretsFile)
|
|
17579
17909
|
};
|
|
17580
17910
|
}
|
|
17581
17911
|
async function assertSnapshotPaths(paths, options = {}) {
|
|
@@ -17605,7 +17935,7 @@ async function assertSnapshotPaths(paths, options = {}) {
|
|
|
17605
17935
|
async function assertDirectoryOrMissing(path) {
|
|
17606
17936
|
if (!path)
|
|
17607
17937
|
return;
|
|
17608
|
-
if (
|
|
17938
|
+
if (resolve5(path) === parse2(resolve5(path)).root)
|
|
17609
17939
|
throw new Error(`snapshot path must not be the filesystem root: ${path}`);
|
|
17610
17940
|
try {
|
|
17611
17941
|
const info = await lstat3(path);
|
|
@@ -17828,13 +18158,13 @@ async function assertNoSymlinks(root) {
|
|
|
17828
18158
|
}
|
|
17829
18159
|
}
|
|
17830
18160
|
function isWithin(parent, child) {
|
|
17831
|
-
const normalizedParent =
|
|
17832
|
-
const normalizedChild =
|
|
18161
|
+
const normalizedParent = resolve5(parent);
|
|
18162
|
+
const normalizedChild = resolve5(child);
|
|
17833
18163
|
return normalizedChild !== normalizedParent && normalizedChild.startsWith(`${normalizedParent}${sep2}`);
|
|
17834
18164
|
}
|
|
17835
18165
|
function pathsOverlap(left, right) {
|
|
17836
|
-
const normalizedLeft =
|
|
17837
|
-
const normalizedRight =
|
|
18166
|
+
const normalizedLeft = resolve5(left);
|
|
18167
|
+
const normalizedRight = resolve5(right);
|
|
17838
18168
|
return normalizedLeft === normalizedRight || isWithin(normalizedLeft, normalizedRight) || isWithin(normalizedRight, normalizedLeft);
|
|
17839
18169
|
}
|
|
17840
18170
|
export {
|
|
@@ -17845,6 +18175,7 @@ export {
|
|
|
17845
18175
|
S3StorageDriver,
|
|
17846
18176
|
SUPACLOUD_LITE_VERSION,
|
|
17847
18177
|
buildPowerSyncPostgresArgs,
|
|
18178
|
+
createExternalAuthProxy,
|
|
17848
18179
|
createBackend as createLiteBackend,
|
|
17849
18180
|
createNativeEngine,
|
|
17850
18181
|
createPgliteEngine,
|
|
@@ -17863,6 +18194,7 @@ export {
|
|
|
17863
18194
|
isNativeEngineSupported,
|
|
17864
18195
|
liteCapabilities,
|
|
17865
18196
|
mintProjectKeys,
|
|
18197
|
+
productionReadiness,
|
|
17866
18198
|
resolveDatabaseEngine,
|
|
17867
18199
|
resolveNativeReplicationOptions,
|
|
17868
18200
|
resolveProjectPaths,
|
|
@@ -27,6 +27,8 @@ export interface ProjectRuntimeOptions {
|
|
|
27
27
|
graphql?: GraphqlOptions;
|
|
28
28
|
externalIdentity?: ExternalIdentityVerifier;
|
|
29
29
|
identityModule?: string;
|
|
30
|
+
externalAuthProxyIssuer?: string;
|
|
31
|
+
externalAuthProxyRedirectOrigins?: string[];
|
|
30
32
|
postgresDir?: string;
|
|
31
33
|
migrationBindings?: SeedOptions['bindings'];
|
|
32
34
|
projectDir?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-runtime.d.ts","sourceRoot":"","sources":["../src/project-runtime.ts"],"names":[],"mappings":"AAGA,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAG7E,OAAO,EAAY,KAAK,aAAa,EAAE,MAAM,8BAA8B,CAAA;AAE3E,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAGrF,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAE/D,OAAO,EAAmB,KAAK,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AAC7F,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,EAAkC,KAAK,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAGrG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAA;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"project-runtime.d.ts","sourceRoot":"","sources":["../src/project-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Same-origin reverse proxy for an external SupAuth / GoTrue OAuth runtime.
|
|
3
|
+
*
|
|
4
|
+
* Browser applications often run on a business origin that the identity
|
|
5
|
+
* service does not allow via CORS. The authorization request is a top-level
|
|
6
|
+
* navigation and needs no proxy; token exchange, userinfo and logout are
|
|
7
|
+
* browser API calls and can be routed through this handler instead.
|
|
8
|
+
*
|
|
9
|
+
* This is deliberately not a general-purpose proxy: only exact OAuth paths and
|
|
10
|
+
* methods are accepted, the upstream origin is fixed by configuration, and
|
|
11
|
+
* credentials, cookies and hop-by-hop headers are never forwarded.
|
|
12
|
+
*/
|
|
13
|
+
export type ExternalAuthFetch = (input: URL | RequestInfo, init?: RequestInit) => Promise<Response>;
|
|
14
|
+
export interface ExternalAuthProxyOptions {
|
|
15
|
+
/** Issuer URL, for example https://auth.example.com/auth/v1. */
|
|
16
|
+
issuer: string;
|
|
17
|
+
/** Extra origins allowed as logout redirect targets, such as the application origin. */
|
|
18
|
+
allowedRedirectOrigins?: readonly string[];
|
|
19
|
+
/** Trusted host override for tests. */
|
|
20
|
+
fetch?: ExternalAuthFetch;
|
|
21
|
+
}
|
|
22
|
+
export type ExternalAuthProxy = (request: Request) => Promise<Response | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Create a request handler that returns `null` for requests it does not own, so
|
|
25
|
+
* the caller can continue normal Lite routing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function createExternalAuthProxy(options: ExternalAuthProxyOptions): ExternalAuthProxy;
|
|
28
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../../src/runtime/auth/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAgBH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,WAAW,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEnG,MAAM,WAAW,wBAAwB;IACvC,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,wFAAwF;IACxF,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1C,uCAAuC;IACvC,KAAK,CAAC,EAAE,iBAAiB,CAAA;CAC1B;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;AA0D9E;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAoD5F"}
|