@rebasepro/server-postgresql 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -89
- package/dist/common/src/util/permissions.d.ts +14 -6
- package/dist/index.es.js +79 -112
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +79 -112
- package/dist/index.umd.js.map +1 -1
- package/dist/server-postgresql/src/auth/services.d.ts +11 -11
- package/dist/server-postgresql/src/data-transformer.d.ts +0 -3
- package/dist/server-postgresql/src/databasePoolManager.d.ts +1 -1
- package/dist/server-postgresql/src/types.d.ts +3 -0
- package/dist/server-postgresql/src/websocket.d.ts +8 -3
- package/dist/types/src/types/backend.d.ts +36 -1
- package/dist/types/src/types/collections.d.ts +21 -1
- package/dist/types/src/types/properties.d.ts +0 -8
- package/package.json +6 -6
- package/src/PostgresBackendDriver.ts +1 -1
- package/src/PostgresBootstrapper.ts +4 -3
- package/src/auth/services.ts +28 -27
- package/src/cli.ts +50 -23
- package/src/data-transformer.ts +57 -95
- package/src/databasePoolManager.ts +2 -1
- package/src/services/EntityPersistService.ts +29 -12
- package/src/types.ts +4 -0
- package/src/websocket.ts +37 -22
- package/drizzle.test.config.ts +0 -10
package/README.md
CHANGED
|
@@ -1,106 +1,86 @@
|
|
|
1
|
-
# @rebasepro/postgresql
|
|
1
|
+
# @rebasepro/server-postgresql
|
|
2
2
|
|
|
3
|
-
PostgreSQL
|
|
4
|
-
|
|
5
|
-
This package provides a complete client-side implementation for connecting Rebase applications to PostgreSQL backends, featuring real-time synchronization via WebSockets.
|
|
3
|
+
PostgreSQL database driver for Rebase, built on Drizzle ORM.
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
|
-
|
|
8
|
+
pnpm add @rebasepro/server-postgresql
|
|
11
9
|
```
|
|
12
10
|
|
|
13
|
-
##
|
|
11
|
+
## What This Package Does
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Implements the Rebase `DatabaseAdapter` / `BackendBootstrapper` interfaces for PostgreSQL. It provides connection pooling, a Drizzle-based data driver, Postgres LISTEN/NOTIFY realtime, auth table management, entity history, schema generation, branching, read replicas, and WebSocket support. Plug it into `@rebasepro/server-core` via `createPostgresAdapter()` or `createPostgresBootstrapper()`.
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
import { usePostgresDataSource } from "@rebasepro/postgresql";
|
|
19
|
-
import { Rebase } from "@rebasepro/core";
|
|
20
|
-
|
|
21
|
-
function App() {
|
|
22
|
-
const dataSource = usePostgresDataSource({
|
|
23
|
-
baseUrl: "http://localhost:3001",
|
|
24
|
-
websocketUrl: "ws://localhost:3001", // Optional, will be inferred from baseUrl
|
|
25
|
-
headers: { // Optional
|
|
26
|
-
"Authorization": "Bearer your-token"
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<Rebase
|
|
32
|
-
dataSource={dataSource}
|
|
33
|
-
collections={collections}
|
|
34
|
-
// ... other props
|
|
35
|
-
/>
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
```
|
|
15
|
+
## Key Exports
|
|
39
16
|
|
|
40
|
-
|
|
17
|
+
| Export | Description |
|
|
18
|
+
|--------|-------------|
|
|
19
|
+
| `createPostgresAdapter(config)` | Creates a `DatabaseAdapter` for use with `initializeRebaseBackend({ database: ... })`. Recommended API. |
|
|
20
|
+
| `createPostgresBootstrapper(config)` | Lower-level `BackendBootstrapper` factory. Used internally by the adapter. |
|
|
21
|
+
| `createPostgresDatabaseConnection(url, schema?, poolConfig?)` | Creates a production-grade pooled Drizzle connection. Returns `{ db, pool, connectionString }`. |
|
|
22
|
+
| `createDirectDatabaseConnection(url, schema?, poolConfig?)` | Non-pooled connection for LISTEN/NOTIFY and advisory locks (bypasses PgBouncer). |
|
|
23
|
+
| `createReadReplicaConnection(url, schema?, poolConfig?)` | Read-only connection for routing reads to replicas. |
|
|
24
|
+
| `PostgresBackendDriver` | The `DataDriver` implementation — CRUD, filtering, RLS, subcollections, admin SQL. |
|
|
25
|
+
| `RealtimeService` | Postgres LISTEN/NOTIFY-based `RealtimeProvider`. |
|
|
26
|
+
| `DatabasePoolManager` | Per-branch/per-tenant dynamic pool management (used with `ADMIN_CONNECTION_STRING`). |
|
|
27
|
+
| `PostgresCollectionRegistry` | Collection → Drizzle table registry with enum and relation tracking. |
|
|
28
|
+
| `BranchService` | Database branching (schema-level isolation). |
|
|
29
|
+
| `generateDrizzleSchema(collections)` | Generates Drizzle schema code from collection definitions. |
|
|
30
|
+
| `createAuthSchema(schemaName?)` | Generates Drizzle tables for the auth system (`users`, `roles`, `user_roles`). |
|
|
41
31
|
|
|
42
|
-
|
|
43
|
-
import { createPostgresDataSource } from "@rebasepro/postgresql";
|
|
32
|
+
## Quick Start
|
|
44
33
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
34
|
+
```typescript
|
|
35
|
+
import { createPostgresDatabaseConnection } from "@rebasepro/server-postgresql";
|
|
36
|
+
import { createPostgresAdapter } from "@rebasepro/server-postgresql";
|
|
37
|
+
import { initializeRebaseBackend } from "@rebasepro/server-core";
|
|
38
|
+
import * as schema from "./generated/schema";
|
|
39
|
+
|
|
40
|
+
// Create connection
|
|
41
|
+
const { db, pool } = createPostgresDatabaseConnection(
|
|
42
|
+
process.env.DATABASE_URL,
|
|
43
|
+
schema
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Create adapter and pass to server-core
|
|
47
|
+
const database = createPostgresAdapter({
|
|
48
|
+
connection: db,
|
|
49
|
+
connectionString: process.env.DATABASE_URL,
|
|
50
|
+
schema: { tables: schema },
|
|
48
51
|
});
|
|
49
|
-
```
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
## API
|
|
60
|
-
|
|
61
|
-
### Configuration
|
|
53
|
+
const backend = await initializeRebaseBackend({
|
|
54
|
+
app,
|
|
55
|
+
server,
|
|
56
|
+
database,
|
|
57
|
+
collections,
|
|
58
|
+
auth: { /* ... */ },
|
|
59
|
+
});
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
61
|
+
// Graceful shutdown
|
|
62
|
+
process.on("SIGTERM", async () => {
|
|
63
|
+
await backend.shutdown();
|
|
64
|
+
await pool.end();
|
|
65
|
+
});
|
|
69
66
|
```
|
|
70
67
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
- `FETCH_ENTITY`
|
|
91
|
-
- `SAVE_ENTITY`
|
|
92
|
-
- `DELETE_ENTITY`
|
|
93
|
-
- `CHECK_UNIQUE_FIELD`
|
|
94
|
-
- `GENERATE_ENTITY_ID`
|
|
95
|
-
- `COUNT_ENTITIES`
|
|
96
|
-
- `subscribe_collection`
|
|
97
|
-
- `subscribe_entity`
|
|
98
|
-
- `unsubscribe`
|
|
99
|
-
|
|
100
|
-
## Development
|
|
101
|
-
|
|
102
|
-
This package is part of the Rebase monorepo. For development instructions, see the main repository README.
|
|
103
|
-
|
|
104
|
-
## License
|
|
105
|
-
|
|
106
|
-
MIT
|
|
68
|
+
## Connection Pool Defaults
|
|
69
|
+
|
|
70
|
+
| Option | Default |
|
|
71
|
+
|--------|---------|
|
|
72
|
+
| `max` | 20 |
|
|
73
|
+
| `idleTimeoutMillis` | 30,000 |
|
|
74
|
+
| `connectionTimeoutMillis` | 10,000 |
|
|
75
|
+
| `queryTimeout` | 30,000 |
|
|
76
|
+
| `statementTimeout` | 30,000 |
|
|
77
|
+
| `keepAlive` | true |
|
|
78
|
+
|
|
79
|
+
## Related Packages
|
|
80
|
+
|
|
81
|
+
| Package | Role |
|
|
82
|
+
|---------|------|
|
|
83
|
+
| `@rebasepro/server-core` | Backend orchestrator that consumes this adapter |
|
|
84
|
+
| `@rebasepro/types` | Shared interfaces (`DatabaseAdapter`, `BackendBootstrapper`, `DataDriver`) |
|
|
85
|
+
| `@rebasepro/sdk-generator` | Generates typed SDKs from collections |
|
|
86
|
+
| `@rebasepro/common` | Default collections and shared utilities |
|
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { Entity, EntityCollection, User } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal auth context for permission checking.
|
|
4
|
+
* Only requires the user object — avoids forcing callers to construct
|
|
5
|
+
* a full AuthController just to check permissions.
|
|
6
|
+
*/
|
|
7
|
+
export interface AuthContext<USER extends User = User> {
|
|
8
|
+
user: USER | null;
|
|
9
|
+
}
|
|
10
|
+
export declare function checkOperation<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, entity: Entity<M> | null, targetOperation: "select" | "insert" | "update" | "delete"): boolean;
|
|
11
|
+
export declare function canReadCollection<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>): boolean;
|
|
12
|
+
export declare function canEditEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
|
13
|
+
export declare function canCreateEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
|
14
|
+
export declare function canDeleteEntity<M extends Record<string, unknown>, USER extends User>(collection: EntityCollection<M>, authContext: AuthContext<USER>, path: string, entity: Entity<M> | null): boolean;
|
package/dist/index.es.js
CHANGED
|
@@ -3964,39 +3964,18 @@ function serializePropertyToServer(value, property) {
|
|
|
3964
3964
|
}
|
|
3965
3965
|
return value;
|
|
3966
3966
|
}
|
|
3967
|
-
case "binary":
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
if (base64Data) {
|
|
3972
|
-
return Buffer.from(base64Data, "base64");
|
|
3973
|
-
}
|
|
3974
|
-
}
|
|
3975
|
-
}
|
|
3976
|
-
if (Buffer.isBuffer(value)) {
|
|
3977
|
-
return value;
|
|
3978
|
-
}
|
|
3967
|
+
case "binary": {
|
|
3968
|
+
const decoded = tryDecodeBase64DataUrl(value);
|
|
3969
|
+
if (decoded) return decoded;
|
|
3970
|
+
if (Buffer.isBuffer(value)) return value;
|
|
3979
3971
|
return value;
|
|
3972
|
+
}
|
|
3980
3973
|
case "string":
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
if (base64Data) {
|
|
3985
|
-
return Buffer.from(base64Data, "base64");
|
|
3986
|
-
}
|
|
3987
|
-
}
|
|
3988
|
-
}
|
|
3989
|
-
return value;
|
|
3990
|
-
default:
|
|
3991
|
-
if (typeof value === "string") {
|
|
3992
|
-
if (value.startsWith("data:application/octet-stream;base64,")) {
|
|
3993
|
-
const base64Data = value.split(",")[1];
|
|
3994
|
-
if (base64Data) {
|
|
3995
|
-
return Buffer.from(base64Data, "base64");
|
|
3996
|
-
}
|
|
3997
|
-
}
|
|
3998
|
-
}
|
|
3974
|
+
default: {
|
|
3975
|
+
const decoded = tryDecodeBase64DataUrl(value);
|
|
3976
|
+
if (decoded) return decoded;
|
|
3999
3977
|
return value;
|
|
3978
|
+
}
|
|
4000
3979
|
}
|
|
4001
3980
|
}
|
|
4002
3981
|
async function parseDataFromServer(data, collection, db, registry) {
|
|
@@ -4116,21 +4095,36 @@ async function parseDataFromServer(data, collection, db, registry) {
|
|
|
4116
4095
|
}
|
|
4117
4096
|
return result;
|
|
4118
4097
|
}
|
|
4119
|
-
function
|
|
4120
|
-
if (
|
|
4121
|
-
|
|
4098
|
+
function tryDecodeBase64DataUrl(value) {
|
|
4099
|
+
if (typeof value !== "string") return null;
|
|
4100
|
+
if (!value.startsWith("data:application/octet-stream;base64,")) return null;
|
|
4101
|
+
const base64Data = value.split(",")[1];
|
|
4102
|
+
return base64Data ? Buffer.from(base64Data, "base64") : null;
|
|
4103
|
+
}
|
|
4104
|
+
function tryResolveBuffer(value) {
|
|
4105
|
+
if (Buffer.isBuffer(value)) return value;
|
|
4106
|
+
if (typeof value === "object" && value !== null) {
|
|
4107
|
+
const rawVal = value;
|
|
4108
|
+
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4109
|
+
return Buffer.from(rawVal.data);
|
|
4110
|
+
}
|
|
4122
4111
|
}
|
|
4112
|
+
return null;
|
|
4113
|
+
}
|
|
4114
|
+
function bufferToStringOrBase64(buf) {
|
|
4115
|
+
for (let i = 0; i < buf.length; i++) {
|
|
4116
|
+
const b = buf[i];
|
|
4117
|
+
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4118
|
+
return `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
return buf.toString("utf8");
|
|
4122
|
+
}
|
|
4123
|
+
function parsePropertyFromServer(value, property, collection, propertyKey) {
|
|
4124
|
+
if (value === null || value === void 0) return value;
|
|
4123
4125
|
switch (property.type) {
|
|
4124
4126
|
case "binary": {
|
|
4125
|
-
|
|
4126
|
-
if (Buffer.isBuffer(value)) {
|
|
4127
|
-
buf = value;
|
|
4128
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4129
|
-
const rawVal = value;
|
|
4130
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4131
|
-
buf = Buffer.from(rawVal.data);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4127
|
+
const buf = tryResolveBuffer(value);
|
|
4134
4128
|
if (buf) {
|
|
4135
4129
|
return `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4136
4130
|
}
|
|
@@ -4138,28 +4132,9 @@ function parsePropertyFromServer(value, property, collection, propertyKey) {
|
|
|
4138
4132
|
}
|
|
4139
4133
|
case "string": {
|
|
4140
4134
|
if (typeof value === "string") return value;
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
isBuffer = true;
|
|
4145
|
-
buf = value;
|
|
4146
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4147
|
-
const rawVal = value;
|
|
4148
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4149
|
-
isBuffer = true;
|
|
4150
|
-
buf = Buffer.from(rawVal.data);
|
|
4151
|
-
}
|
|
4152
|
-
}
|
|
4153
|
-
if (isBuffer && buf) {
|
|
4154
|
-
let isPrintable = true;
|
|
4155
|
-
for (let i = 0; i < buf.length; i++) {
|
|
4156
|
-
const b = buf[i];
|
|
4157
|
-
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4158
|
-
isPrintable = false;
|
|
4159
|
-
break;
|
|
4160
|
-
}
|
|
4161
|
-
}
|
|
4162
|
-
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4135
|
+
const buf = tryResolveBuffer(value);
|
|
4136
|
+
if (buf) {
|
|
4137
|
+
return bufferToStringOrBase64(buf);
|
|
4163
4138
|
}
|
|
4164
4139
|
if (typeof value === "object" && value !== null) {
|
|
4165
4140
|
try {
|
|
@@ -4273,28 +4248,9 @@ function parsePropertyFromServer(value, property, collection, propertyKey) {
|
|
|
4273
4248
|
return null;
|
|
4274
4249
|
}
|
|
4275
4250
|
default: {
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
isBuffer = true;
|
|
4280
|
-
buf = value;
|
|
4281
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4282
|
-
const rawVal = value;
|
|
4283
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4284
|
-
isBuffer = true;
|
|
4285
|
-
buf = Buffer.from(rawVal.data);
|
|
4286
|
-
}
|
|
4287
|
-
}
|
|
4288
|
-
if (isBuffer && buf) {
|
|
4289
|
-
let isPrintable = true;
|
|
4290
|
-
for (let i = 0; i < buf.length; i++) {
|
|
4291
|
-
const b = buf[i];
|
|
4292
|
-
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4293
|
-
isPrintable = false;
|
|
4294
|
-
break;
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4251
|
+
const buf = tryResolveBuffer(value);
|
|
4252
|
+
if (buf) {
|
|
4253
|
+
return bufferToStringOrBase64(buf);
|
|
4298
4254
|
}
|
|
4299
4255
|
return value;
|
|
4300
4256
|
}
|
|
@@ -6487,12 +6443,12 @@ class EntityPersistService {
|
|
|
6487
6443
|
*/
|
|
6488
6444
|
extractCauseMessage(error) {
|
|
6489
6445
|
if (!error || typeof error !== "object") return null;
|
|
6490
|
-
|
|
6491
|
-
if (
|
|
6492
|
-
const deeper = this.extractCauseMessage(
|
|
6446
|
+
if (!(error instanceof Error)) return null;
|
|
6447
|
+
if (error.cause && typeof error.cause === "object") {
|
|
6448
|
+
const deeper = this.extractCauseMessage(error.cause);
|
|
6493
6449
|
if (deeper) return deeper;
|
|
6494
|
-
if (
|
|
6495
|
-
return
|
|
6450
|
+
if (error.cause instanceof Error && error.cause.message) {
|
|
6451
|
+
return error.cause.message;
|
|
6496
6452
|
}
|
|
6497
6453
|
}
|
|
6498
6454
|
return null;
|
|
@@ -6513,12 +6469,17 @@ class EntityPersistService {
|
|
|
6513
6469
|
*/
|
|
6514
6470
|
extractPgError(error) {
|
|
6515
6471
|
if (!error || typeof error !== "object") return null;
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
|
|
6472
|
+
if (!(error instanceof Error)) {
|
|
6473
|
+
if ("cause" in error && error.cause && typeof error.cause === "object") {
|
|
6474
|
+
return this.extractPgError(error.cause);
|
|
6475
|
+
}
|
|
6476
|
+
return null;
|
|
6477
|
+
}
|
|
6478
|
+
if ("code" in error && typeof error.code === "string" && /^[0-9A-Z]{5}$/.test(error.code)) {
|
|
6479
|
+
return error;
|
|
6519
6480
|
}
|
|
6520
|
-
if (
|
|
6521
|
-
return this.extractPgError(
|
|
6481
|
+
if (error.cause && typeof error.cause === "object") {
|
|
6482
|
+
return this.extractPgError(error.cause);
|
|
6522
6483
|
}
|
|
6523
6484
|
return null;
|
|
6524
6485
|
}
|
|
@@ -7633,7 +7594,7 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7633
7594
|
return this.withTransaction((delegate) => delegate.deleteEntity(props));
|
|
7634
7595
|
}
|
|
7635
7596
|
async deleteAll(path2) {
|
|
7636
|
-
return this.delegate.deleteAll(path2);
|
|
7597
|
+
return this.withTransaction((delegate) => delegate.deleteAll(path2));
|
|
7637
7598
|
}
|
|
7638
7599
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7639
7600
|
return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
|
|
@@ -7712,6 +7673,7 @@ class DatabasePoolManager {
|
|
|
7712
7673
|
}
|
|
7713
7674
|
await Promise.all(promises2);
|
|
7714
7675
|
this.pools.clear();
|
|
7676
|
+
this.drizzleInstances.clear();
|
|
7715
7677
|
}
|
|
7716
7678
|
}
|
|
7717
7679
|
function createAuthSchema(usersSchemaName = "rebase") {
|
|
@@ -9605,20 +9567,19 @@ class RealtimeService extends EventEmitter {
|
|
|
9605
9567
|
}
|
|
9606
9568
|
}
|
|
9607
9569
|
const PostgresRealtimeProvider = RealtimeService;
|
|
9608
|
-
const clientSessions = /* @__PURE__ */ new Map();
|
|
9609
9570
|
const WS_RATE_LIMIT = 2e3;
|
|
9610
9571
|
const WS_RATE_WINDOW_MS = 6e4;
|
|
9611
9572
|
const ADMIN_ONLY_TYPES = /* @__PURE__ */ new Set(["EXECUTE_SQL", "FETCH_DATABASES", "FETCH_ROLES", "FETCH_UNMAPPED_TABLES", "FETCH_TABLE_METADATA", "FETCH_CURRENT_DATABASE", "CREATE_BRANCH", "DELETE_BRANCH", "LIST_BRANCHES"]);
|
|
9612
9573
|
function extractErrorMessage(error) {
|
|
9613
9574
|
if (!error) return "Unknown error";
|
|
9614
|
-
if (
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
return extractErrorMessage(err.cause);
|
|
9618
|
-
}
|
|
9619
|
-
if (typeof err.message === "string") {
|
|
9620
|
-
return err.message;
|
|
9575
|
+
if (error instanceof Error) {
|
|
9576
|
+
if ("cause" in error && error.cause) {
|
|
9577
|
+
return extractErrorMessage(error.cause);
|
|
9621
9578
|
}
|
|
9579
|
+
return error.message;
|
|
9580
|
+
}
|
|
9581
|
+
if (typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
9582
|
+
return error.message;
|
|
9622
9583
|
}
|
|
9623
9584
|
return String(error);
|
|
9624
9585
|
}
|
|
@@ -9626,14 +9587,10 @@ function isAdminSession(session) {
|
|
|
9626
9587
|
if (!session?.user) return false;
|
|
9627
9588
|
if (session.user.isAdmin) return true;
|
|
9628
9589
|
if (!session.user.roles) return false;
|
|
9629
|
-
return session.user.roles.some((r) =>
|
|
9630
|
-
if (typeof r === "string") return r === "admin";
|
|
9631
|
-
if (r && typeof r === "object" && "isAdmin" in r) return r.isAdmin;
|
|
9632
|
-
if (r && typeof r === "object" && "id" in r) return r.id === "admin";
|
|
9633
|
-
return false;
|
|
9634
|
-
});
|
|
9590
|
+
return session.user.roles.some((r) => r === "admin");
|
|
9635
9591
|
}
|
|
9636
9592
|
function createPostgresWebSocket(server, realtimeService, driver, authConfig, authAdapter) {
|
|
9593
|
+
const clientSessions = /* @__PURE__ */ new Map();
|
|
9637
9594
|
const isProduction = process.env.NODE_ENV === "production";
|
|
9638
9595
|
const wsDebug = (...args) => {
|
|
9639
9596
|
if (!isProduction) console.debug(...args);
|
|
@@ -9772,13 +9729,23 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
|
|
|
9772
9729
|
}
|
|
9773
9730
|
const getScopedDelegate = async () => {
|
|
9774
9731
|
const session = clientSessions.get(clientId);
|
|
9775
|
-
if (
|
|
9732
|
+
if (typeof driver.withAuth === "function") {
|
|
9776
9733
|
try {
|
|
9777
9734
|
const userForAuth = session?.user ? {
|
|
9778
9735
|
uid: session.user.userId,
|
|
9736
|
+
displayName: null,
|
|
9737
|
+
email: null,
|
|
9738
|
+
photoURL: null,
|
|
9739
|
+
providerId: "websocket",
|
|
9740
|
+
isAnonymous: false,
|
|
9779
9741
|
roles: session.user.roles ?? []
|
|
9780
9742
|
} : {
|
|
9781
9743
|
uid: "anon",
|
|
9744
|
+
displayName: null,
|
|
9745
|
+
email: null,
|
|
9746
|
+
photoURL: null,
|
|
9747
|
+
providerId: "websocket",
|
|
9748
|
+
isAnonymous: true,
|
|
9782
9749
|
roles: ["anon"]
|
|
9783
9750
|
};
|
|
9784
9751
|
return await driver.withAuth(userForAuth);
|