@rebasepro/server-postgres 0.0.1-canary.4829d6e
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/LICENSE +22 -0
- package/README.md +86 -0
- package/dist/PostgresAdapter.d.ts +6 -0
- package/dist/PostgresBackendDriver.d.ts +150 -0
- package/dist/PostgresBootstrapper.d.ts +51 -0
- package/dist/auth/ensure-tables.d.ts +10 -0
- package/dist/auth/services.d.ts +250 -0
- package/dist/backup/backup-cli.d.ts +3 -0
- package/dist/backup/backup-cron.d.ts +53 -0
- package/dist/backup/backup-service.d.ts +85 -0
- package/dist/backup/index.d.ts +12 -0
- package/dist/backup/pg-tools.d.ts +110 -0
- package/dist/backup/retention.d.ts +35 -0
- package/dist/chunk-DSJWtz9O.js +40 -0
- package/dist/cli-errors.d.ts +42 -0
- package/dist/cli-helpers.d.ts +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/collections/PostgresCollectionRegistry.d.ts +47 -0
- package/dist/connection.d.ts +65 -0
- package/dist/data-transformer.d.ts +55 -0
- package/dist/databasePoolManager.d.ts +20 -0
- package/dist/history/HistoryService.d.ts +71 -0
- package/dist/history/ensure-history-table.d.ts +7 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.es.js +23535 -0
- package/dist/index.es.js.map +1 -0
- package/dist/interfaces.d.ts +18 -0
- package/dist/schema/auth-bootstrap-sql.d.ts +24 -0
- package/dist/schema/auth-default-policies.d.ts +10 -0
- package/dist/schema/auth-schema.d.ts +2376 -0
- package/dist/schema/doctor-cli.d.ts +2 -0
- package/dist/schema/doctor.d.ts +58 -0
- package/dist/schema/dynamic-tables.d.ts +31 -0
- package/dist/schema/generate-drizzle-schema-logic.d.ts +2 -0
- package/dist/schema/generate-drizzle-schema.d.ts +1 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -0
- package/dist/schema/generate-postgres-ddl.d.ts +1 -0
- package/dist/schema/introspect-db-inference.d.ts +5 -0
- package/dist/schema/introspect-db-logic.d.ts +118 -0
- package/dist/schema/introspect-db.d.ts +1 -0
- package/dist/schema/introspect-runtime.d.ts +57 -0
- package/dist/schema/test-schema.d.ts +24 -0
- package/dist/security/policy-drift.d.ts +57 -0
- package/dist/security/rls-enforcement.d.ts +122 -0
- package/dist/services/BranchService.d.ts +47 -0
- package/dist/services/FetchService.d.ts +214 -0
- package/dist/services/PersistService.d.ts +39 -0
- package/dist/services/RelationService.d.ts +109 -0
- package/dist/services/cdc/CdcListener.d.ts +54 -0
- package/dist/services/cdc/trigger-cdc.d.ts +64 -0
- package/dist/services/collection-helpers.d.ts +38 -0
- package/dist/services/dataService.d.ts +110 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/realtimeService.d.ts +298 -0
- package/dist/src-Eh-CZosp.js +595 -0
- package/dist/src-Eh-CZosp.js.map +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/utils/drizzle-conditions.d.ts +138 -0
- package/dist/utils/pg-array-null-patch.d.ts +16 -0
- package/dist/utils/pg-error-utils.d.ts +65 -0
- package/dist/utils/table-classification.d.ts +8 -0
- package/dist/websocket.d.ts +18 -0
- package/package.json +113 -0
- package/src/PostgresAdapter.ts +58 -0
- package/src/PostgresBackendDriver.ts +1387 -0
- package/src/PostgresBootstrapper.ts +581 -0
- package/src/auth/ensure-tables.ts +367 -0
- package/src/auth/services.ts +1321 -0
- package/src/backup/backup-cli.ts +383 -0
- package/src/backup/backup-cron.ts +189 -0
- package/src/backup/backup-service.ts +299 -0
- package/src/backup/index.ts +12 -0
- package/src/backup/pg-tools.ts +231 -0
- package/src/backup/retention.ts +75 -0
- package/src/cli-errors.ts +265 -0
- package/src/cli-helpers.ts +196 -0
- package/src/cli.ts +786 -0
- package/src/collections/PostgresCollectionRegistry.ts +103 -0
- package/src/connection.ts +166 -0
- package/src/data-transformer.ts +733 -0
- package/src/databasePoolManager.ts +87 -0
- package/src/history/HistoryService.ts +272 -0
- package/src/history/ensure-history-table.ts +46 -0
- package/src/index.ts +15 -0
- package/src/interfaces.ts +60 -0
- package/src/schema/auth-bootstrap-sql.ts +41 -0
- package/src/schema/auth-default-policies.ts +125 -0
- package/src/schema/auth-schema.ts +233 -0
- package/src/schema/doctor-cli.ts +113 -0
- package/src/schema/doctor.ts +733 -0
- package/src/schema/dynamic-tables.test.ts +302 -0
- package/src/schema/dynamic-tables.ts +293 -0
- package/src/schema/generate-drizzle-schema-logic.ts +850 -0
- package/src/schema/generate-drizzle-schema.ts +131 -0
- package/src/schema/generate-postgres-ddl-logic.ts +490 -0
- package/src/schema/generate-postgres-ddl.ts +92 -0
- package/src/schema/introspect-db-inference.ts +238 -0
- package/src/schema/introspect-db-logic.ts +910 -0
- package/src/schema/introspect-db.ts +266 -0
- package/src/schema/introspect-runtime.test.ts +212 -0
- package/src/schema/introspect-runtime.ts +293 -0
- package/src/schema/test-schema.ts +11 -0
- package/src/security/policy-drift.test.ts +122 -0
- package/src/security/policy-drift.ts +159 -0
- package/src/security/rls-enforcement.ts +295 -0
- package/src/services/BranchService.ts +251 -0
- package/src/services/FetchService.ts +1661 -0
- package/src/services/PersistService.ts +329 -0
- package/src/services/RelationService.ts +1306 -0
- package/src/services/cdc/CdcListener.ts +167 -0
- package/src/services/cdc/trigger-cdc.ts +169 -0
- package/src/services/collection-helpers.ts +151 -0
- package/src/services/dataService.ts +246 -0
- package/src/services/index.ts +13 -0
- package/src/services/realtimeService.ts +1502 -0
- package/src/types.ts +4 -0
- package/src/utils/drizzle-conditions.ts +1162 -0
- package/src/utils/pg-array-null-patch.ts +42 -0
- package/src/utils/pg-error-utils.ts +227 -0
- package/src/utils/table-classification.ts +16 -0
- package/src/websocket.ts +640 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rebase
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# @rebasepro/server-postgres
|
|
2
|
+
|
|
3
|
+
PostgreSQL database driver for Rebase, built on Drizzle ORM.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @rebasepro/server-postgres
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What This Package Does
|
|
12
|
+
|
|
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, snapshot history, schema generation, branching, read replicas, and WebSocket support. Plug it into `@rebasepro/server` via `createPostgresAdapter()` or `createPostgresBootstrapper()`.
|
|
14
|
+
|
|
15
|
+
## Key Exports
|
|
16
|
+
|
|
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`). |
|
|
31
|
+
|
|
32
|
+
## Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createPostgresDatabaseConnection } from "@rebasepro/server-postgres";
|
|
36
|
+
import { createPostgresAdapter } from "@rebasepro/server-postgres";
|
|
37
|
+
import { initializeRebaseBackend } from "@rebasepro/server";
|
|
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
|
|
47
|
+
const database = createPostgresAdapter({
|
|
48
|
+
connection: db,
|
|
49
|
+
connectionString: process.env.DATABASE_URL,
|
|
50
|
+
schema: { tables: schema },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const backend = await initializeRebaseBackend({
|
|
54
|
+
app,
|
|
55
|
+
server,
|
|
56
|
+
database,
|
|
57
|
+
collections,
|
|
58
|
+
auth: { /* ... */ },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Graceful shutdown
|
|
62
|
+
process.on("SIGTERM", async () => {
|
|
63
|
+
await backend.shutdown();
|
|
64
|
+
await pool.end();
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
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` | Backend orchestrator that consumes this adapter |
|
|
84
|
+
| `@rebasepro/types` | Shared interfaces (`DatabaseAdapter`, `BackendBootstrapper`, `DataDriver`) |
|
|
85
|
+
| `@rebasepro/codegen` | Generates typed SDKs from collections |
|
|
86
|
+
| `@rebasepro/common` | Default collections and shared utilities |
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { DatabaseAdapter } from "@rebasepro/types";
|
|
2
|
+
import type { PostgresDriverConfig } from "./PostgresBootstrapper";
|
|
3
|
+
/**
|
|
4
|
+
* Creates a Postgres database adapter for Rebase.
|
|
5
|
+
*/
|
|
6
|
+
export declare function createPostgresAdapter(pgConfig: PostgresDriverConfig): DatabaseAdapter;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { DataService } from "./services/dataService";
|
|
2
|
+
import { BranchService } from "./services/BranchService";
|
|
3
|
+
import { RealtimeService } from "./services/realtimeService";
|
|
4
|
+
import { DatabasePoolManager } from "./databasePoolManager";
|
|
5
|
+
import { DrizzleClient } from "./interfaces";
|
|
6
|
+
import { DatabaseAdmin, DataDriver, DeleteProps, CollectionConfig, FetchCollectionProps, FetchOneProps, ListenCollectionProps, ListenOneProps, RebaseClient, RebaseSdkData, RestFetchService, SaveProps, TableMetadata, User } from "@rebasepro/types";
|
|
7
|
+
import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
|
|
8
|
+
import { HistoryService } from "./history/HistoryService";
|
|
9
|
+
export declare class PostgresBackendDriver implements DataDriver {
|
|
10
|
+
db: DrizzleClient;
|
|
11
|
+
readonly registry: PostgresCollectionRegistry;
|
|
12
|
+
poolManager?: DatabasePoolManager | undefined;
|
|
13
|
+
key: string;
|
|
14
|
+
initialised: boolean;
|
|
15
|
+
dataService: DataService;
|
|
16
|
+
realtimeService: RealtimeService;
|
|
17
|
+
historyService?: HistoryService;
|
|
18
|
+
branchService?: BranchService;
|
|
19
|
+
user?: User;
|
|
20
|
+
data: RebaseSdkData;
|
|
21
|
+
client?: RebaseClient;
|
|
22
|
+
/**
|
|
23
|
+
* Auto-set to `true` when a SET LOCAL ROLE fails with insufficient
|
|
24
|
+
* privileges, so subsequent queries skip the doomed attempt.
|
|
25
|
+
* Mirrors the static `DISABLE_DB_ROLE_SWITCHING` env var but is
|
|
26
|
+
* learned at runtime.
|
|
27
|
+
*/
|
|
28
|
+
private _roleSwitchingDisabled;
|
|
29
|
+
/**
|
|
30
|
+
* Restricted role that authenticated (user-context) requests run as (via
|
|
31
|
+
* `SET LOCAL ROLE`) so RLS binds every statement — reads *and* writes. Set
|
|
32
|
+
* by the bootstrapper after posture detection: defined when the connection
|
|
33
|
+
* would otherwise bypass RLS (superuser / BYPASSRLS / table owner),
|
|
34
|
+
* undefined when RLS already applies natively. The base (server-context)
|
|
35
|
+
* driver never switches — it is the trusted owner plane (auth flows,
|
|
36
|
+
* migrations, `dataAsAdmin`).
|
|
37
|
+
*/
|
|
38
|
+
rlsUserRole?: string;
|
|
39
|
+
/**
|
|
40
|
+
* When true, realtime notifications are deferred until after the
|
|
41
|
+
* wrapping transaction commits. Set by `withAuth` → `withTransaction`.
|
|
42
|
+
*/
|
|
43
|
+
_deferNotifications: boolean;
|
|
44
|
+
_pendingNotifications: Array<{
|
|
45
|
+
path: string;
|
|
46
|
+
id: string;
|
|
47
|
+
row: Record<string, unknown> | null;
|
|
48
|
+
databaseId?: string;
|
|
49
|
+
}>;
|
|
50
|
+
constructor(db: DrizzleClient, realtimeService: RealtimeService, registry: PostgresCollectionRegistry, user?: User, poolManager?: DatabasePoolManager | undefined, historyService?: HistoryService);
|
|
51
|
+
/**
|
|
52
|
+
* Typed admin capabilities (SQLAdmin + SchemaAdmin + BranchAdmin).
|
|
53
|
+
* Implemented as a getter so method references are resolved at call-time,
|
|
54
|
+
* allowing test spies applied after construction to take effect.
|
|
55
|
+
*/
|
|
56
|
+
get admin(): DatabaseAdmin;
|
|
57
|
+
/**
|
|
58
|
+
* REST-optimised fetch service (include-aware eager-loading).
|
|
59
|
+
* Delegates to the underlying FetchService (include-aware eager loading),
|
|
60
|
+
* then runs the afterRead pipeline on the results. The raw FetchService does
|
|
61
|
+
* NOT run callbacks, so masking must be applied here — otherwise every
|
|
62
|
+
* REST/SDK read leaks unmasked data (see {@link applyAfterReadForRest}).
|
|
63
|
+
*/
|
|
64
|
+
get restFetchService(): RestFetchService;
|
|
65
|
+
private buildCallContext;
|
|
66
|
+
private resolveCollectionCallbacks;
|
|
67
|
+
/**
|
|
68
|
+
* Run the three-tier afterRead pipeline (global → collection → property) on a
|
|
69
|
+
* single row for a collection whose callbacks have already been resolved.
|
|
70
|
+
*/
|
|
71
|
+
private applyAfterReadToRow;
|
|
72
|
+
private static hasAfterRead;
|
|
73
|
+
/**
|
|
74
|
+
* Apply afterRead to REST/SDK read results.
|
|
75
|
+
*
|
|
76
|
+
* The REST / `include` path fetches rows through the raw fetch service, which
|
|
77
|
+
* does NOT run callbacks — so without this, `afterRead` transforms (e.g. PII
|
|
78
|
+
* masking) are silently skipped on every SDK/REST read, leaking raw data.
|
|
79
|
+
* This choke point guarantees afterRead runs there too, matching the driver's
|
|
80
|
+
* fetchCollection/fetchOne paths.
|
|
81
|
+
*
|
|
82
|
+
* It also masks embedded relation data one level deep by running the TARGET
|
|
83
|
+
* collection's afterRead (so `post.author.email` is masked by the authors
|
|
84
|
+
* collection, not left raw).
|
|
85
|
+
*/
|
|
86
|
+
applyAfterReadForRest(rows: Record<string, unknown>[], path: string): Promise<Record<string, unknown>[]>;
|
|
87
|
+
fetchCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, vectorSearch }: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
|
|
88
|
+
listenCollection<M extends Record<string, unknown>>({ path, collection, filter, limit, offset, startAfter, orderBy, searchString, order, onUpdate, onError }: ListenCollectionProps<M>): () => void;
|
|
89
|
+
fetchOne<M extends Record<string, unknown>>({ path, id, databaseId, collection }: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
|
|
90
|
+
listenOne<M extends Record<string, unknown>>({ path, id, collection, onUpdate, onError }: ListenOneProps<M>): () => void;
|
|
91
|
+
save<M extends Record<string, unknown>>({ path, id, values, collection, status }: SaveProps<M>): Promise<Record<string, unknown>>;
|
|
92
|
+
delete<M extends Record<string, unknown>>({ row, collection }: DeleteProps<M>): Promise<void>;
|
|
93
|
+
deleteAll(path: string): Promise<void>;
|
|
94
|
+
checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean>;
|
|
95
|
+
count<M extends Record<string, unknown>>({ path, collection, filter, searchString }: FetchCollectionProps<M>): Promise<number>;
|
|
96
|
+
private getTargetDb;
|
|
97
|
+
executeSql(sqlText: string, options?: {
|
|
98
|
+
database?: string;
|
|
99
|
+
role?: string;
|
|
100
|
+
params?: unknown[];
|
|
101
|
+
}): Promise<Record<string, unknown>[]>;
|
|
102
|
+
fetchAvailableDatabases(): Promise<string[]>;
|
|
103
|
+
fetchAvailableRoles(): Promise<string[]>;
|
|
104
|
+
fetchCurrentDatabase(): Promise<string | undefined>;
|
|
105
|
+
/**
|
|
106
|
+
* Fetch public tables that are not yet mapped to a collection.
|
|
107
|
+
* Excludes internal tables (_rebase_*, _auth_*, auth tables, etc.)
|
|
108
|
+
* and junction/connection tables used for many-to-many relations.
|
|
109
|
+
*/
|
|
110
|
+
fetchUnmappedTables(mappedPaths?: string[]): Promise<string[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Fetch metadata for a given table from information_schema (columns, policies, constraints).
|
|
113
|
+
*/
|
|
114
|
+
fetchTableMetadata(tableName: string): Promise<TableMetadata>;
|
|
115
|
+
private generateSubscriptionId;
|
|
116
|
+
/**
|
|
117
|
+
* Create a new delegate instance with authenticated context.
|
|
118
|
+
* Starts a transaction and sets the current_user_id and current_user_roles
|
|
119
|
+
* configuration parameters for PostgreSQL Row Level Security.
|
|
120
|
+
*/
|
|
121
|
+
withAuth(user: User): Promise<DataDriver>;
|
|
122
|
+
}
|
|
123
|
+
export declare class AuthenticatedPostgresBackendDriver implements DataDriver {
|
|
124
|
+
delegate: PostgresBackendDriver;
|
|
125
|
+
key: string;
|
|
126
|
+
initialised: boolean;
|
|
127
|
+
user: User;
|
|
128
|
+
data: RebaseSdkData;
|
|
129
|
+
constructor(delegate: PostgresBackendDriver, user: User);
|
|
130
|
+
/**
|
|
131
|
+
* Typed admin capabilities — delegates to the base driver.
|
|
132
|
+
*/
|
|
133
|
+
admin: DatabaseAdmin;
|
|
134
|
+
get restFetchService(): RestFetchService;
|
|
135
|
+
private withTransaction;
|
|
136
|
+
fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
|
|
137
|
+
/**
|
|
138
|
+
* Injects the authenticated user's context into the most recently
|
|
139
|
+
* registered realtime subscription so RLS-aware polling can apply.
|
|
140
|
+
*/
|
|
141
|
+
private injectAuthContext;
|
|
142
|
+
listenCollection<M extends Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
|
|
143
|
+
fetchOne<M extends Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
|
|
144
|
+
listenOne<M extends Record<string, unknown>>(props: ListenOneProps<M>): () => void;
|
|
145
|
+
save<M extends Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
|
|
146
|
+
delete<M extends Record<string, unknown>>(props: DeleteProps<M>): Promise<void>;
|
|
147
|
+
deleteAll(path: string): Promise<void>;
|
|
148
|
+
checkUniqueField(path: string, name: string, value: unknown, id?: string, collection?: CollectionConfig): Promise<boolean>;
|
|
149
|
+
count<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
|
|
150
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostgresBootstrapper
|
|
3
|
+
*
|
|
4
|
+
* Implements the `BackendBootstrapper` interface for PostgreSQL.
|
|
5
|
+
*/
|
|
6
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
7
|
+
import { BackendBootstrapper } from "@rebasepro/types";
|
|
8
|
+
import { PostgresBackendDriver } from "./PostgresBackendDriver";
|
|
9
|
+
import { RealtimeService } from "./services/realtimeService";
|
|
10
|
+
import { DatabasePoolManager } from "./databasePoolManager";
|
|
11
|
+
import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
|
|
12
|
+
export interface PostgresDriverConfig {
|
|
13
|
+
connectionString?: string;
|
|
14
|
+
adminConnectionString?: string;
|
|
15
|
+
readConnectionString?: string;
|
|
16
|
+
connection?: unknown;
|
|
17
|
+
schema?: {
|
|
18
|
+
tables?: Record<string, unknown>;
|
|
19
|
+
enums?: Record<string, unknown>;
|
|
20
|
+
relations?: Record<string, unknown>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* PostgreSQL schema to read when deriving collections from the database
|
|
24
|
+
* (BaaS mode). Defaults to `public`.
|
|
25
|
+
*/
|
|
26
|
+
introspectionSchema?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Opaque internals bag that PostgresBootstrapper stores during `initializeDriver()`
|
|
30
|
+
* and re-uses in subsequent lifecycle hooks.
|
|
31
|
+
*/
|
|
32
|
+
export interface PostgresDriverInternals {
|
|
33
|
+
db: NodePgDatabase<any>;
|
|
34
|
+
readDb?: NodePgDatabase<any>;
|
|
35
|
+
registry: PostgresCollectionRegistry;
|
|
36
|
+
realtimeService: RealtimeService;
|
|
37
|
+
driver: PostgresBackendDriver;
|
|
38
|
+
poolManager?: DatabasePoolManager;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Default PostgreSQL bootstrapper.
|
|
42
|
+
*
|
|
43
|
+
* Use it to register Postgres with `initializeRebaseBackend()`:
|
|
44
|
+
* ```typescript
|
|
45
|
+
* initializeRebaseBackend({
|
|
46
|
+
* ...config,
|
|
47
|
+
* bootstrappers: [postgresBootstrapper()]
|
|
48
|
+
* });
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare function createPostgresBootstrapper(pgConfig: PostgresDriverConfig): BackendBootstrapper;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import type { CollectionConfig } from "@rebasepro/types";
|
|
3
|
+
/**
|
|
4
|
+
* Auto-create auth tables if they don't exist.
|
|
5
|
+
*
|
|
6
|
+
* @param db — Drizzle database instance
|
|
7
|
+
* @param collection — The collection that represents auth users.
|
|
8
|
+
* When omitted, a default `rebase.users` table is created.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ensureAuthTablesExist(db: NodePgDatabase, collection?: CollectionConfig): Promise<void>;
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
+
import type { RebasePgTable } from "../types";
|
|
3
|
+
import { UserRepository, TokenRepository, MfaRepository, AuthRepository, UserData, CreateUserData, RoleData, CreateRoleData, RefreshTokenInfo, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo, RoleData as Role } from "@rebasepro/server";
|
|
4
|
+
export type { Role };
|
|
5
|
+
export interface AuthSchemaTables {
|
|
6
|
+
users: RebasePgTable;
|
|
7
|
+
refreshTokens: RebasePgTable;
|
|
8
|
+
passwordResetTokens: RebasePgTable;
|
|
9
|
+
appConfig: RebasePgTable;
|
|
10
|
+
userIdentities: RebasePgTable;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* PostgreSQL implementation of UserRepository.
|
|
14
|
+
* Handles all user-related database operations using Drizzle ORM.
|
|
15
|
+
*/
|
|
16
|
+
export declare class UserService implements UserRepository {
|
|
17
|
+
private db;
|
|
18
|
+
private usersTable;
|
|
19
|
+
private userIdentitiesTable;
|
|
20
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
21
|
+
private getQualifiedUsersTableName;
|
|
22
|
+
private mapRowToUser;
|
|
23
|
+
private mapPayload;
|
|
24
|
+
createUser(data: CreateUserData): Promise<UserData>;
|
|
25
|
+
getUserById(id: string): Promise<UserData | null>;
|
|
26
|
+
getUserByEmail(email: string): Promise<UserData | null>;
|
|
27
|
+
getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
|
|
28
|
+
getUserIdentities(userId: string): Promise<UserIdentityData[]>;
|
|
29
|
+
linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
|
|
30
|
+
updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
|
|
31
|
+
deleteUser(id: string): Promise<void>;
|
|
32
|
+
listUsers(): Promise<UserData[]>;
|
|
33
|
+
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
34
|
+
/**
|
|
35
|
+
* Update user's password hash
|
|
36
|
+
*/
|
|
37
|
+
updatePassword(id: string, passwordHash: string): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Set email verification status
|
|
40
|
+
*/
|
|
41
|
+
setEmailVerified(id: string, verified: boolean): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Set email verification token
|
|
44
|
+
*/
|
|
45
|
+
setVerificationToken(id: string, token: string | null): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Find user by email verification token
|
|
48
|
+
*/
|
|
49
|
+
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
52
|
+
*/
|
|
53
|
+
getUserRoles(userId: string): Promise<Role[]>;
|
|
54
|
+
/**
|
|
55
|
+
* Get role IDs for a user
|
|
56
|
+
*/
|
|
57
|
+
getUserRoleIds(userId: string): Promise<string[]>;
|
|
58
|
+
/**
|
|
59
|
+
* Set roles for a user (replaces existing roles)
|
|
60
|
+
*/
|
|
61
|
+
setUserRoles(userId: string, roleIds: string[]): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Assign a specific role to new user (appends if not present)
|
|
64
|
+
*/
|
|
65
|
+
assignDefaultRole(userId: string, roleId: string): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Get user with their roles
|
|
68
|
+
*/
|
|
69
|
+
getUserWithRoles(userId: string): Promise<{
|
|
70
|
+
user: UserData;
|
|
71
|
+
roles: Role[];
|
|
72
|
+
} | null>;
|
|
73
|
+
}
|
|
74
|
+
export declare class RefreshTokenService {
|
|
75
|
+
private db;
|
|
76
|
+
private refreshTokensTable;
|
|
77
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
78
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
79
|
+
findByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
80
|
+
deleteByHash(tokenHash: string): Promise<void>;
|
|
81
|
+
deleteAllForUser(userId: string): Promise<void>;
|
|
82
|
+
listForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
83
|
+
deleteById(id: string, userId: string): Promise<void>;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Password reset token service
|
|
87
|
+
*/
|
|
88
|
+
export declare class PasswordResetTokenService {
|
|
89
|
+
private db;
|
|
90
|
+
private passwordResetTokensTable;
|
|
91
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
92
|
+
private getQualifiedPasswordResetTokensTableName;
|
|
93
|
+
/**
|
|
94
|
+
* Create a password reset token
|
|
95
|
+
*/
|
|
96
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Find a valid (not expired, not used) token by hash
|
|
99
|
+
*/
|
|
100
|
+
findValidByHash(tokenHash: string): Promise<{
|
|
101
|
+
userId: string;
|
|
102
|
+
expiresAt: Date;
|
|
103
|
+
} | null>;
|
|
104
|
+
/**
|
|
105
|
+
* Mark token as used
|
|
106
|
+
*/
|
|
107
|
+
markAsUsed(tokenHash: string): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Delete all tokens for a user
|
|
110
|
+
*/
|
|
111
|
+
deleteAllForUser(userId: string): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Clean up expired tokens
|
|
114
|
+
*/
|
|
115
|
+
deleteExpired(): Promise<void>;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Magic link token service.
|
|
119
|
+
* Handles magic link token storage for passwordless email login.
|
|
120
|
+
*/
|
|
121
|
+
export declare class MagicLinkTokenService {
|
|
122
|
+
private db;
|
|
123
|
+
private magicLinkTokensTable;
|
|
124
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
125
|
+
private getQualifiedTableName;
|
|
126
|
+
createToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
127
|
+
findValidByHash(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
128
|
+
markAsUsed(tokenHash: string): Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* PostgreSQL implementation of TokenRepository.
|
|
132
|
+
* Combines refresh token and password reset token operations.
|
|
133
|
+
*/
|
|
134
|
+
export declare class PostgresTokenRepository implements TokenRepository {
|
|
135
|
+
private db;
|
|
136
|
+
private refreshTokenService;
|
|
137
|
+
private passwordResetTokenService;
|
|
138
|
+
private magicLinkTokenService;
|
|
139
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
140
|
+
createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
141
|
+
findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
142
|
+
deleteRefreshToken(tokenHash: string): Promise<void>;
|
|
143
|
+
deleteAllRefreshTokensForUser(userId: string): Promise<void>;
|
|
144
|
+
listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
145
|
+
deleteRefreshTokenById(id: string, userId: string): Promise<void>;
|
|
146
|
+
createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
147
|
+
findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
|
|
148
|
+
markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
|
|
149
|
+
deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
|
|
150
|
+
deleteExpiredTokens(): Promise<void>;
|
|
151
|
+
createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
152
|
+
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
153
|
+
markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* PostgreSQL implementation of AuthRepository.
|
|
157
|
+
* Combines user, role, and token repository operations.
|
|
158
|
+
* This provides a convenient single-class interface for all auth operations.
|
|
159
|
+
*/
|
|
160
|
+
export declare class PostgresAuthRepository implements AuthRepository {
|
|
161
|
+
private db;
|
|
162
|
+
private userService;
|
|
163
|
+
private tokenRepository;
|
|
164
|
+
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
165
|
+
createUser(data: CreateUserData): Promise<UserData>;
|
|
166
|
+
getUserById(id: string): Promise<UserData | null>;
|
|
167
|
+
getUserByEmail(email: string): Promise<UserData | null>;
|
|
168
|
+
getUserByIdentity(provider: string, providerId: string): Promise<UserData | null>;
|
|
169
|
+
getUserIdentities(userId: string): Promise<UserIdentityData[]>;
|
|
170
|
+
linkUserIdentity(userId: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void>;
|
|
171
|
+
updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null>;
|
|
172
|
+
deleteUser(id: string): Promise<void>;
|
|
173
|
+
listUsers(): Promise<UserData[]>;
|
|
174
|
+
listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult>;
|
|
175
|
+
updatePassword(id: string, passwordHash: string): Promise<void>;
|
|
176
|
+
setEmailVerified(id: string, verified: boolean): Promise<void>;
|
|
177
|
+
setVerificationToken(id: string, token: string | null): Promise<void>;
|
|
178
|
+
getUserByVerificationToken(token: string): Promise<UserData | null>;
|
|
179
|
+
getUserRoles(userId: string): Promise<RoleData[]>;
|
|
180
|
+
getUserRoleIds(userId: string): Promise<string[]>;
|
|
181
|
+
setUserRoles(userId: string, roleIds: string[]): Promise<void>;
|
|
182
|
+
assignDefaultRole(userId: string, roleId: string): Promise<void>;
|
|
183
|
+
getUserWithRoles(userId: string): Promise<{
|
|
184
|
+
user: UserData;
|
|
185
|
+
roles: RoleData[];
|
|
186
|
+
} | null>;
|
|
187
|
+
getRoleById(id: string): Promise<RoleData | null>;
|
|
188
|
+
listRoles(): Promise<RoleData[]>;
|
|
189
|
+
createRole(_data: CreateRoleData): Promise<RoleData>;
|
|
190
|
+
updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
|
|
191
|
+
deleteRole(_id: string): Promise<void>;
|
|
192
|
+
createRefreshToken(userId: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void>;
|
|
193
|
+
findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null>;
|
|
194
|
+
deleteRefreshToken(tokenHash: string): Promise<void>;
|
|
195
|
+
deleteAllRefreshTokensForUser(userId: string): Promise<void>;
|
|
196
|
+
listRefreshTokensForUser(userId: string): Promise<RefreshTokenInfo[]>;
|
|
197
|
+
deleteRefreshTokenById(id: string, userId: string): Promise<void>;
|
|
198
|
+
createPasswordResetToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
199
|
+
findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null>;
|
|
200
|
+
markPasswordResetTokenUsed(tokenHash: string): Promise<void>;
|
|
201
|
+
deleteAllPasswordResetTokensForUser(userId: string): Promise<void>;
|
|
202
|
+
deleteExpiredTokens(): Promise<void>;
|
|
203
|
+
createMagicLinkToken(userId: string, tokenHash: string, expiresAt: Date): Promise<void>;
|
|
204
|
+
findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null>;
|
|
205
|
+
markMagicLinkTokenUsed(tokenHash: string): Promise<void>;
|
|
206
|
+
private _mfaService;
|
|
207
|
+
private getMfaService;
|
|
208
|
+
createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
|
|
209
|
+
getMfaFactors(userId: string): Promise<MfaFactor[]>;
|
|
210
|
+
getMfaFactorById(factorId: string): Promise<(MfaFactor & {
|
|
211
|
+
secretEncrypted: string;
|
|
212
|
+
}) | null>;
|
|
213
|
+
verifyMfaFactor(factorId: string): Promise<void>;
|
|
214
|
+
deleteMfaFactor(factorId: string, userId: string): Promise<void>;
|
|
215
|
+
createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
|
|
216
|
+
getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
|
|
217
|
+
verifyMfaChallenge(challengeId: string): Promise<void>;
|
|
218
|
+
createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
|
|
219
|
+
useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
|
|
220
|
+
getUnusedRecoveryCodeCount(userId: string): Promise<number>;
|
|
221
|
+
deleteAllRecoveryCodes(userId: string): Promise<void>;
|
|
222
|
+
hasVerifiedMfaFactors(userId: string): Promise<boolean>;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* PostgreSQL implementation of MfaRepository.
|
|
226
|
+
* Handles all MFA-related database operations.
|
|
227
|
+
*/
|
|
228
|
+
export declare class MfaService implements MfaRepository {
|
|
229
|
+
private db;
|
|
230
|
+
private schemaName;
|
|
231
|
+
constructor(db: NodePgDatabase, schemaName?: string);
|
|
232
|
+
private qualify;
|
|
233
|
+
createMfaFactor(userId: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor>;
|
|
234
|
+
getMfaFactors(userId: string): Promise<MfaFactor[]>;
|
|
235
|
+
getMfaFactorById(factorId: string): Promise<(MfaFactor & {
|
|
236
|
+
secretEncrypted: string;
|
|
237
|
+
}) | null>;
|
|
238
|
+
verifyMfaFactor(factorId: string): Promise<void>;
|
|
239
|
+
deleteMfaFactor(factorId: string, userId: string): Promise<void>;
|
|
240
|
+
createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo>;
|
|
241
|
+
getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null>;
|
|
242
|
+
verifyMfaChallenge(challengeId: string): Promise<void>;
|
|
243
|
+
createRecoveryCodes(userId: string, codeHashes: string[]): Promise<void>;
|
|
244
|
+
useRecoveryCode(userId: string, codeHash: string): Promise<boolean>;
|
|
245
|
+
getUnusedRecoveryCodeCount(userId: string): Promise<number>;
|
|
246
|
+
deleteAllRecoveryCodes(userId: string): Promise<void>;
|
|
247
|
+
hasVerifiedMfaFactors(userId: string): Promise<boolean>;
|
|
248
|
+
}
|
|
249
|
+
/** PostgreSQL user repository implementation */
|
|
250
|
+
export type PostgresUserRepository = UserService;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { CronJobDefinition } from "@rebasepro/types";
|
|
2
|
+
import type { StorageController } from "@rebasepro/server";
|
|
3
|
+
import { BackupDestination } from "./pg-tools";
|
|
4
|
+
export interface BackupCronConfig {
|
|
5
|
+
/** Cron expression, e.g. `"0 3 * * *"` for 03:00 daily. */
|
|
6
|
+
schedule: string;
|
|
7
|
+
/** Postgres connection string. Defaults to `DATABASE_URL`. */
|
|
8
|
+
connectionString: string;
|
|
9
|
+
/** Where backups are written: a local path or an `s3://` / `gs://` URL. */
|
|
10
|
+
destination: BackupDestination;
|
|
11
|
+
/**
|
|
12
|
+
* Storage controller used for `s3`/`gcs` destinations. Reuse the one the
|
|
13
|
+
* backend already configured (S3/GCS/Local StorageController). Not needed
|
|
14
|
+
* for local destinations.
|
|
15
|
+
*/
|
|
16
|
+
storage?: StorageController;
|
|
17
|
+
/** Delete backups older than this many days. `0` disables pruning. */
|
|
18
|
+
retentionDays?: number;
|
|
19
|
+
/** Always keep at least this many recent backups regardless of age. */
|
|
20
|
+
keepMinimum?: number;
|
|
21
|
+
/** Schemas to exclude from the dump (defaults to Atlas revision schema). */
|
|
22
|
+
excludeSchemas?: string[];
|
|
23
|
+
/** Cron job display name. */
|
|
24
|
+
name?: string;
|
|
25
|
+
/** Whether the job is enabled. */
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface EnvResolution {
|
|
29
|
+
/** Resolved config, present when a schedule is configured. */
|
|
30
|
+
config?: Omit<BackupCronConfig, "storage">;
|
|
31
|
+
/** True when `BACKUP_SCHEDULE` is unset — the cron should be a no-op. */
|
|
32
|
+
disabled?: boolean;
|
|
33
|
+
/** Human-readable reason the config could not be built. */
|
|
34
|
+
error?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Build backup-cron config purely from environment variables.
|
|
38
|
+
* Recognised keys:
|
|
39
|
+
* - `BACKUP_SCHEDULE` cron expression (required to enable)
|
|
40
|
+
* - `BACKUP_DESTINATION` local path or `s3://` / `gs://` URL (required)
|
|
41
|
+
* - `BACKUP_RETENTION_DAYS` integer days (optional)
|
|
42
|
+
* - `BACKUP_KEEP_MINIMUM` integer count (optional)
|
|
43
|
+
* - `DATABASE_URL` connection string
|
|
44
|
+
*
|
|
45
|
+
* Pure and side-effect free so it can be unit-tested without a database.
|
|
46
|
+
*/
|
|
47
|
+
export declare function backupCronConfigFromEnv(env: Record<string, string | undefined>): EnvResolution;
|
|
48
|
+
/**
|
|
49
|
+
* Create a {@link CronJobDefinition} that dumps the database, uploads the
|
|
50
|
+
* result to the configured destination, and prunes old backups. Object
|
|
51
|
+
* destinations require {@link BackupCronConfig.storage}.
|
|
52
|
+
*/
|
|
53
|
+
export declare function createBackupCron(config: BackupCronConfig): CronJobDefinition;
|