@rebasepro/server-postgresql 0.2.4 → 0.2.5
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/common/src/collections/default-collections.d.ts +5 -8
- package/dist/index.es.js +286 -365
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +283 -362
- package/dist/index.umd.js.map +1 -1
- package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +2 -0
- package/dist/server-postgresql/src/auth/services.d.ts +6 -31
- package/dist/server-postgresql/src/schema/auth-schema.d.ts +87 -340
- package/dist/server-postgresql/src/services/EntityPersistService.d.ts +4 -0
- package/dist/server-postgresql/src/services/entityService.d.ts +4 -0
- package/dist/types/src/controllers/auth.d.ts +2 -2
- package/dist/types/src/controllers/client.d.ts +25 -40
- package/dist/types/src/controllers/data.d.ts +4 -0
- package/dist/types/src/controllers/data_driver.d.ts +5 -0
- package/dist/types/src/types/auth_adapter.d.ts +3 -56
- package/dist/types/src/types/backend.d.ts +2 -2
- package/dist/types/src/types/backend_hooks.d.ts +2 -17
- package/dist/types/src/types/properties.d.ts +7 -5
- package/dist/types/src/types/user_management_delegate.d.ts +16 -53
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -1
- package/package.json +6 -6
- package/src/PostgresBackendDriver.ts +10 -0
- package/src/PostgresBootstrapper.ts +3 -12
- package/src/auth/ensure-tables.ts +52 -101
- package/src/auth/services.ts +71 -170
- package/src/schema/auth-schema.ts +13 -69
- package/src/schema/doctor.ts +44 -3
- package/src/schema/generate-drizzle-schema-logic.ts +33 -3
- package/src/schema/introspect-db-logic.ts +7 -0
- package/src/services/EntityPersistService.ts +9 -0
- package/src/services/entityService.ts +7 -0
- package/test/auth-services.test.ts +7 -150
- package/test/doctor.test.ts +6 -2
- package/dist/server-postgresql/src/schema/default-collections.d.ts +0 -2
- package/dist/types/src/users/roles.d.ts +0 -14
- package/src/schema/default-collections.ts +0 -69
package/dist/index.es.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Pool, Client } from "pg";
|
|
2
2
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
3
3
|
import { sql, inArray, eq, and, or, ilike, asc, desc, gt, lt, getTableName as getTableName$1, count, relations, isTable } from "drizzle-orm";
|
|
4
|
-
import { PgVarchar, PgText, PgChar, pgSchema, pgTable, timestamp, jsonb, boolean, varchar, uuid,
|
|
4
|
+
import { PgVarchar, PgText, PgChar, pgSchema, pgTable, timestamp, jsonb, text, boolean, varchar, uuid, unique, getTableConfig } from "drizzle-orm/pg-core";
|
|
5
5
|
import { createHash, randomUUID } from "crypto";
|
|
6
6
|
import * as fs from "fs";
|
|
7
7
|
import { promises } from "fs";
|
|
@@ -2719,11 +2719,17 @@ const defaultUsersCollection = {
|
|
|
2719
2719
|
schema: "rebase",
|
|
2720
2720
|
icon: "Users",
|
|
2721
2721
|
group: "Settings",
|
|
2722
|
+
openEntityMode: "dialog",
|
|
2723
|
+
disableDefaultActions: ["copy"],
|
|
2724
|
+
sort: ["createdAt", "desc"],
|
|
2722
2725
|
properties: {
|
|
2723
2726
|
id: {
|
|
2724
2727
|
name: "ID",
|
|
2725
2728
|
type: "string",
|
|
2726
|
-
isId: "uuid"
|
|
2729
|
+
isId: "uuid",
|
|
2730
|
+
ui: {
|
|
2731
|
+
readOnly: true
|
|
2732
|
+
}
|
|
2727
2733
|
},
|
|
2728
2734
|
email: {
|
|
2729
2735
|
name: "Email",
|
|
@@ -2733,38 +2739,77 @@ const defaultUsersCollection = {
|
|
|
2733
2739
|
unique: true
|
|
2734
2740
|
}
|
|
2735
2741
|
},
|
|
2736
|
-
|
|
2737
|
-
name: "
|
|
2742
|
+
displayName: {
|
|
2743
|
+
name: "Name",
|
|
2738
2744
|
type: "string",
|
|
2739
|
-
|
|
2740
|
-
|
|
2745
|
+
columnName: "display_name",
|
|
2746
|
+
validation: {
|
|
2747
|
+
required: true
|
|
2741
2748
|
}
|
|
2742
2749
|
},
|
|
2743
|
-
|
|
2744
|
-
name: "Display Name",
|
|
2745
|
-
type: "string"
|
|
2746
|
-
},
|
|
2747
|
-
photo_url: {
|
|
2750
|
+
photoURL: {
|
|
2748
2751
|
name: "Photo URL",
|
|
2749
|
-
type: "string"
|
|
2752
|
+
type: "string",
|
|
2753
|
+
columnName: "photo_url",
|
|
2754
|
+
url: "image"
|
|
2750
2755
|
},
|
|
2751
|
-
|
|
2756
|
+
roles: {
|
|
2757
|
+
name: "Roles",
|
|
2758
|
+
type: "array",
|
|
2759
|
+
columnType: "text[]",
|
|
2760
|
+
of: {
|
|
2761
|
+
name: "Role",
|
|
2762
|
+
type: "string",
|
|
2763
|
+
enum: {
|
|
2764
|
+
admin: "Admin",
|
|
2765
|
+
editor: "Editor",
|
|
2766
|
+
viewer: "Viewer"
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
},
|
|
2770
|
+
passwordHash: {
|
|
2771
|
+
name: "Password Hash",
|
|
2772
|
+
type: "string",
|
|
2773
|
+
columnName: "password_hash",
|
|
2774
|
+
ui: {
|
|
2775
|
+
hideFromCollection: true,
|
|
2776
|
+
disabled: {
|
|
2777
|
+
hidden: true
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
},
|
|
2781
|
+
emailVerified: {
|
|
2752
2782
|
name: "Email Verified",
|
|
2753
2783
|
type: "boolean",
|
|
2754
|
-
|
|
2784
|
+
columnName: "email_verified",
|
|
2785
|
+
defaultValue: false,
|
|
2786
|
+
ui: {
|
|
2787
|
+
hideFromCollection: true,
|
|
2788
|
+
disabled: {
|
|
2789
|
+
hidden: true
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2755
2792
|
},
|
|
2756
|
-
|
|
2793
|
+
emailVerificationToken: {
|
|
2757
2794
|
name: "Email Verification Token",
|
|
2758
2795
|
type: "string",
|
|
2796
|
+
columnName: "email_verification_token",
|
|
2759
2797
|
ui: {
|
|
2760
|
-
hideFromCollection: true
|
|
2798
|
+
hideFromCollection: true,
|
|
2799
|
+
disabled: {
|
|
2800
|
+
hidden: true
|
|
2801
|
+
}
|
|
2761
2802
|
}
|
|
2762
2803
|
},
|
|
2763
|
-
|
|
2804
|
+
emailVerificationSentAt: {
|
|
2764
2805
|
name: "Email Verification Sent At",
|
|
2765
2806
|
type: "date",
|
|
2807
|
+
columnName: "email_verification_sent_at",
|
|
2766
2808
|
ui: {
|
|
2767
|
-
hideFromCollection: true
|
|
2809
|
+
hideFromCollection: true,
|
|
2810
|
+
disabled: {
|
|
2811
|
+
hidden: true
|
|
2812
|
+
}
|
|
2768
2813
|
}
|
|
2769
2814
|
},
|
|
2770
2815
|
metadata: {
|
|
@@ -2772,28 +2817,35 @@ const defaultUsersCollection = {
|
|
|
2772
2817
|
type: "map",
|
|
2773
2818
|
defaultValue: {},
|
|
2774
2819
|
ui: {
|
|
2775
|
-
hideFromCollection: true
|
|
2820
|
+
hideFromCollection: true,
|
|
2821
|
+
disabled: {
|
|
2822
|
+
hidden: true
|
|
2823
|
+
}
|
|
2776
2824
|
}
|
|
2777
2825
|
},
|
|
2778
|
-
|
|
2826
|
+
createdAt: {
|
|
2779
2827
|
name: "Created At",
|
|
2780
2828
|
type: "date",
|
|
2781
|
-
|
|
2829
|
+
columnName: "created_at",
|
|
2782
2830
|
ui: {
|
|
2783
|
-
readOnly: true
|
|
2784
|
-
hideFromCollection: true
|
|
2831
|
+
readOnly: true
|
|
2785
2832
|
}
|
|
2786
2833
|
},
|
|
2787
|
-
|
|
2834
|
+
updatedAt: {
|
|
2788
2835
|
name: "Updated At",
|
|
2789
2836
|
type: "date",
|
|
2837
|
+
columnName: "updated_at",
|
|
2790
2838
|
autoValue: "on_update",
|
|
2791
2839
|
ui: {
|
|
2792
|
-
|
|
2793
|
-
|
|
2840
|
+
hideFromCollection: true,
|
|
2841
|
+
disabled: {
|
|
2842
|
+
hidden: true
|
|
2843
|
+
}
|
|
2794
2844
|
}
|
|
2795
2845
|
}
|
|
2796
|
-
}
|
|
2846
|
+
},
|
|
2847
|
+
listProperties: ["displayName", "email", "roles", "createdAt"],
|
|
2848
|
+
propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
|
|
2797
2849
|
};
|
|
2798
2850
|
function mapOperator(op) {
|
|
2799
2851
|
switch (op) {
|
|
@@ -3041,6 +3093,9 @@ function createDriverAccessor(driver, slug) {
|
|
|
3041
3093
|
}
|
|
3042
3094
|
});
|
|
3043
3095
|
},
|
|
3096
|
+
deleteAll: driver.deleteAll ? async () => {
|
|
3097
|
+
return driver.deleteAll(slug);
|
|
3098
|
+
} : void 0,
|
|
3044
3099
|
count: driver.countEntities ? async (params) => {
|
|
3045
3100
|
return driver.countEntities({
|
|
3046
3101
|
path: slug,
|
|
@@ -6305,6 +6360,14 @@ class EntityPersistService {
|
|
|
6305
6360
|
const parsedId = parsedIdObj[idInfo.fieldName];
|
|
6306
6361
|
await this.db.delete(table).where(eq(idField, parsedId));
|
|
6307
6362
|
}
|
|
6363
|
+
/**
|
|
6364
|
+
* Delete all entities from a collection
|
|
6365
|
+
*/
|
|
6366
|
+
async deleteAll(collectionPath, _databaseId) {
|
|
6367
|
+
const collection = getCollectionByPath(collectionPath, this.registry);
|
|
6368
|
+
const table = getTableForCollection(collection, this.registry);
|
|
6369
|
+
await this.db.delete(table);
|
|
6370
|
+
}
|
|
6308
6371
|
/**
|
|
6309
6372
|
* Save an entity (create or update)
|
|
6310
6373
|
*/
|
|
@@ -6636,6 +6699,12 @@ class EntityService {
|
|
|
6636
6699
|
async deleteEntity(collectionPath, entityId, databaseId) {
|
|
6637
6700
|
return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
|
|
6638
6701
|
}
|
|
6702
|
+
/**
|
|
6703
|
+
* Delete all entities from a collection
|
|
6704
|
+
*/
|
|
6705
|
+
async deleteAll(collectionPath, databaseId) {
|
|
6706
|
+
return this.persistService.deleteAll(collectionPath, databaseId);
|
|
6707
|
+
}
|
|
6639
6708
|
/**
|
|
6640
6709
|
* Execute raw SQL
|
|
6641
6710
|
*/
|
|
@@ -7336,6 +7405,10 @@ class PostgresBackendDriver {
|
|
|
7336
7405
|
await this.realtimeService.notifyEntityUpdate(entity.path, entity.id.toString(), null, entity.databaseId || resolvedCollection?.databaseId);
|
|
7337
7406
|
}
|
|
7338
7407
|
}
|
|
7408
|
+
async deleteAll(path2) {
|
|
7409
|
+
await this.entityService.deleteAll(path2);
|
|
7410
|
+
await this.realtimeService.notifyEntityUpdate(path2, "*", null);
|
|
7411
|
+
}
|
|
7339
7412
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7340
7413
|
return this.entityService.checkUniqueField(path2, name, value, entityId, collection?.databaseId);
|
|
7341
7414
|
}
|
|
@@ -7605,11 +7678,11 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7605
7678
|
console.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object:", this.user);
|
|
7606
7679
|
userId = "anonymous";
|
|
7607
7680
|
}
|
|
7608
|
-
const
|
|
7681
|
+
const userRoles = this.user?.roles ?? [];
|
|
7609
7682
|
if (!this.user?.roles) {
|
|
7610
7683
|
console.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object:", this.user);
|
|
7611
7684
|
}
|
|
7612
|
-
const normalizedRoles =
|
|
7685
|
+
const normalizedRoles = userRoles.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
|
|
7613
7686
|
const rolesString = normalizedRoles.join(",");
|
|
7614
7687
|
await tx.execute(sql`
|
|
7615
7688
|
SELECT
|
|
@@ -7617,7 +7690,7 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7617
7690
|
set_config('app.user_roles', ${rolesString}, true),
|
|
7618
7691
|
set_config('app.jwt', ${JSON.stringify({
|
|
7619
7692
|
sub: userId,
|
|
7620
|
-
roles:
|
|
7693
|
+
roles: userRoles
|
|
7621
7694
|
})}, true)
|
|
7622
7695
|
`);
|
|
7623
7696
|
const txEntityService = new EntityService(tx, this.delegate.registry);
|
|
@@ -7672,6 +7745,9 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7672
7745
|
async deleteEntity(props) {
|
|
7673
7746
|
return this.withTransaction((delegate) => delegate.deleteEntity(props));
|
|
7674
7747
|
}
|
|
7748
|
+
async deleteAll(path2) {
|
|
7749
|
+
return this.delegate.deleteAll(path2);
|
|
7750
|
+
}
|
|
7675
7751
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7676
7752
|
return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
|
|
7677
7753
|
}
|
|
@@ -7751,11 +7827,10 @@ class DatabasePoolManager {
|
|
|
7751
7827
|
this.pools.clear();
|
|
7752
7828
|
}
|
|
7753
7829
|
}
|
|
7754
|
-
function createAuthSchema(
|
|
7755
|
-
const rolesSchema = rolesSchemaName === "public" ? null : pgSchema(rolesSchemaName);
|
|
7830
|
+
function createAuthSchema(usersSchemaName = "rebase") {
|
|
7756
7831
|
const usersSchema2 = usersSchemaName === "public" ? null : pgSchema(usersSchemaName);
|
|
7757
|
-
const
|
|
7758
|
-
const usersTableCreator =
|
|
7832
|
+
const tableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgTable;
|
|
7833
|
+
const usersTableCreator = tableCreator;
|
|
7759
7834
|
const users2 = usersTableCreator("users", {
|
|
7760
7835
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7761
7836
|
email: varchar("email", {
|
|
@@ -7777,37 +7852,12 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7777
7852
|
}),
|
|
7778
7853
|
emailVerificationSentAt: timestamp("email_verification_sent_at"),
|
|
7779
7854
|
isAnonymous: boolean("is_anonymous").default(false).notNull(),
|
|
7855
|
+
roles: text("roles").array().default([]).notNull(),
|
|
7780
7856
|
metadata: jsonb("metadata").$type().default({}).notNull(),
|
|
7781
7857
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
7782
7858
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7783
7859
|
});
|
|
7784
|
-
const
|
|
7785
|
-
id: varchar("id", {
|
|
7786
|
-
length: 50
|
|
7787
|
-
}).primaryKey(),
|
|
7788
|
-
// 'admin', 'editor', 'viewer'
|
|
7789
|
-
name: varchar("name", {
|
|
7790
|
-
length: 100
|
|
7791
|
-
}).notNull(),
|
|
7792
|
-
isAdmin: boolean("is_admin").default(false).notNull(),
|
|
7793
|
-
defaultPermissions: jsonb("default_permissions").$type(),
|
|
7794
|
-
collectionPermissions: jsonb("collection_permissions").$type()
|
|
7795
|
-
});
|
|
7796
|
-
const userRoles2 = rolesTableCreator("user_roles", {
|
|
7797
|
-
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7798
|
-
onDelete: "cascade"
|
|
7799
|
-
}),
|
|
7800
|
-
roleId: varchar("role_id", {
|
|
7801
|
-
length: 50
|
|
7802
|
-
}).notNull().references(() => roles2.id, {
|
|
7803
|
-
onDelete: "cascade"
|
|
7804
|
-
})
|
|
7805
|
-
}, (table) => ({
|
|
7806
|
-
pk: primaryKey({
|
|
7807
|
-
columns: [table.userId, table.roleId]
|
|
7808
|
-
})
|
|
7809
|
-
}));
|
|
7810
|
-
const refreshTokens2 = rolesTableCreator("refresh_tokens", {
|
|
7860
|
+
const refreshTokens2 = tableCreator("refresh_tokens", {
|
|
7811
7861
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7812
7862
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7813
7863
|
onDelete: "cascade"
|
|
@@ -7826,7 +7876,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7826
7876
|
}, (table) => ({
|
|
7827
7877
|
uniqueDeviceSession: unique("unique_device_session").on(table.userId, table.userAgent, table.ipAddress)
|
|
7828
7878
|
}));
|
|
7829
|
-
const passwordResetTokens2 =
|
|
7879
|
+
const passwordResetTokens2 = tableCreator("password_reset_tokens", {
|
|
7830
7880
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7831
7881
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7832
7882
|
onDelete: "cascade"
|
|
@@ -7838,14 +7888,14 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7838
7888
|
usedAt: timestamp("used_at"),
|
|
7839
7889
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7840
7890
|
});
|
|
7841
|
-
const appConfig2 =
|
|
7891
|
+
const appConfig2 = tableCreator("app_config", {
|
|
7842
7892
|
key: varchar("key", {
|
|
7843
7893
|
length: 100
|
|
7844
7894
|
}).primaryKey(),
|
|
7845
7895
|
value: jsonb("value").notNull(),
|
|
7846
7896
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7847
7897
|
});
|
|
7848
|
-
const userIdentities2 =
|
|
7898
|
+
const userIdentities2 = tableCreator("user_identities", {
|
|
7849
7899
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7850
7900
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7851
7901
|
onDelete: "cascade"
|
|
@@ -7863,7 +7913,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7863
7913
|
}, (table) => ({
|
|
7864
7914
|
uniqueProviderId: unique("unique_provider_id").on(table.provider, table.providerId)
|
|
7865
7915
|
}));
|
|
7866
|
-
const mfaFactors2 =
|
|
7916
|
+
const mfaFactors2 = tableCreator("mfa_factors", {
|
|
7867
7917
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7868
7918
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7869
7919
|
onDelete: "cascade"
|
|
@@ -7882,7 +7932,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7882
7932
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
7883
7933
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7884
7934
|
});
|
|
7885
|
-
const mfaChallenges2 =
|
|
7935
|
+
const mfaChallenges2 = tableCreator("mfa_challenges", {
|
|
7886
7936
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7887
7937
|
factorId: uuid("factor_id").notNull().references(() => mfaFactors2.id, {
|
|
7888
7938
|
onDelete: "cascade"
|
|
@@ -7894,7 +7944,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7894
7944
|
}),
|
|
7895
7945
|
expiresAt: timestamp("expires_at").notNull()
|
|
7896
7946
|
});
|
|
7897
|
-
const recoveryCodes2 =
|
|
7947
|
+
const recoveryCodes2 = tableCreator("recovery_codes", {
|
|
7898
7948
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7899
7949
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7900
7950
|
onDelete: "cascade"
|
|
@@ -7906,11 +7956,8 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7906
7956
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7907
7957
|
});
|
|
7908
7958
|
return {
|
|
7909
|
-
rolesSchema,
|
|
7910
7959
|
usersSchema: usersSchema2,
|
|
7911
7960
|
users: users2,
|
|
7912
|
-
roles: roles2,
|
|
7913
|
-
userRoles: userRoles2,
|
|
7914
7961
|
refreshTokens: refreshTokens2,
|
|
7915
7962
|
passwordResetTokens: passwordResetTokens2,
|
|
7916
7963
|
appConfig: appConfig2,
|
|
@@ -7920,12 +7967,9 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7920
7967
|
recoveryCodes: recoveryCodes2
|
|
7921
7968
|
};
|
|
7922
7969
|
}
|
|
7923
|
-
const defaultAuthSchema = createAuthSchema("rebase"
|
|
7924
|
-
const rebaseSchema = defaultAuthSchema.rolesSchema;
|
|
7970
|
+
const defaultAuthSchema = createAuthSchema("rebase");
|
|
7925
7971
|
const usersSchema = defaultAuthSchema.usersSchema;
|
|
7926
7972
|
const users = defaultAuthSchema.users;
|
|
7927
|
-
const roles = defaultAuthSchema.roles;
|
|
7928
|
-
const userRoles = defaultAuthSchema.userRoles;
|
|
7929
7973
|
const refreshTokens = defaultAuthSchema.refreshTokens;
|
|
7930
7974
|
const passwordResetTokens = defaultAuthSchema.passwordResetTokens;
|
|
7931
7975
|
const appConfig = defaultAuthSchema.appConfig;
|
|
@@ -7936,30 +7980,12 @@ const recoveryCodes = defaultAuthSchema.recoveryCodes;
|
|
|
7936
7980
|
const usersRelations = relations(users, ({
|
|
7937
7981
|
many
|
|
7938
7982
|
}) => ({
|
|
7939
|
-
userRoles: many(userRoles),
|
|
7940
7983
|
refreshTokens: many(refreshTokens),
|
|
7941
7984
|
passwordResetTokens: many(passwordResetTokens),
|
|
7942
7985
|
userIdentities: many(userIdentities),
|
|
7943
7986
|
mfaFactors: many(mfaFactors),
|
|
7944
7987
|
recoveryCodes: many(recoveryCodes)
|
|
7945
7988
|
}));
|
|
7946
|
-
const rolesRelations = relations(roles, ({
|
|
7947
|
-
many
|
|
7948
|
-
}) => ({
|
|
7949
|
-
userRoles: many(userRoles)
|
|
7950
|
-
}));
|
|
7951
|
-
const userRolesRelations = relations(userRoles, ({
|
|
7952
|
-
one
|
|
7953
|
-
}) => ({
|
|
7954
|
-
user: one(users, {
|
|
7955
|
-
fields: [userRoles.userId],
|
|
7956
|
-
references: [users.id]
|
|
7957
|
-
}),
|
|
7958
|
-
role: one(roles, {
|
|
7959
|
-
fields: [userRoles.roleId],
|
|
7960
|
-
references: [roles.id]
|
|
7961
|
-
})
|
|
7962
|
-
}));
|
|
7963
7989
|
const refreshTokensRelations = relations(refreshTokens, ({
|
|
7964
7990
|
one
|
|
7965
7991
|
}) => ({
|
|
@@ -8066,6 +8092,8 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
8066
8092
|
columnDefinition = `${enumName}("${colName}")`;
|
|
8067
8093
|
} else if ("isId" in stringProp && stringProp.isId === "uuid") {
|
|
8068
8094
|
columnDefinition = `uuid("${colName}")`;
|
|
8095
|
+
} else if (stringProp.columnType === "uuid") {
|
|
8096
|
+
columnDefinition = `uuid("${colName}")`;
|
|
8069
8097
|
} else if (stringProp.columnType === "text") {
|
|
8070
8098
|
columnDefinition = `text("${colName}")`;
|
|
8071
8099
|
} else if (stringProp.columnType === "char") {
|
|
@@ -8133,11 +8161,38 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
8133
8161
|
}
|
|
8134
8162
|
break;
|
|
8135
8163
|
}
|
|
8136
|
-
case "map":
|
|
8164
|
+
case "map": {
|
|
8165
|
+
const mapProp = prop;
|
|
8166
|
+
if (mapProp.columnType === "json") {
|
|
8167
|
+
columnDefinition = `json("${colName}")`;
|
|
8168
|
+
} else {
|
|
8169
|
+
columnDefinition = `jsonb("${colName}")`;
|
|
8170
|
+
}
|
|
8171
|
+
break;
|
|
8172
|
+
}
|
|
8137
8173
|
case "array": {
|
|
8138
|
-
const
|
|
8139
|
-
|
|
8174
|
+
const arrayProp = prop;
|
|
8175
|
+
let colType = arrayProp.columnType;
|
|
8176
|
+
if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {
|
|
8177
|
+
const ofProp = arrayProp.of;
|
|
8178
|
+
if (ofProp.type === "string") {
|
|
8179
|
+
colType = "text[]";
|
|
8180
|
+
} else if (ofProp.type === "number") {
|
|
8181
|
+
colType = ofProp.validation?.integer ? "integer[]" : "numeric[]";
|
|
8182
|
+
} else if (ofProp.type === "boolean") {
|
|
8183
|
+
colType = "boolean[]";
|
|
8184
|
+
}
|
|
8185
|
+
}
|
|
8186
|
+
if (colType === "json") {
|
|
8140
8187
|
columnDefinition = `json("${colName}")`;
|
|
8188
|
+
} else if (colType === "text[]") {
|
|
8189
|
+
columnDefinition = `text("${colName}").array()`;
|
|
8190
|
+
} else if (colType === "integer[]") {
|
|
8191
|
+
columnDefinition = `integer("${colName}").array()`;
|
|
8192
|
+
} else if (colType === "boolean[]") {
|
|
8193
|
+
columnDefinition = `boolean("${colName}").array()`;
|
|
8194
|
+
} else if (colType === "numeric[]") {
|
|
8195
|
+
columnDefinition = `numeric("${colName}").array()`;
|
|
8141
8196
|
} else {
|
|
8142
8197
|
columnDefinition = `jsonb("${colName}")`;
|
|
8143
8198
|
}
|
|
@@ -8221,8 +8276,8 @@ const resolveRawSql = (expression) => {
|
|
|
8221
8276
|
const resolved = expression.replace(/\{(\w+)\}/g, (_, col) => col);
|
|
8222
8277
|
return `sql\`${resolved}\``;
|
|
8223
8278
|
};
|
|
8224
|
-
const wrapWithRoleCheck = (clause,
|
|
8225
|
-
const rolesArrayString = `ARRAY[${
|
|
8279
|
+
const wrapWithRoleCheck = (clause, roles) => {
|
|
8280
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8226
8281
|
const roleCondition = `string_to_array(auth.roles(), ',') @> ${rolesArrayString}`;
|
|
8227
8282
|
return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
|
|
8228
8283
|
};
|
|
@@ -8275,22 +8330,22 @@ const generatePolicyCode = (collection, rule, index) => {
|
|
|
8275
8330
|
};
|
|
8276
8331
|
const generateSinglePolicyCode = (collection, rule, operation, policyName) => {
|
|
8277
8332
|
const mode = rule.mode ?? "permissive";
|
|
8278
|
-
const
|
|
8333
|
+
const roles = rule.roles ? [...rule.roles].sort() : void 0;
|
|
8279
8334
|
const needsUsing = operation !== "insert";
|
|
8280
8335
|
const needsWithCheck = operation !== "select" && operation !== "delete";
|
|
8281
8336
|
let usingClause = needsUsing ? buildUsingClause(rule, collection) : null;
|
|
8282
8337
|
let withCheckClause = needsWithCheck ? buildWithCheckClause(rule, collection) : null;
|
|
8283
|
-
if (
|
|
8338
|
+
if (roles && roles.length > 0) {
|
|
8284
8339
|
if (usingClause) {
|
|
8285
|
-
usingClause = wrapWithRoleCheck(usingClause,
|
|
8340
|
+
usingClause = wrapWithRoleCheck(usingClause, roles);
|
|
8286
8341
|
} else if (needsUsing) {
|
|
8287
|
-
const rolesArrayString = `ARRAY[${
|
|
8342
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8288
8343
|
usingClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8289
8344
|
}
|
|
8290
8345
|
if (withCheckClause) {
|
|
8291
|
-
withCheckClause = wrapWithRoleCheck(withCheckClause,
|
|
8346
|
+
withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
|
|
8292
8347
|
} else if (needsWithCheck) {
|
|
8293
|
-
const rolesArrayString = `ARRAY[${
|
|
8348
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8294
8349
|
withCheckClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8295
8350
|
}
|
|
8296
8351
|
}
|
|
@@ -8613,7 +8668,7 @@ ${tableRelations.join(",\n")}
|
|
|
8613
8668
|
schemaContent += tablesExport + enumsExport + relationsExport;
|
|
8614
8669
|
return schemaContent;
|
|
8615
8670
|
};
|
|
8616
|
-
const formatTerminalText = (
|
|
8671
|
+
const formatTerminalText = (text2, options = {}) => {
|
|
8617
8672
|
let codes = "";
|
|
8618
8673
|
if (options.bold) codes += "\x1B[1m";
|
|
8619
8674
|
if (options.backgroundColor) {
|
|
@@ -8640,7 +8695,7 @@ const formatTerminalText = (text, options = {}) => {
|
|
|
8640
8695
|
};
|
|
8641
8696
|
codes += textColors[options.textColor];
|
|
8642
8697
|
}
|
|
8643
|
-
return `${codes}${
|
|
8698
|
+
return `${codes}${text2}\x1B[0m`;
|
|
8644
8699
|
};
|
|
8645
8700
|
const runGeneration = async (collectionsFilePath, outputPath) => {
|
|
8646
8701
|
try {
|
|
@@ -10024,15 +10079,15 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
|
|
|
10024
10079
|
wsDebug("👤 [WebSocket Server] Processing FETCH_ROLES request");
|
|
10025
10080
|
const delegate = await getScopedDelegate();
|
|
10026
10081
|
const admin = delegate.admin;
|
|
10027
|
-
let
|
|
10082
|
+
let roles = [];
|
|
10028
10083
|
if (isSQLAdmin(admin) && admin.fetchAvailableRoles) {
|
|
10029
|
-
|
|
10084
|
+
roles = await admin.fetchAvailableRoles();
|
|
10030
10085
|
}
|
|
10031
|
-
wsDebug(`👤 [WebSocket Server] Fetched ${
|
|
10086
|
+
wsDebug(`👤 [WebSocket Server] Fetched ${roles.length} roles.`);
|
|
10032
10087
|
const response = {
|
|
10033
10088
|
type: "FETCH_ROLES_SUCCESS",
|
|
10034
10089
|
payload: {
|
|
10035
|
-
roles
|
|
10090
|
+
roles
|
|
10036
10091
|
},
|
|
10037
10092
|
requestId
|
|
10038
10093
|
};
|
|
@@ -10286,37 +10341,6 @@ class PostgresCollectionRegistry extends CollectionRegistry {
|
|
|
10286
10341
|
return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
|
|
10287
10342
|
}
|
|
10288
10343
|
}
|
|
10289
|
-
const DEFAULT_ROLES = [{
|
|
10290
|
-
id: "admin",
|
|
10291
|
-
name: "Admin",
|
|
10292
|
-
is_admin: true,
|
|
10293
|
-
default_permissions: {
|
|
10294
|
-
read: true,
|
|
10295
|
-
create: true,
|
|
10296
|
-
edit: true,
|
|
10297
|
-
delete: true
|
|
10298
|
-
}
|
|
10299
|
-
}, {
|
|
10300
|
-
id: "editor",
|
|
10301
|
-
name: "Editor",
|
|
10302
|
-
is_admin: false,
|
|
10303
|
-
default_permissions: {
|
|
10304
|
-
read: true,
|
|
10305
|
-
create: true,
|
|
10306
|
-
edit: true,
|
|
10307
|
-
delete: true
|
|
10308
|
-
}
|
|
10309
|
-
}, {
|
|
10310
|
-
id: "viewer",
|
|
10311
|
-
name: "Viewer",
|
|
10312
|
-
is_admin: false,
|
|
10313
|
-
default_permissions: {
|
|
10314
|
-
read: true,
|
|
10315
|
-
create: false,
|
|
10316
|
-
edit: false,
|
|
10317
|
-
delete: false
|
|
10318
|
-
}
|
|
10319
|
-
}];
|
|
10320
10344
|
async function ensureAuthTablesExist(db, registry) {
|
|
10321
10345
|
logger.info("🔍 Checking auth tables...");
|
|
10322
10346
|
try {
|
|
@@ -10345,26 +10369,15 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10345
10369
|
}
|
|
10346
10370
|
}
|
|
10347
10371
|
}
|
|
10348
|
-
let rolesSchema = "rebase";
|
|
10349
|
-
if (registry) {
|
|
10350
|
-
const rolesTable = registry.getTable("roles");
|
|
10351
|
-
if (rolesTable) {
|
|
10352
|
-
rolesSchema = getTableConfig(rolesTable).schema || "public";
|
|
10353
|
-
}
|
|
10354
|
-
}
|
|
10355
10372
|
if (usersSchema2 !== "public") {
|
|
10356
10373
|
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(usersSchema2)}`);
|
|
10357
10374
|
}
|
|
10358
|
-
if (rolesSchema !== "public" && rolesSchema !== usersSchema2) {
|
|
10359
|
-
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(rolesSchema)}`);
|
|
10360
|
-
}
|
|
10361
10375
|
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS rebase`);
|
|
10362
|
-
const
|
|
10363
|
-
const
|
|
10364
|
-
const
|
|
10365
|
-
const
|
|
10366
|
-
const
|
|
10367
|
-
const appConfigTableName = `"${rolesSchema}"."app_config"`;
|
|
10376
|
+
const authSchema = usersSchema2 === "public" ? "rebase" : usersSchema2;
|
|
10377
|
+
const userIdentitiesTable = `"${authSchema}"."user_identities"`;
|
|
10378
|
+
const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
|
|
10379
|
+
const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
|
|
10380
|
+
const appConfigTableName = `"${authSchema}"."app_config"`;
|
|
10368
10381
|
await db.execute(sql`
|
|
10369
10382
|
CREATE TABLE IF NOT EXISTS ${sql.raw(userIdentitiesTable)} (
|
|
10370
10383
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10381,27 +10394,6 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10381
10394
|
CREATE INDEX IF NOT EXISTS idx_user_identities_user
|
|
10382
10395
|
ON ${sql.raw(userIdentitiesTable)}(user_id)
|
|
10383
10396
|
`);
|
|
10384
|
-
await db.execute(sql`
|
|
10385
|
-
CREATE TABLE IF NOT EXISTS ${sql.raw(rolesTableName)} (
|
|
10386
|
-
id TEXT PRIMARY KEY,
|
|
10387
|
-
name TEXT NOT NULL,
|
|
10388
|
-
is_admin BOOLEAN DEFAULT FALSE,
|
|
10389
|
-
default_permissions JSONB,
|
|
10390
|
-
collection_permissions JSONB,
|
|
10391
|
-
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
10392
|
-
)
|
|
10393
|
-
`);
|
|
10394
|
-
await db.execute(sql`
|
|
10395
|
-
CREATE TABLE IF NOT EXISTS ${sql.raw(userRolesTableName)} (
|
|
10396
|
-
user_id ${sql.raw(userIdType)} NOT NULL REFERENCES ${sql.raw(usersTableName)}(id) ON DELETE CASCADE,
|
|
10397
|
-
role_id TEXT NOT NULL REFERENCES ${sql.raw(rolesTableName)}(id) ON DELETE CASCADE,
|
|
10398
|
-
PRIMARY KEY (user_id, role_id)
|
|
10399
|
-
)
|
|
10400
|
-
`);
|
|
10401
|
-
await db.execute(sql`
|
|
10402
|
-
CREATE INDEX IF NOT EXISTS idx_user_roles_user
|
|
10403
|
-
ON ${sql.raw(userRolesTableName)}(user_id)
|
|
10404
|
-
`);
|
|
10405
10397
|
await db.execute(sql`
|
|
10406
10398
|
CREATE TABLE IF NOT EXISTS ${sql.raw(refreshTokensTableName)} (
|
|
10407
10399
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10469,14 +10461,43 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10469
10461
|
$$ LANGUAGE sql STABLE
|
|
10470
10462
|
`);
|
|
10471
10463
|
});
|
|
10472
|
-
await seedDefaultRoles(db, rolesTableName);
|
|
10473
10464
|
await db.execute(sql`
|
|
10474
10465
|
ALTER TABLE ${sql.raw(usersTableName)}
|
|
10475
10466
|
ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
|
|
10476
10467
|
`);
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10468
|
+
await db.execute(sql`
|
|
10469
|
+
ALTER TABLE ${sql.raw(usersTableName)}
|
|
10470
|
+
ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
|
|
10471
|
+
`);
|
|
10472
|
+
try {
|
|
10473
|
+
const legacyCheck = await db.execute(sql`
|
|
10474
|
+
SELECT EXISTS (
|
|
10475
|
+
SELECT 1 FROM information_schema.tables
|
|
10476
|
+
WHERE table_schema = 'rebase' AND table_name = 'user_roles'
|
|
10477
|
+
) AS has_user_roles
|
|
10478
|
+
`);
|
|
10479
|
+
const hasLegacyTables = legacyCheck.rows[0].has_user_roles;
|
|
10480
|
+
if (hasLegacyTables) {
|
|
10481
|
+
logger.info("🔄 Migrating roles from legacy user_roles table...");
|
|
10482
|
+
await db.execute(sql`
|
|
10483
|
+
UPDATE ${sql.raw(usersTableName)} u
|
|
10484
|
+
SET roles = COALESCE((
|
|
10485
|
+
SELECT array_agg(ur.role_id)
|
|
10486
|
+
FROM "rebase"."user_roles" ur
|
|
10487
|
+
WHERE ur.user_id = u.id
|
|
10488
|
+
), '{}')
|
|
10489
|
+
WHERE u.roles = '{}' OR u.roles IS NULL
|
|
10490
|
+
`);
|
|
10491
|
+
await db.execute(sql`DROP TABLE IF EXISTS "rebase"."user_roles" CASCADE`);
|
|
10492
|
+
await db.execute(sql`DROP TABLE IF EXISTS "rebase"."roles" CASCADE`);
|
|
10493
|
+
logger.info("✅ Legacy roles tables migrated and dropped");
|
|
10494
|
+
}
|
|
10495
|
+
} catch (migrationError) {
|
|
10496
|
+
logger.warn(`⚠️ Legacy roles migration skipped: ${migrationError instanceof Error ? migrationError.message : String(migrationError)}`);
|
|
10497
|
+
}
|
|
10498
|
+
const mfaFactorsTableName = `"${authSchema}"."mfa_factors"`;
|
|
10499
|
+
const mfaChallengesTableName = `"${authSchema}"."mfa_challenges"`;
|
|
10500
|
+
const recoveryCodesTableName = `"${authSchema}"."recovery_codes"`;
|
|
10480
10501
|
await db.execute(sql`
|
|
10481
10502
|
CREATE TABLE IF NOT EXISTS ${sql.raw(mfaFactorsTableName)} (
|
|
10482
10503
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10528,28 +10549,6 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10528
10549
|
logger.warn("⚠️ Continuing without creating auth tables.");
|
|
10529
10550
|
}
|
|
10530
10551
|
}
|
|
10531
|
-
async function seedDefaultRoles(db, rolesTableName) {
|
|
10532
|
-
const result = await db.execute(sql`SELECT COUNT(*) as count FROM ${sql.raw(rolesTableName)}`);
|
|
10533
|
-
const count2 = parseInt(result.rows[0]?.count || "0", 10);
|
|
10534
|
-
if (count2 > 0) {
|
|
10535
|
-
logger.info(`📋 Found ${count2} existing roles`);
|
|
10536
|
-
return;
|
|
10537
|
-
}
|
|
10538
|
-
logger.info("🌱 Seeding default roles...");
|
|
10539
|
-
for (const role of DEFAULT_ROLES) {
|
|
10540
|
-
await db.execute(sql`
|
|
10541
|
-
INSERT INTO ${sql.raw(rolesTableName)} (id, name, is_admin, default_permissions)
|
|
10542
|
-
VALUES (
|
|
10543
|
-
${role.id},
|
|
10544
|
-
${role.name},
|
|
10545
|
-
${role.is_admin},
|
|
10546
|
-
${JSON.stringify(role.default_permissions)}::jsonb
|
|
10547
|
-
)
|
|
10548
|
-
ON CONFLICT (id) DO NOTHING
|
|
10549
|
-
`);
|
|
10550
|
-
}
|
|
10551
|
-
logger.info("✅ Default roles created: admin, editor, viewer");
|
|
10552
|
-
}
|
|
10553
10552
|
function getColumnKey(table, ...keys2) {
|
|
10554
10553
|
if (!table) return void 0;
|
|
10555
10554
|
for (const key of keys2) {
|
|
@@ -10569,24 +10568,18 @@ function getColumn(table, ...keys2) {
|
|
|
10569
10568
|
class UserService {
|
|
10570
10569
|
constructor(db, tableOrTables) {
|
|
10571
10570
|
this.db = db;
|
|
10572
|
-
if (tableOrTables &&
|
|
10571
|
+
if (tableOrTables && tableOrTables.users) {
|
|
10573
10572
|
const tables = tableOrTables;
|
|
10574
10573
|
this.usersTable = tables.users || users;
|
|
10575
10574
|
this.userIdentitiesTable = tables.userIdentities || userIdentities;
|
|
10576
|
-
this.userRolesTable = tables.userRoles || userRoles;
|
|
10577
|
-
this.rolesTable = tables.roles || roles;
|
|
10578
10575
|
} else {
|
|
10579
10576
|
const table = tableOrTables;
|
|
10580
10577
|
this.usersTable = table || users;
|
|
10581
10578
|
this.userIdentitiesTable = userIdentities;
|
|
10582
|
-
this.userRolesTable = userRoles;
|
|
10583
|
-
this.rolesTable = roles;
|
|
10584
10579
|
}
|
|
10585
10580
|
}
|
|
10586
10581
|
usersTable;
|
|
10587
10582
|
userIdentitiesTable;
|
|
10588
|
-
userRolesTable;
|
|
10589
|
-
rolesTable;
|
|
10590
10583
|
getQualifiedUsersTableName() {
|
|
10591
10584
|
const name = getTableName$1(this.usersTable);
|
|
10592
10585
|
const schema = getTableConfig(this.usersTable).schema || "public";
|
|
@@ -10608,7 +10601,7 @@ class UserService {
|
|
|
10608
10601
|
const metadata = {
|
|
10609
10602
|
...row.metadata || {}
|
|
10610
10603
|
};
|
|
10611
|
-
const knownKeys = /* @__PURE__ */ new Set(["id", "uid", "email", "password_hash", "passwordHash", "display_name", "displayName", "photo_url", "photoUrl", "photoURL", "email_verified", "emailVerified", "email_verification_token", "emailVerificationToken", "email_verification_sent_at", "emailVerificationSentAt", "is_anonymous", "isAnonymous", "created_at", "createdAt", "updated_at", "updatedAt", "metadata"]);
|
|
10604
|
+
const knownKeys = /* @__PURE__ */ new Set(["id", "uid", "email", "password_hash", "passwordHash", "display_name", "displayName", "photo_url", "photoUrl", "photoURL", "email_verified", "emailVerified", "email_verification_token", "emailVerificationToken", "email_verification_sent_at", "emailVerificationSentAt", "is_anonymous", "isAnonymous", "roles", "created_at", "createdAt", "updated_at", "updatedAt", "metadata"]);
|
|
10612
10605
|
for (const [key, val] of Object.entries(row)) {
|
|
10613
10606
|
if (!knownKeys.has(key)) {
|
|
10614
10607
|
const camelKey = camelCase(key);
|
|
@@ -10759,19 +10752,18 @@ class UserService {
|
|
|
10759
10752
|
const displayNameCol = getColumn(this.usersTable, "displayName", "display_name");
|
|
10760
10753
|
const displayNameColumn = displayNameCol ? displayNameCol.name : "display_name";
|
|
10761
10754
|
const idCol = getColumn(this.usersTable, "id");
|
|
10762
|
-
|
|
10755
|
+
idCol ? idCol.name : "id";
|
|
10763
10756
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
10764
|
-
const rolesSchema = getTableConfig(this.userRolesTable).schema || "public";
|
|
10765
10757
|
const conditions = [];
|
|
10766
10758
|
if (roleId) {
|
|
10767
|
-
conditions.push(sql
|
|
10759
|
+
conditions.push(sql`${roleId} = ANY(${sql.raw(usersTableName)}.roles)`);
|
|
10768
10760
|
}
|
|
10769
10761
|
if (search) {
|
|
10770
10762
|
const pattern = `%${search}%`;
|
|
10771
10763
|
conditions.push(sql`(${sql.raw(usersTableName)}.${sql.raw(emailColumn)} ILIKE ${pattern} OR ${sql.raw(usersTableName)}.${sql.raw(displayNameColumn)} ILIKE ${pattern})`);
|
|
10772
10764
|
}
|
|
10773
10765
|
const whereClause = conditions.length > 0 ? sql`WHERE ${sql.join(conditions, sql` AND `)}` : sql``;
|
|
10774
|
-
const orderByClause = roleId ? sql`ORDER BY ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}` : sql`ORDER BY (
|
|
10766
|
+
const orderByClause = roleId ? sql`ORDER BY ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}` : sql`ORDER BY array_length(${sql.raw(usersTableName)}.roles, 1) DESC NULLS LAST, ${sql.raw(usersTableName)}.${sql.raw(orderColumn)} ${direction}`;
|
|
10775
10767
|
const countResult = await this.db.execute(sql`
|
|
10776
10768
|
SELECT count(*)::int as total FROM ${sql.raw(usersTableName)}
|
|
10777
10769
|
${whereClause}
|
|
@@ -10845,54 +10837,57 @@ class UserService {
|
|
|
10845
10837
|
return row ? this.mapRowToUser(row) : null;
|
|
10846
10838
|
}
|
|
10847
10839
|
/**
|
|
10848
|
-
* Get roles for a user from database
|
|
10840
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
10849
10841
|
*/
|
|
10850
10842
|
async getUserRoles(userId) {
|
|
10851
|
-
const
|
|
10843
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10852
10844
|
const result = await this.db.execute(sql`
|
|
10853
|
-
SELECT
|
|
10854
|
-
FROM ${sql.raw(`"${rolesSchema}"."roles"`)} r
|
|
10855
|
-
INNER JOIN ${sql.raw(`"${rolesSchema}"."user_roles"`)} ur ON r.id = ur.role_id
|
|
10856
|
-
WHERE ur.user_id = ${userId}
|
|
10845
|
+
SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10857
10846
|
`);
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10847
|
+
if (result.rows.length === 0) return [];
|
|
10848
|
+
const row = result.rows[0];
|
|
10849
|
+
const roleIds = row.roles ?? [];
|
|
10850
|
+
return roleIds.map((id) => ({
|
|
10851
|
+
id,
|
|
10852
|
+
name: id,
|
|
10853
|
+
isAdmin: id === "admin",
|
|
10854
|
+
defaultPermissions: null,
|
|
10855
|
+
collectionPermissions: null
|
|
10864
10856
|
}));
|
|
10865
10857
|
}
|
|
10866
10858
|
/**
|
|
10867
10859
|
* Get role IDs for a user
|
|
10868
10860
|
*/
|
|
10869
10861
|
async getUserRoleIds(userId) {
|
|
10870
|
-
const
|
|
10871
|
-
|
|
10862
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10863
|
+
const result = await this.db.execute(sql`
|
|
10864
|
+
SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10865
|
+
`);
|
|
10866
|
+
if (result.rows.length === 0) return [];
|
|
10867
|
+
const row = result.rows[0];
|
|
10868
|
+
return row.roles ?? [];
|
|
10872
10869
|
}
|
|
10873
10870
|
/**
|
|
10874
|
-
* Set roles for a user
|
|
10871
|
+
* Set roles for a user (replaces existing roles)
|
|
10875
10872
|
*/
|
|
10876
10873
|
async setUserRoles(userId, roleIds) {
|
|
10877
|
-
const
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
`);
|
|
10885
|
-
}
|
|
10874
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10875
|
+
const rolesArray = `{${roleIds.join(",")}}`;
|
|
10876
|
+
await this.db.execute(sql`
|
|
10877
|
+
UPDATE ${sql.raw(usersTableName)}
|
|
10878
|
+
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
10879
|
+
WHERE id = ${userId}
|
|
10880
|
+
`);
|
|
10886
10881
|
}
|
|
10887
10882
|
/**
|
|
10888
|
-
* Assign a specific role to new user
|
|
10883
|
+
* Assign a specific role to new user (appends if not present)
|
|
10889
10884
|
*/
|
|
10890
10885
|
async assignDefaultRole(userId, roleId) {
|
|
10891
|
-
const
|
|
10886
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10892
10887
|
await this.db.execute(sql`
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10888
|
+
UPDATE ${sql.raw(usersTableName)}
|
|
10889
|
+
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
10890
|
+
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
10896
10891
|
`);
|
|
10897
10892
|
}
|
|
10898
10893
|
/**
|
|
@@ -10901,101 +10896,12 @@ class UserService {
|
|
|
10901
10896
|
async getUserWithRoles(userId) {
|
|
10902
10897
|
const user = await this.getUserById(userId);
|
|
10903
10898
|
if (!user) return null;
|
|
10904
|
-
const
|
|
10899
|
+
const roles = await this.getUserRoles(userId);
|
|
10905
10900
|
return {
|
|
10906
10901
|
user,
|
|
10907
|
-
roles
|
|
10908
|
-
};
|
|
10909
|
-
}
|
|
10910
|
-
}
|
|
10911
|
-
class RoleService {
|
|
10912
|
-
constructor(db, tableOrTables) {
|
|
10913
|
-
this.db = db;
|
|
10914
|
-
if (tableOrTables && (tableOrTables.roles || tableOrTables.users)) {
|
|
10915
|
-
this.rolesTable = tableOrTables.roles || roles;
|
|
10916
|
-
} else {
|
|
10917
|
-
this.rolesTable = tableOrTables || roles;
|
|
10918
|
-
}
|
|
10919
|
-
}
|
|
10920
|
-
rolesTable;
|
|
10921
|
-
getQualifiedRolesTableName() {
|
|
10922
|
-
const name = getTableName$1(this.rolesTable);
|
|
10923
|
-
const schema = getTableConfig(this.rolesTable).schema || "public";
|
|
10924
|
-
return `"${schema}"."${name}"`;
|
|
10925
|
-
}
|
|
10926
|
-
async getRoleById(id) {
|
|
10927
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10928
|
-
const result = await this.db.execute(sql`
|
|
10929
|
-
SELECT id, name, is_admin, default_permissions, collection_permissions
|
|
10930
|
-
FROM ${sql.raw(tableName)}
|
|
10931
|
-
WHERE id = ${id}
|
|
10932
|
-
`);
|
|
10933
|
-
if (result.rows.length === 0) return null;
|
|
10934
|
-
const row = result.rows[0];
|
|
10935
|
-
return {
|
|
10936
|
-
id: row.id,
|
|
10937
|
-
name: row.name,
|
|
10938
|
-
isAdmin: row.is_admin,
|
|
10939
|
-
defaultPermissions: row.default_permissions,
|
|
10940
|
-
collectionPermissions: row.collection_permissions
|
|
10941
|
-
};
|
|
10942
|
-
}
|
|
10943
|
-
async listRoles() {
|
|
10944
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10945
|
-
const result = await this.db.execute(sql`
|
|
10946
|
-
SELECT id, name, is_admin, default_permissions, collection_permissions
|
|
10947
|
-
FROM ${sql.raw(tableName)}
|
|
10948
|
-
ORDER BY name
|
|
10949
|
-
`);
|
|
10950
|
-
return result.rows.map((row) => ({
|
|
10951
|
-
id: row.id,
|
|
10952
|
-
name: row.name,
|
|
10953
|
-
isAdmin: row.is_admin,
|
|
10954
|
-
defaultPermissions: row.default_permissions,
|
|
10955
|
-
collectionPermissions: row.collection_permissions
|
|
10956
|
-
}));
|
|
10957
|
-
}
|
|
10958
|
-
async createRole(data) {
|
|
10959
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10960
|
-
const result = await this.db.execute(sql`
|
|
10961
|
-
INSERT INTO ${sql.raw(tableName)} (id, name, is_admin, default_permissions, collection_permissions)
|
|
10962
|
-
VALUES (
|
|
10963
|
-
${data.id},
|
|
10964
|
-
${data.name},
|
|
10965
|
-
${data.isAdmin ?? false},
|
|
10966
|
-
${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : null}::jsonb,
|
|
10967
|
-
${data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null}::jsonb
|
|
10968
|
-
)
|
|
10969
|
-
RETURNING id, name, is_admin, default_permissions, collection_permissions
|
|
10970
|
-
`);
|
|
10971
|
-
const row = result.rows[0];
|
|
10972
|
-
return {
|
|
10973
|
-
id: row.id,
|
|
10974
|
-
name: row.name,
|
|
10975
|
-
isAdmin: row.is_admin,
|
|
10976
|
-
defaultPermissions: row.default_permissions,
|
|
10977
|
-
collectionPermissions: row.collection_permissions
|
|
10902
|
+
roles
|
|
10978
10903
|
};
|
|
10979
10904
|
}
|
|
10980
|
-
async updateRole(id, data) {
|
|
10981
|
-
const existing = await this.getRoleById(id);
|
|
10982
|
-
if (!existing) return null;
|
|
10983
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10984
|
-
await this.db.execute(sql`
|
|
10985
|
-
UPDATE ${sql.raw(tableName)}
|
|
10986
|
-
SET
|
|
10987
|
-
name = ${data.name ?? existing.name},
|
|
10988
|
-
is_admin = ${data.isAdmin ?? existing.isAdmin},
|
|
10989
|
-
default_permissions = ${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : JSON.stringify(existing.defaultPermissions)}::jsonb,
|
|
10990
|
-
collection_permissions = ${data.collectionPermissions !== void 0 ? data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null : existing.collectionPermissions ? JSON.stringify(existing.collectionPermissions) : null}::jsonb
|
|
10991
|
-
WHERE id = ${id}
|
|
10992
|
-
`);
|
|
10993
|
-
return this.getRoleById(id);
|
|
10994
|
-
}
|
|
10995
|
-
async deleteRole(id) {
|
|
10996
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10997
|
-
await this.db.execute(sql`DELETE FROM ${sql.raw(tableName)} WHERE id = ${id}`);
|
|
10998
|
-
}
|
|
10999
10905
|
}
|
|
11000
10906
|
class RefreshTokenService {
|
|
11001
10907
|
constructor(db, tableOrTables) {
|
|
@@ -11191,11 +11097,9 @@ class PostgresAuthRepository {
|
|
|
11191
11097
|
constructor(db, tableOrTables) {
|
|
11192
11098
|
this.db = db;
|
|
11193
11099
|
this.userService = new UserService(db, tableOrTables);
|
|
11194
|
-
this.roleService = new RoleService(db, tableOrTables);
|
|
11195
11100
|
this.tokenRepository = new PostgresTokenRepository(db, tableOrTables);
|
|
11196
11101
|
}
|
|
11197
11102
|
userService;
|
|
11198
|
-
roleService;
|
|
11199
11103
|
tokenRepository;
|
|
11200
11104
|
// User operations (delegate to UserService)
|
|
11201
11105
|
async createUser(data) {
|
|
@@ -11255,25 +11159,56 @@ class PostgresAuthRepository {
|
|
|
11255
11159
|
async getUserWithRoles(userId) {
|
|
11256
11160
|
return this.userService.getUserWithRoles(userId);
|
|
11257
11161
|
}
|
|
11258
|
-
// Role operations (
|
|
11162
|
+
// Role operations (roles are inline on users, synthesized from string IDs)
|
|
11259
11163
|
async getRoleById(id) {
|
|
11260
|
-
return
|
|
11164
|
+
return {
|
|
11165
|
+
id,
|
|
11166
|
+
name: id,
|
|
11167
|
+
isAdmin: id === "admin",
|
|
11168
|
+
defaultPermissions: null,
|
|
11169
|
+
collectionPermissions: null
|
|
11170
|
+
};
|
|
11261
11171
|
}
|
|
11262
11172
|
async listRoles() {
|
|
11263
|
-
return
|
|
11173
|
+
return [{
|
|
11174
|
+
id: "admin",
|
|
11175
|
+
name: "Admin",
|
|
11176
|
+
isAdmin: true,
|
|
11177
|
+
defaultPermissions: null,
|
|
11178
|
+
collectionPermissions: null
|
|
11179
|
+
}, {
|
|
11180
|
+
id: "editor",
|
|
11181
|
+
name: "Editor",
|
|
11182
|
+
isAdmin: false,
|
|
11183
|
+
defaultPermissions: null,
|
|
11184
|
+
collectionPermissions: null
|
|
11185
|
+
}, {
|
|
11186
|
+
id: "viewer",
|
|
11187
|
+
name: "Viewer",
|
|
11188
|
+
isAdmin: false,
|
|
11189
|
+
defaultPermissions: null,
|
|
11190
|
+
collectionPermissions: null
|
|
11191
|
+
}];
|
|
11192
|
+
}
|
|
11193
|
+
async createRole(_data) {
|
|
11194
|
+
return {
|
|
11195
|
+
id: _data.id,
|
|
11196
|
+
name: _data.name,
|
|
11197
|
+
isAdmin: _data.isAdmin ?? false,
|
|
11198
|
+
defaultPermissions: _data.defaultPermissions ?? null,
|
|
11199
|
+
collectionPermissions: _data.collectionPermissions ?? null
|
|
11200
|
+
};
|
|
11264
11201
|
}
|
|
11265
|
-
async
|
|
11266
|
-
return
|
|
11267
|
-
|
|
11202
|
+
async updateRole(id, data) {
|
|
11203
|
+
return {
|
|
11204
|
+
id,
|
|
11205
|
+
name: data.name ?? id,
|
|
11206
|
+
isAdmin: data.isAdmin ?? id === "admin",
|
|
11268
11207
|
defaultPermissions: data.defaultPermissions ?? null,
|
|
11269
11208
|
collectionPermissions: data.collectionPermissions ?? null
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
async updateRole(id, data) {
|
|
11273
|
-
return this.roleService.updateRole(id, data);
|
|
11209
|
+
};
|
|
11274
11210
|
}
|
|
11275
|
-
async deleteRole(
|
|
11276
|
-
await this.roleService.deleteRole(id);
|
|
11211
|
+
async deleteRole(_id) {
|
|
11277
11212
|
}
|
|
11278
11213
|
// Token operations (delegate to PostgresTokenRepository)
|
|
11279
11214
|
async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
|
|
@@ -11829,28 +11764,19 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11829
11764
|
emailService = createEmailService(authConfig.email);
|
|
11830
11765
|
}
|
|
11831
11766
|
const customUsersTable = registry?.getTable("users");
|
|
11832
|
-
const customRolesTable = registry?.getTable("roles");
|
|
11833
11767
|
let usersSchemaName = "rebase";
|
|
11834
|
-
let rolesSchemaName = "rebase";
|
|
11835
11768
|
if (customUsersTable) {
|
|
11836
11769
|
usersSchemaName = getTableConfig(customUsersTable).schema || "public";
|
|
11837
11770
|
}
|
|
11838
|
-
|
|
11839
|
-
rolesSchemaName = getTableConfig(customRolesTable).schema || "public";
|
|
11840
|
-
}
|
|
11841
|
-
const authTables = createAuthSchema(rolesSchemaName, usersSchemaName);
|
|
11771
|
+
const authTables = createAuthSchema(usersSchemaName);
|
|
11842
11772
|
if (customUsersTable) {
|
|
11843
11773
|
authTables.users = customUsersTable;
|
|
11844
11774
|
}
|
|
11845
|
-
if (customRolesTable) {
|
|
11846
|
-
authTables.roles = customRolesTable;
|
|
11847
|
-
}
|
|
11848
11775
|
const userService = new UserService(db, authTables);
|
|
11849
|
-
const roleService = new RoleService(db, authTables);
|
|
11850
11776
|
const authRepository = new PostgresAuthRepository(db, authTables);
|
|
11851
11777
|
return {
|
|
11852
11778
|
userService,
|
|
11853
|
-
roleService,
|
|
11779
|
+
roleService: userService,
|
|
11854
11780
|
emailService,
|
|
11855
11781
|
authRepository
|
|
11856
11782
|
};
|
|
@@ -11955,17 +11881,12 @@ export {
|
|
|
11955
11881
|
mfaFactorsRelations,
|
|
11956
11882
|
passwordResetTokens,
|
|
11957
11883
|
passwordResetTokensRelations,
|
|
11958
|
-
rebaseSchema,
|
|
11959
11884
|
recoveryCodes,
|
|
11960
11885
|
recoveryCodesRelations,
|
|
11961
11886
|
refreshTokens,
|
|
11962
11887
|
refreshTokensRelations,
|
|
11963
|
-
roles,
|
|
11964
|
-
rolesRelations,
|
|
11965
11888
|
userIdentities,
|
|
11966
11889
|
userIdentitiesRelations,
|
|
11967
|
-
userRoles,
|
|
11968
|
-
userRolesRelations,
|
|
11969
11890
|
users,
|
|
11970
11891
|
usersRelations,
|
|
11971
11892
|
usersSchema
|