@rebasepro/server-postgres 0.9.1-canary.d906fb7 → 0.9.1-canary.e2fc7b6
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 +126 -26
- package/dist/index.es.js.map +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/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,
|
|
@@ -9196,6 +9234,7 @@ var DatabasePoolManager = class {
|
|
|
9196
9234
|
pool.on("error", (err) => {
|
|
9197
9235
|
logger.error(`[DatabasePoolManager] Unexpected error on idle client for db ${databaseName}`, { error: err });
|
|
9198
9236
|
});
|
|
9237
|
+
guardPoolAgainstDirtyRelease(pool, `pg-pool:${databaseName}`);
|
|
9199
9238
|
this.pools.set(databaseName, pool);
|
|
9200
9239
|
return pool;
|
|
9201
9240
|
}
|
|
@@ -20756,14 +20795,22 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
20756
20795
|
$$ LANGUAGE sql STABLE
|
|
20757
20796
|
`);
|
|
20758
20797
|
});
|
|
20759
|
-
|
|
20760
|
-
|
|
20761
|
-
|
|
20762
|
-
|
|
20763
|
-
|
|
20764
|
-
|
|
20765
|
-
|
|
20766
|
-
|
|
20798
|
+
for (const columnDef of [
|
|
20799
|
+
"display_name VARCHAR(255)",
|
|
20800
|
+
"photo_url VARCHAR(500)",
|
|
20801
|
+
"roles TEXT[] DEFAULT '{}' NOT NULL",
|
|
20802
|
+
"password_hash VARCHAR(255)",
|
|
20803
|
+
"email_verified BOOLEAN DEFAULT FALSE NOT NULL",
|
|
20804
|
+
"email_verification_token VARCHAR(255)",
|
|
20805
|
+
"email_verification_sent_at TIMESTAMP WITH TIME ZONE",
|
|
20806
|
+
"is_anonymous BOOLEAN DEFAULT FALSE NOT NULL",
|
|
20807
|
+
"metadata JSONB DEFAULT '{}' NOT NULL",
|
|
20808
|
+
"created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL",
|
|
20809
|
+
"updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL"
|
|
20810
|
+
]) await db.execute(sql`
|
|
20811
|
+
ALTER TABLE ${sql.raw(usersTableName)}
|
|
20812
|
+
ADD COLUMN IF NOT EXISTS ${sql.raw(columnDef)}
|
|
20813
|
+
`);
|
|
20767
20814
|
try {
|
|
20768
20815
|
if ((await db.execute(sql`
|
|
20769
20816
|
SELECT EXISTS (
|
|
@@ -20834,6 +20881,34 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
20834
20881
|
CREATE INDEX IF NOT EXISTS idx_recovery_codes_user
|
|
20835
20882
|
ON ${sql.raw(recoveryCodesTableName)}(user_id)
|
|
20836
20883
|
`);
|
|
20884
|
+
try {
|
|
20885
|
+
const authTablePairs = [
|
|
20886
|
+
[usersSchema, resolvedTable],
|
|
20887
|
+
[authSchema, "user_identities"],
|
|
20888
|
+
[authSchema, "refresh_tokens"],
|
|
20889
|
+
[authSchema, "password_reset_tokens"],
|
|
20890
|
+
[authSchema, "app_config"],
|
|
20891
|
+
[authSchema, "mfa_factors"],
|
|
20892
|
+
[authSchema, "mfa_challenges"],
|
|
20893
|
+
[authSchema, "recovery_codes"]
|
|
20894
|
+
];
|
|
20895
|
+
for (const [schemaName, tableName] of authTablePairs) if ((await db.execute(sql`
|
|
20896
|
+
SELECT 1
|
|
20897
|
+
FROM pg_class c
|
|
20898
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
20899
|
+
WHERE n.nspname = ${schemaName}
|
|
20900
|
+
AND c.relname = ${tableName}
|
|
20901
|
+
AND c.relforcerowsecurity
|
|
20902
|
+
`)).rows.length > 0) {
|
|
20903
|
+
await db.execute(sql`
|
|
20904
|
+
ALTER TABLE ${sql.raw(`"${schemaName}"."${tableName}"`)}
|
|
20905
|
+
NO FORCE ROW LEVEL SECURITY
|
|
20906
|
+
`);
|
|
20907
|
+
logger.warn(`🔧 Cleared stale FORCE ROW LEVEL SECURITY on "${schemaName}"."${tableName}" (legacy RLS model — it binds the owner connection and breaks privileged auth writes)`);
|
|
20908
|
+
}
|
|
20909
|
+
} catch (rlsReconcileError) {
|
|
20910
|
+
logger.warn(`⚠️ Could not reconcile FORCE ROW LEVEL SECURITY on auth tables: ${rlsReconcileError instanceof Error ? rlsReconcileError.message : String(rlsReconcileError)}`);
|
|
20911
|
+
}
|
|
20837
20912
|
logger.info("✅ Auth tables ready");
|
|
20838
20913
|
} catch (error) {
|
|
20839
20914
|
logger.error("❌ Failed to create auth tables", { error });
|
|
@@ -20881,6 +20956,31 @@ var UserService = class {
|
|
|
20881
20956
|
const name = getTableName(this.usersTable);
|
|
20882
20957
|
return `"${getTableConfig(this.usersTable).schema || "public"}"."${name}"`;
|
|
20883
20958
|
}
|
|
20959
|
+
/**
|
|
20960
|
+
* Run a privileged auth write with an explicitly cleared RLS context.
|
|
20961
|
+
*
|
|
20962
|
+
* The auth services run on the base/owner connection, which by design
|
|
20963
|
+
* carries a NULL `app.user_id` so the `auth.uid() IS NULL` server-escape
|
|
20964
|
+
* in the default policies applies. That NULL is normally guaranteed by
|
|
20965
|
+
* `set_config(..., is_local = true)` resetting at transaction end — but a
|
|
20966
|
+
* GUC that survives on a pooled connection (or a connection role that
|
|
20967
|
+
* doesn't bypass RLS: FORCE ROW LEVEL SECURITY, or a non-owner role)
|
|
20968
|
+
* turns the trusted write into an RLS-scoped one and denies it with
|
|
20969
|
+
* SQLSTATE 42501. Clearing the GUCs here, transaction-locally at the
|
|
20970
|
+
* single chokepoint, makes the server context deterministic instead of
|
|
20971
|
+
* trusting whatever state the pool hands us. `auth.uid()` reads '' as
|
|
20972
|
+
* NULL via NULLIF, so '' is the server context.
|
|
20973
|
+
*/
|
|
20974
|
+
async withServerContext(fn) {
|
|
20975
|
+
return await this.db.transaction(async (tx) => {
|
|
20976
|
+
await tx.execute(sql`
|
|
20977
|
+
SELECT set_config('app.user_id', '', true),
|
|
20978
|
+
set_config('app.user_roles', '', true),
|
|
20979
|
+
set_config('app.jwt', '', true)
|
|
20980
|
+
`);
|
|
20981
|
+
return await fn(tx);
|
|
20982
|
+
});
|
|
20983
|
+
}
|
|
20884
20984
|
mapRowToUser(row) {
|
|
20885
20985
|
if (!row) return row;
|
|
20886
20986
|
const id = row.id ?? row.uid;
|
|
@@ -20978,7 +21078,7 @@ var UserService = class {
|
|
|
20978
21078
|
}
|
|
20979
21079
|
async createUser(data) {
|
|
20980
21080
|
const payload = this.mapPayload(data);
|
|
20981
|
-
const [row] = await this.db.insert(this.usersTable).values(payload).returning();
|
|
21081
|
+
const [row] = await this.withServerContext(async (db) => await db.insert(this.usersTable).values(payload).returning());
|
|
20982
21082
|
return this.mapRowToUser(row);
|
|
20983
21083
|
}
|
|
20984
21084
|
async getUserById(id) {
|
|
@@ -21017,12 +21117,12 @@ var UserService = class {
|
|
|
21017
21117
|
}));
|
|
21018
21118
|
}
|
|
21019
21119
|
async linkUserIdentity(userId, provider, providerId, profileData) {
|
|
21020
|
-
await this.db.insert(this.userIdentitiesTable).values({
|
|
21120
|
+
await this.withServerContext(async (db) => db.insert(this.userIdentitiesTable).values({
|
|
21021
21121
|
userId,
|
|
21022
21122
|
provider,
|
|
21023
21123
|
providerId,
|
|
21024
21124
|
profileData: profileData || null
|
|
21025
|
-
}).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] });
|
|
21125
|
+
}).onConflictDoNothing({ target: [this.userIdentitiesTable.provider, this.userIdentitiesTable.providerId] }));
|
|
21026
21126
|
}
|
|
21027
21127
|
async updateUser(id, data) {
|
|
21028
21128
|
const idCol = getColumn(this.usersTable, "id");
|
|
@@ -21030,13 +21130,13 @@ var UserService = class {
|
|
|
21030
21130
|
const payload = this.mapPayload(data);
|
|
21031
21131
|
const updatedAtKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21032
21132
|
payload[updatedAtKey] = /* @__PURE__ */ new Date();
|
|
21033
|
-
const [row] = await this.db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning();
|
|
21133
|
+
const [row] = await this.withServerContext(async (db) => await db.update(this.usersTable).set(payload).where(eq(idCol, id)).returning());
|
|
21034
21134
|
return row ? this.mapRowToUser(row) : null;
|
|
21035
21135
|
}
|
|
21036
21136
|
async deleteUser(id) {
|
|
21037
21137
|
const idCol = getColumn(this.usersTable, "id");
|
|
21038
21138
|
if (!idCol) return;
|
|
21039
|
-
await this.db.delete(this.usersTable).where(eq(idCol, id));
|
|
21139
|
+
await this.withServerContext(async (db) => db.delete(this.usersTable).where(eq(idCol, id)));
|
|
21040
21140
|
}
|
|
21041
21141
|
async listUsers() {
|
|
21042
21142
|
return (await this.db.select().from(this.usersTable)).map((row) => this.mapRowToUser(row));
|
|
@@ -21090,10 +21190,10 @@ var UserService = class {
|
|
|
21090
21190
|
if (!idCol) return;
|
|
21091
21191
|
const passwordHashColKey = getColumnKey(this.usersTable, "passwordHash", "password_hash") || "passwordHash";
|
|
21092
21192
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21093
|
-
await this.db.update(this.usersTable).set({
|
|
21193
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21094
21194
|
[passwordHashColKey]: passwordHash,
|
|
21095
21195
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21096
|
-
}).where(eq(idCol, id));
|
|
21196
|
+
}).where(eq(idCol, id)));
|
|
21097
21197
|
}
|
|
21098
21198
|
/**
|
|
21099
21199
|
* Set email verification status
|
|
@@ -21104,11 +21204,11 @@ var UserService = class {
|
|
|
21104
21204
|
const emailVerifiedColKey = getColumnKey(this.usersTable, "emailVerified", "email_verified") || "emailVerified";
|
|
21105
21205
|
const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
|
|
21106
21206
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21107
|
-
await this.db.update(this.usersTable).set({
|
|
21207
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21108
21208
|
[emailVerifiedColKey]: verified,
|
|
21109
21209
|
[emailVerificationTokenColKey]: null,
|
|
21110
21210
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21111
|
-
}).where(eq(idCol, id));
|
|
21211
|
+
}).where(eq(idCol, id)));
|
|
21112
21212
|
}
|
|
21113
21213
|
/**
|
|
21114
21214
|
* Set email verification token
|
|
@@ -21119,11 +21219,11 @@ var UserService = class {
|
|
|
21119
21219
|
const emailVerificationTokenColKey = getColumnKey(this.usersTable, "emailVerificationToken", "email_verification_token") || "emailVerificationToken";
|
|
21120
21220
|
const emailVerificationSentAtColKey = getColumnKey(this.usersTable, "emailVerificationSentAt", "email_verification_sent_at") || "emailVerificationSentAt";
|
|
21121
21221
|
const updatedAtColKey = getColumnKey(this.usersTable, "updatedAt", "updated_at") || "updatedAt";
|
|
21122
|
-
await this.db.update(this.usersTable).set({
|
|
21222
|
+
await this.withServerContext(async (db) => db.update(this.usersTable).set({
|
|
21123
21223
|
[emailVerificationTokenColKey]: token,
|
|
21124
21224
|
[emailVerificationSentAtColKey]: token ? /* @__PURE__ */ new Date() : null,
|
|
21125
21225
|
[updatedAtColKey]: /* @__PURE__ */ new Date()
|
|
21126
|
-
}).where(eq(idCol, id));
|
|
21226
|
+
}).where(eq(idCol, id)));
|
|
21127
21227
|
}
|
|
21128
21228
|
/**
|
|
21129
21229
|
* Find user by email verification token
|
|
@@ -21168,22 +21268,22 @@ var UserService = class {
|
|
|
21168
21268
|
async setUserRoles(userId, roleIds) {
|
|
21169
21269
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
21170
21270
|
const rolesArray = `{${roleIds.join(",")}}`;
|
|
21171
|
-
await this.db.execute(sql`
|
|
21271
|
+
await this.withServerContext(async (db) => db.execute(sql`
|
|
21172
21272
|
UPDATE ${sql.raw(usersTableName)}
|
|
21173
21273
|
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
21174
21274
|
WHERE id = ${userId}
|
|
21175
|
-
`);
|
|
21275
|
+
`));
|
|
21176
21276
|
}
|
|
21177
21277
|
/**
|
|
21178
21278
|
* Assign a specific role to new user (appends if not present)
|
|
21179
21279
|
*/
|
|
21180
21280
|
async assignDefaultRole(userId, roleId) {
|
|
21181
21281
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
21182
|
-
await this.db.execute(sql`
|
|
21282
|
+
await this.withServerContext(async (db) => db.execute(sql`
|
|
21183
21283
|
UPDATE ${sql.raw(usersTableName)}
|
|
21184
21284
|
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
21185
21285
|
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
21186
|
-
`);
|
|
21286
|
+
`));
|
|
21187
21287
|
}
|
|
21188
21288
|
/**
|
|
21189
21289
|
* Get user with their roles
|
|
@@ -22833,6 +22933,6 @@ function createPostgresAdapter(pgConfig) {
|
|
|
22833
22933
|
};
|
|
22834
22934
|
}
|
|
22835
22935
|
//#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 };
|
|
22936
|
+
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
22937
|
|
|
22838
22938
|
//# sourceMappingURL=index.es.js.map
|