@rebasepro/server-postgres 0.9.1-canary.d906fb7 → 0.9.1-canary.e3f810f
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/dist/auth/services.d.ts +16 -0
- package/dist/connection.d.ts +21 -0
- package/dist/index.es.js +148 -29
- package/dist/index.es.js.map +1 -1
- package/dist/schema/doctor.d.ts +1 -1
- package/package.json +6 -6
- package/src/auth/ensure-tables.ts +73 -11
- package/src/auth/services.ts +49 -19
- package/src/connection.ts +61 -1
- package/src/databasePoolManager.ts +2 -0
- package/src/schema/doctor.ts +45 -20
- package/src/services/row-pipeline.ts +27 -3
package/dist/auth/services.d.ts
CHANGED
|
@@ -19,6 +19,22 @@ export declare class UserService implements UserRepository {
|
|
|
19
19
|
private userIdentitiesTable;
|
|
20
20
|
constructor(db: NodePgDatabase, tableOrTables?: RebasePgTable | Partial<AuthSchemaTables>);
|
|
21
21
|
private getQualifiedUsersTableName;
|
|
22
|
+
/**
|
|
23
|
+
* Run a privileged auth write with an explicitly cleared RLS context.
|
|
24
|
+
*
|
|
25
|
+
* The auth services run on the base/owner connection, which by design
|
|
26
|
+
* carries a NULL `app.user_id` so the `auth.uid() IS NULL` server-escape
|
|
27
|
+
* in the default policies applies. That NULL is normally guaranteed by
|
|
28
|
+
* `set_config(..., is_local = true)` resetting at transaction end — but a
|
|
29
|
+
* GUC that survives on a pooled connection (or a connection role that
|
|
30
|
+
* doesn't bypass RLS: FORCE ROW LEVEL SECURITY, or a non-owner role)
|
|
31
|
+
* turns the trusted write into an RLS-scoped one and denies it with
|
|
32
|
+
* SQLSTATE 42501. Clearing the GUCs here, transaction-locally at the
|
|
33
|
+
* single chokepoint, makes the server context deterministic instead of
|
|
34
|
+
* trusting whatever state the pool hands us. `auth.uid()` reads '' as
|
|
35
|
+
* NULL via NULLIF, so '' is the server context.
|
|
36
|
+
*/
|
|
37
|
+
private withServerContext;
|
|
22
38
|
private mapRowToUser;
|
|
23
39
|
private mapPayload;
|
|
24
40
|
createUser(data: CreateUserData): Promise<UserData>;
|
package/dist/connection.d.ts
CHANGED
|
@@ -19,6 +19,27 @@ export interface PostgresPoolConfig {
|
|
|
19
19
|
/** Enable TCP keep-alive (default: true) */
|
|
20
20
|
keepAlive?: boolean;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Destroy pool clients that are released while still inside a transaction.
|
|
24
|
+
*
|
|
25
|
+
* pg-pool returns a client to the idle list whenever `release()` is called
|
|
26
|
+
* without an error — even if the connection is still mid-transaction (status
|
|
27
|
+
* `T`/`E`). That happens in practice: drizzle's pool transaction releases in
|
|
28
|
+
* a `finally` after attempting ROLLBACK, and if the ROLLBACK itself fails
|
|
29
|
+
* (e.g. it was queued behind a statement that hit the client-side
|
|
30
|
+
* query_timeout), the client goes back dirty. The next checkout then runs
|
|
31
|
+
* its statements inside the zombie transaction — with the previous request's
|
|
32
|
+
* `app.*` RLS GUCs still applied, which turns unrelated queries into
|
|
33
|
+
* RLS-scoped ones (observed in production as registration failing with
|
|
34
|
+
* SQLSTATE 42501 under a leaked anonymous context).
|
|
35
|
+
*
|
|
36
|
+
* pg-pool emits `release` before it consults its private `_expired` set, so
|
|
37
|
+
* marking the client expired here makes `_release()` destroy it instead of
|
|
38
|
+
* pooling it. Both `client._txStatus` (pg ≥ 8.16) and `pool._expired` are
|
|
39
|
+
* private APIs — feature-detect and fall back to loud logging so an upstream
|
|
40
|
+
* change degrades to observability, never to silent corruption.
|
|
41
|
+
*/
|
|
42
|
+
export declare function guardPoolAgainstDirtyRelease(pool: Pool, label: string): void;
|
|
22
43
|
/**
|
|
23
44
|
* Create a Drizzle-backed Postgres connection with a production-grade
|
|
24
45
|
* connection pool.
|
package/dist/index.es.js
CHANGED
|
@@ -71,16 +71,51 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
71
71
|
var connection_exports = /* @__PURE__ */ __exportAll({
|
|
72
72
|
createDirectDatabaseConnection: () => createDirectDatabaseConnection,
|
|
73
73
|
createPostgresDatabaseConnection: () => createPostgresDatabaseConnection,
|
|
74
|
-
createReadReplicaConnection: () => createReadReplicaConnection
|
|
74
|
+
createReadReplicaConnection: () => createReadReplicaConnection,
|
|
75
|
+
guardPoolAgainstDirtyRelease: () => guardPoolAgainstDirtyRelease
|
|
75
76
|
});
|
|
76
77
|
var DEFAULT_POOL = {
|
|
77
78
|
max: 20,
|
|
78
79
|
idleTimeoutMillis: 3e4,
|
|
79
80
|
connectionTimeoutMillis: 1e4,
|
|
80
|
-
queryTimeout:
|
|
81
|
+
queryTimeout: 6e4,
|
|
81
82
|
statementTimeout: 3e4,
|
|
82
83
|
keepAlive: true
|
|
83
84
|
};
|
|
85
|
+
/** ReadyForQuery status byte: `I` idle, `T` in transaction, `E` failed transaction. */
|
|
86
|
+
var TX_IDLE = "I";
|
|
87
|
+
/**
|
|
88
|
+
* Destroy pool clients that are released while still inside a transaction.
|
|
89
|
+
*
|
|
90
|
+
* pg-pool returns a client to the idle list whenever `release()` is called
|
|
91
|
+
* without an error — even if the connection is still mid-transaction (status
|
|
92
|
+
* `T`/`E`). That happens in practice: drizzle's pool transaction releases in
|
|
93
|
+
* a `finally` after attempting ROLLBACK, and if the ROLLBACK itself fails
|
|
94
|
+
* (e.g. it was queued behind a statement that hit the client-side
|
|
95
|
+
* query_timeout), the client goes back dirty. The next checkout then runs
|
|
96
|
+
* its statements inside the zombie transaction — with the previous request's
|
|
97
|
+
* `app.*` RLS GUCs still applied, which turns unrelated queries into
|
|
98
|
+
* RLS-scoped ones (observed in production as registration failing with
|
|
99
|
+
* SQLSTATE 42501 under a leaked anonymous context).
|
|
100
|
+
*
|
|
101
|
+
* pg-pool emits `release` before it consults its private `_expired` set, so
|
|
102
|
+
* marking the client expired here makes `_release()` destroy it instead of
|
|
103
|
+
* pooling it. Both `client._txStatus` (pg ≥ 8.16) and `pool._expired` are
|
|
104
|
+
* private APIs — feature-detect and fall back to loud logging so an upstream
|
|
105
|
+
* change degrades to observability, never to silent corruption.
|
|
106
|
+
*/
|
|
107
|
+
function guardPoolAgainstDirtyRelease(pool, label) {
|
|
108
|
+
pool.on("release", (err, client) => {
|
|
109
|
+
if (err) return;
|
|
110
|
+
const txStatus = client?._txStatus;
|
|
111
|
+
if (typeof txStatus !== "string" || txStatus === TX_IDLE) return;
|
|
112
|
+
const expired = pool._expired;
|
|
113
|
+
if (expired && typeof expired.add === "function" && typeof expired.has === "function" && client && typeof client === "object") {
|
|
114
|
+
expired.add(client);
|
|
115
|
+
logger.error(`[${label}] Client released back to the pool while still in a transaction (status '${txStatus}') — destroying it so the open transaction and its session state (RLS GUCs) cannot leak into the next request.`);
|
|
116
|
+
} else logger.error(`[${label}] Client released mid-transaction (status '${txStatus}') but the pool's internal expiry set is unavailable (pg-pool internals changed?). The connection may leak its open transaction into subsequent requests.`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
84
119
|
/**
|
|
85
120
|
* Create a Drizzle-backed Postgres connection with a production-grade
|
|
86
121
|
* connection pool.
|
|
@@ -112,6 +147,7 @@ function createPostgresDatabaseConnection(connectionString, schema, poolConfig)
|
|
|
112
147
|
logger.error("[pg-pool] Unexpected pool error", { detail: err.message });
|
|
113
148
|
if (err.message.includes("ETIMEDOUT")) logger.warn("[pg-pool] Connection timeout detected — pool will auto-retry");
|
|
114
149
|
});
|
|
150
|
+
guardPoolAgainstDirtyRelease(pool, "pg-pool");
|
|
115
151
|
return {
|
|
116
152
|
db: schema ? drizzle(pool, { schema }) : drizzle(pool),
|
|
117
153
|
pool,
|
|
@@ -144,6 +180,7 @@ function createDirectDatabaseConnection(connectionString, schema, poolConfig) {
|
|
|
144
180
|
pool.on("error", (err) => {
|
|
145
181
|
logger.error("[pg-direct-pool] Unexpected pool error", { detail: err.message });
|
|
146
182
|
});
|
|
183
|
+
guardPoolAgainstDirtyRelease(pool, "pg-direct-pool");
|
|
147
184
|
return {
|
|
148
185
|
db: schema ? drizzle(pool, { schema }) : drizzle(pool),
|
|
149
186
|
pool,
|
|
@@ -173,6 +210,7 @@ function createReadReplicaConnection(connectionString, schema, poolConfig) {
|
|
|
173
210
|
pool.on("error", (err) => {
|
|
174
211
|
logger.error("[pg-replica-pool] Unexpected pool error", { detail: err.message });
|
|
175
212
|
});
|
|
213
|
+
guardPoolAgainstDirtyRelease(pool, "pg-replica-pool");
|
|
176
214
|
return {
|
|
177
215
|
db: schema ? drizzle(pool, { schema }) : drizzle(pool),
|
|
178
216
|
pool,
|
|
@@ -6486,8 +6524,27 @@ function coerceDeclaredNumbers(row, collection) {
|
|
|
6486
6524
|
return out;
|
|
6487
6525
|
}
|
|
6488
6526
|
/** Render one target row in the requested style. */
|
|
6527
|
+
/**
|
|
6528
|
+
* Drop every column the collection marked `excludeFromApi`.
|
|
6529
|
+
*
|
|
6530
|
+
* Password hashes and verification tokens have to be readable server-side but
|
|
6531
|
+
* must never reach a client — and "never" has to mean every exit from this
|
|
6532
|
+
* pipeline, including relation targets, or a secret leaks through whichever
|
|
6533
|
+
* path was overlooked. Keyed by both the property name and its column name,
|
|
6534
|
+
* since a row can arrive keyed either way depending on the caller.
|
|
6535
|
+
*/
|
|
6536
|
+
function stripExcluded(row, collection) {
|
|
6537
|
+
const properties = collection.properties;
|
|
6538
|
+
if (!properties) return row;
|
|
6539
|
+
for (const [key, property] of Object.entries(properties)) {
|
|
6540
|
+
if (!property?.excludeFromApi) continue;
|
|
6541
|
+
delete row[key];
|
|
6542
|
+
if (property.columnName) delete row[property.columnName];
|
|
6543
|
+
}
|
|
6544
|
+
return row;
|
|
6545
|
+
}
|
|
6489
6546
|
function renderTarget(targetRow, targetCollection, style, registry) {
|
|
6490
|
-
if (style === "inline") return coerceDeclaredNumbers({ ...targetRow }, targetCollection);
|
|
6547
|
+
if (style === "inline") return stripExcluded(coerceDeclaredNumbers({ ...targetRow }, targetCollection), targetCollection);
|
|
6491
6548
|
const address = relationTargetAddress(targetRow, targetCollection, registry);
|
|
6492
6549
|
const path = targetCollection.slug;
|
|
6493
6550
|
return createRelationRefWithData(address, path, {
|
|
@@ -6529,7 +6586,7 @@ function toCmsRow(row, collection, registry) {
|
|
|
6529
6586
|
normalized[key] = relData.map((item) => renderTarget(isJunctionRelation(relation) ? unwrapJunctionRow(item) : item, targetCollection, "ref", registry));
|
|
6530
6587
|
} else if (relation.cardinality === "one" && typeof relData === "object" && !Array.isArray(relData)) normalized[key] = renderTarget(relData, relation.target(), "ref", registry);
|
|
6531
6588
|
}
|
|
6532
|
-
return normalized;
|
|
6589
|
+
return stripExcluded(normalized, collection);
|
|
6533
6590
|
}
|
|
6534
6591
|
/**
|
|
6535
6592
|
* The row REST serves: every column under its own name, with the value Postgres
|
|
@@ -6554,7 +6611,7 @@ function toRestRow(row, collection, registry) {
|
|
|
6554
6611
|
else if (relation && typeof value === "object" && value !== null) flat[key] = renderTarget(value, relation.target(), "inline", registry);
|
|
6555
6612
|
else flat[key] = coerceDeclaredNumber(value, collection.properties?.[key]);
|
|
6556
6613
|
}
|
|
6557
|
-
return flat;
|
|
6614
|
+
return stripExcluded(flat, collection);
|
|
6558
6615
|
}
|
|
6559
6616
|
//#endregion
|
|
6560
6617
|
//#region src/services/FetchService.ts
|
|
@@ -9196,6 +9253,7 @@ var DatabasePoolManager = class {
|
|
|
9196
9253
|
pool.on("error", (err) => {
|
|
9197
9254
|
logger.error(`[DatabasePoolManager] Unexpected error on idle client for db ${databaseName}`, { error: err });
|
|
9198
9255
|
});
|
|
9256
|
+
guardPoolAgainstDirtyRelease(pool, `pg-pool:${databaseName}`);
|
|
9199
9257
|
this.pools.set(databaseName, pool);
|
|
9200
9258
|
return pool;
|
|
9201
9259
|
}
|
|
@@ -20756,14 +20814,22 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
20756
20814
|
$$ LANGUAGE sql STABLE
|
|
20757
20815
|
`);
|
|
20758
20816
|
});
|
|
20759
|
-
|
|
20760
|
-
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20817
|
+
for (const columnDef of [
|
|
20818
|
+
"display_name VARCHAR(255)",
|
|
20819
|
+
"photo_url VARCHAR(500)",
|
|
20820
|
+
"roles TEXT[] DEFAULT '{}' NOT NULL",
|
|
20821
|
+
"password_hash VARCHAR(255)",
|
|
20822
|
+
"email_verified BOOLEAN DEFAULT FALSE NOT NULL",
|
|
20823
|
+
"email_verification_token VARCHAR(255)",
|
|
20824
|
+
"email_verification_sent_at TIMESTAMP WITH TIME ZONE",
|
|
20825
|
+
"is_anonymous BOOLEAN DEFAULT FALSE NOT NULL",
|
|
20826
|
+
"metadata JSONB DEFAULT '{}' NOT NULL",
|
|
20827
|
+
"created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL",
|
|
20828
|
+
"updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL"
|
|
20829
|
+
]) await db.execute(sql`
|
|
20830
|
+
ALTER TABLE ${sql.raw(usersTableName)}
|
|
20831
|
+
ADD COLUMN IF NOT EXISTS ${sql.raw(columnDef)}
|
|
20832
|
+
`);
|
|
20767
20833
|
try {
|
|
20768
20834
|
if ((await db.execute(sql`
|
|
20769
20835
|
SELECT EXISTS (
|
|
@@ -20834,6 +20900,34 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
20834
20900
|
CREATE INDEX IF NOT EXISTS idx_recovery_codes_user
|
|
20835
20901
|
ON ${sql.raw(recoveryCodesTableName)}(user_id)
|
|
20836
20902
|
`);
|
|
20903
|
+
try {
|
|
20904
|
+
const authTablePairs = [
|
|
20905
|
+
[usersSchema, resolvedTable],
|
|
20906
|
+
[authSchema, "user_identities"],
|
|
20907
|
+
[authSchema, "refresh_tokens"],
|
|
20908
|
+
[authSchema, "password_reset_tokens"],
|
|
20909
|
+
[authSchema, "app_config"],
|
|
20910
|
+
[authSchema, "mfa_factors"],
|
|
20911
|
+
[authSchema, "mfa_challenges"],
|
|
20912
|
+
[authSchema, "recovery_codes"]
|
|
20913
|
+
];
|
|
20914
|
+
for (const [schemaName, tableName] of authTablePairs) if ((await db.execute(sql`
|
|
20915
|
+
SELECT 1
|
|
20916
|
+
FROM pg_class c
|
|
20917
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
20918
|
+
WHERE n.nspname = ${schemaName}
|
|
20919
|
+
AND c.relname = ${tableName}
|
|
20920
|
+
AND c.relforcerowsecurity
|
|
20921
|
+
`)).rows.length > 0) {
|
|
20922
|
+
await db.execute(sql`
|
|
20923
|
+
ALTER TABLE ${sql.raw(`"${schemaName}"."${tableName}"`)}
|
|
20924
|
+
NO FORCE ROW LEVEL SECURITY
|
|
20925
|
+
`);
|
|
20926
|
+
logger.warn(`🔧 Cleared stale FORCE ROW LEVEL SECURITY on "${schemaName}"."${tableName}" (legacy RLS model — it binds the owner connection and breaks privileged auth writes)`);
|
|
20927
|
+
}
|
|
20928
|
+
} catch (rlsReconcileError) {
|
|
20929
|
+
logger.warn(`⚠️ Could not reconcile FORCE ROW LEVEL SECURITY on auth tables: ${rlsReconcileError instanceof Error ? rlsReconcileError.message : String(rlsReconcileError)}`);
|
|
20930
|
+
}
|
|
20837
20931
|
logger.info("✅ Auth tables ready");
|
|
20838
20932
|
} catch (error) {
|
|
20839
20933
|
logger.error("❌ Failed to create auth tables", { error });
|
|
@@ -20881,6 +20975,31 @@ var UserService = class {
|
|
|
20881
20975
|
const name = getTableName(this.usersTable);
|
|
20882
20976
|
return `"${getTableConfig(this.usersTable).schema || "public"}"."${name}"`;
|
|
20883
20977
|
}
|
|
20978
|
+
/**
|
|
20979
|
+
* Run a privileged auth write with an explicitly cleared RLS context.
|
|
20980
|
+
*
|
|
20981
|
+
* The auth services run on the base/owner connection, which by design
|
|
20982
|
+
* carries a NULL `app.user_id` so the `auth.uid() IS NULL` server-escape
|
|
20983
|
+
* in the default policies applies. That NULL is normally guaranteed by
|
|
20984
|
+
* `set_config(..., is_local = true)` resetting at transaction end — but a
|
|
20985
|
+
* GUC that survives on a pooled connection (or a connection role that
|
|
20986
|
+
* doesn't bypass RLS: FORCE ROW LEVEL SECURITY, or a non-owner role)
|
|
20987
|
+
* turns the trusted write into an RLS-scoped one and denies it with
|
|
20988
|
+
* SQLSTATE 42501. Clearing the GUCs here, transaction-locally at the
|
|
20989
|
+
* single chokepoint, makes the server context deterministic instead of
|
|
20990
|
+
* trusting whatever state the pool hands us. `auth.uid()` reads '' as
|
|
20991
|
+
* NULL via NULLIF, so '' is the server context.
|
|
20992
|
+
*/
|
|
20993
|
+
async withServerContext(fn) {
|
|
20994
|
+
return await this.db.transaction(async (tx) => {
|
|
20995
|
+
await tx.execute(sql`
|
|
20996
|
+
SELECT set_config('app.user_id', '', true),
|
|
20997
|
+
set_config('app.user_roles', '', true),
|
|
20998
|
+
set_config('app.jwt', '', true)
|
|
20999
|
+
`);
|
|
21000
|
+
return await fn(tx);
|
|
21001
|
+
});
|
|
21002
|
+
}
|
|
20884
21003
|
mapRowToUser(row) {
|
|
20885
21004
|
if (!row) return row;
|
|
20886
21005
|
const id = row.id ?? row.uid;
|
|
@@ -20978,7 +21097,7 @@ var UserService = class {
|
|
|
20978
21097
|
}
|
|
20979
21098
|
async createUser(data) {
|
|
20980
21099
|
const payload = this.mapPayload(data);
|
|
20981
|
-
const [row] = await this.db.insert(this.usersTable).values(payload).returning();
|
|
21100
|
+
const [row] = await this.withServerContext(async (db) => await db.insert(this.usersTable).values(payload).returning());
|
|
20982
21101
|
return this.mapRowToUser(row);
|
|
20983
21102
|
}
|
|
20984
21103
|
async getUserById(id) {
|
|
@@ -21017,12 +21136,12 @@ var UserService = class {
|
|
|
21017
21136
|
}));
|
|
21018
21137
|
}
|
|
21019
21138
|
async linkUserIdentity(userId, provider, providerId, profileData) {
|
|
21020
|
-
await this.db.insert(this.userIdentitiesTable).values({
|
|
21139
|
+
await this.withServerContext(async (db) => db.insert(this.userIdentitiesTable).values({
|
|
21021
21140
|
userId,
|
|
21022
21141
|
provider,
|
|
21023
21142
|
providerId,
|
|
21024
21143
|
profileData: profileData || null
|
|
21025
|
-
}).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] });
|
|
21144
|
+
}).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] }));
|
|
21026
21145
|
}
|
|
21027
21146
|
async updateUser(id, data) {
|
|
21028
21147
|
const idCol = getColumn(this.usersTable, "id");
|
|
@@ -21030,13 +21149,13 @@ var UserService = class {
|
|
|
21030
21149
|
const payload = this.mapPayload(data);
|
|
21031
21150
|
const updatedAtKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21032
21151
|
payload[updatedAtKey] = /* @__PURE__ */ new Date();
|
|
21033
|
-
const [row] = await this.db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning();
|
|
21152
|
+
const [row] = await this.withServerContext(async (db) => await db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning());
|
|
21034
21153
|
return row ? this.mapRowToUser(row) : null;
|
|
21035
21154
|
}
|
|
21036
21155
|
async deleteUser(id) {
|
|
21037
21156
|
const idCol = getColumn(this.usersTable, "id");
|
|
21038
21157
|
if (!idCol) return;
|
|
21039
|
-
await this.db.delete(this.usersTable).where(eq(idCol, id));
|
|
21158
|
+
await this.withServerContext(async (db) => db.delete(this.usersTable).where(eq(idCol, id)));
|
|
21040
21159
|
}
|
|
21041
21160
|
async listUsers() {
|
|
21042
21161
|
return (await this.db.select().from(this.usersTable)).map((row) => this.mapRowToUser(row));
|
|
@@ -21090,10 +21209,10 @@ var UserService = class {
|
|
|
21090
21209
|
if (!idCol) return;
|
|
21091
21210
|
const passwordHashColKey = getColumnKey(this.usersTable, "passwordHash", "password_hash") || "passwordHash";
|
|
21092
21211
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21093
|
-
await this.db.update(this.usersTable).set({
|
|
21212
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21094
21213
|
[passwordHashColKey]: passwordHash,
|
|
21095
21214
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21096
|
-
}).where(eq(idCol, id));
|
|
21215
|
+
}).where(eq(idCol, id)));
|
|
21097
21216
|
}
|
|
21098
21217
|
/**
|
|
21099
21218
|
* Set email verification status
|
|
@@ -21104,11 +21223,11 @@ var UserService = class {
|
|
|
21104
21223
|
const emailVerifiedColKey = getColumnKey(this.usersTable, "emailVerified", "email_verified") || "emailVerified";
|
|
21105
21224
|
const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
|
|
21106
21225
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21107
|
-
await this.db.update(this.usersTable).set({
|
|
21226
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21108
21227
|
[emailVerifiedColKey]: verified,
|
|
21109
21228
|
[emailVerificationTokenColKey]: null,
|
|
21110
21229
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21111
|
-
}).where(eq(idCol, id));
|
|
21230
|
+
}).where(eq(idCol, id)));
|
|
21112
21231
|
}
|
|
21113
21232
|
/**
|
|
21114
21233
|
* Set email verification token
|
|
@@ -21119,11 +21238,11 @@ var UserService = class {
|
|
|
21119
21238
|
const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
|
|
21120
21239
|
const emailVerificationSentAtColKey = getColumnKey(this.usersTable, "emailVerificationSentAt", "email_verification_sent_at") || "emailVerificationSentAt";
|
|
21121
21240
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21122
|
-
await this.db.update(this.usersTable).set({
|
|
21241
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21123
21242
|
[emailVerificationTokenColKey]: token,
|
|
21124
21243
|
[emailVerificationSentAtColKey]: token ? /* @__PURE__ */ new Date() : null,
|
|
21125
21244
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21126
|
-
}).where(eq(idCol, id));
|
|
21245
|
+
}).where(eq(idCol, id)));
|
|
21127
21246
|
}
|
|
21128
21247
|
/**
|
|
21129
21248
|
* Find user by email verification token
|
|
@@ -21168,22 +21287,22 @@ var UserService = class {
|
|
|
21168
21287
|
async setUserRoles(userId, roleIds) {
|
|
21169
21288
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
21170
21289
|
const rolesArray = `{${roleIds.join(",")}}`;
|
|
21171
|
-
await this.db.execute(sql`
|
|
21290
|
+
await this.withServerContext(async (db) => db.execute(sql`
|
|
21172
21291
|
UPDATE ${sql.raw(usersTableName)}
|
|
21173
21292
|
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
21174
21293
|
WHERE id = ${userId}
|
|
21175
|
-
`);
|
|
21294
|
+
`));
|
|
21176
21295
|
}
|
|
21177
21296
|
/**
|
|
21178
21297
|
* Assign a specific role to new user (appends if not present)
|
|
21179
21298
|
*/
|
|
21180
21299
|
async assignDefaultRole(userId, roleId) {
|
|
21181
21300
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
21182
|
-
await this.db.execute(sql`
|
|
21301
|
+
await this.withServerContext(async (db) => db.execute(sql`
|
|
21183
21302
|
UPDATE ${sql.raw(usersTableName)}
|
|
21184
21303
|
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
21185
21304
|
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
21186
|
-
`);
|
|
21305
|
+
`));
|
|
21187
21306
|
}
|
|
21188
21307
|
/**
|
|
21189
21308
|
* Get user with their roles
|
|
@@ -22833,6 +22952,6 @@ function createPostgresAdapter(pgConfig) {
|
|
|
22833
22952
|
};
|
|
22834
22953
|
}
|
|
22835
22954
|
//#endregion
|
|
22836
|
-
export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgRestoreArgs, checkToolServerCompatibility, createAuthSchema, createBackupCron, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, ensureDatabaseExists, generateSchema, getServerVersionMajor, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, resolveConnectionString, resolvePgBinary, restoreDump, selectBackupsToPrune, serverVersionNumToMajor, uploadBackup, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema, withDatabaseName };
|
|
22955
|
+
export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, DatabasePoolManager, DrizzleConditionBuilder, PostgresBackendDriver, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgRestoreArgs, checkToolServerCompatibility, createAuthSchema, createBackupCron, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, ensureDatabaseExists, generateSchema, getServerVersionMajor, guardPoolAgainstDirtyRelease, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, resolveConnectionString, resolvePgBinary, restoreDump, selectBackupsToPrune, serverVersionNumToMajor, uploadBackup, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema, withDatabaseName };
|
|
22837
22956
|
|
|
22838
22957
|
//# sourceMappingURL=index.es.js.map
|