@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.umd.js
CHANGED
|
@@ -2727,11 +2727,17 @@
|
|
|
2727
2727
|
schema: "rebase",
|
|
2728
2728
|
icon: "Users",
|
|
2729
2729
|
group: "Settings",
|
|
2730
|
+
openEntityMode: "dialog",
|
|
2731
|
+
disableDefaultActions: ["copy"],
|
|
2732
|
+
sort: ["createdAt", "desc"],
|
|
2730
2733
|
properties: {
|
|
2731
2734
|
id: {
|
|
2732
2735
|
name: "ID",
|
|
2733
2736
|
type: "string",
|
|
2734
|
-
isId: "uuid"
|
|
2737
|
+
isId: "uuid",
|
|
2738
|
+
ui: {
|
|
2739
|
+
readOnly: true
|
|
2740
|
+
}
|
|
2735
2741
|
},
|
|
2736
2742
|
email: {
|
|
2737
2743
|
name: "Email",
|
|
@@ -2741,38 +2747,77 @@
|
|
|
2741
2747
|
unique: true
|
|
2742
2748
|
}
|
|
2743
2749
|
},
|
|
2744
|
-
|
|
2745
|
-
name: "
|
|
2750
|
+
displayName: {
|
|
2751
|
+
name: "Name",
|
|
2746
2752
|
type: "string",
|
|
2747
|
-
|
|
2748
|
-
|
|
2753
|
+
columnName: "display_name",
|
|
2754
|
+
validation: {
|
|
2755
|
+
required: true
|
|
2749
2756
|
}
|
|
2750
2757
|
},
|
|
2751
|
-
|
|
2752
|
-
name: "Display Name",
|
|
2753
|
-
type: "string"
|
|
2754
|
-
},
|
|
2755
|
-
photo_url: {
|
|
2758
|
+
photoURL: {
|
|
2756
2759
|
name: "Photo URL",
|
|
2757
|
-
type: "string"
|
|
2760
|
+
type: "string",
|
|
2761
|
+
columnName: "photo_url",
|
|
2762
|
+
url: "image"
|
|
2758
2763
|
},
|
|
2759
|
-
|
|
2764
|
+
roles: {
|
|
2765
|
+
name: "Roles",
|
|
2766
|
+
type: "array",
|
|
2767
|
+
columnType: "text[]",
|
|
2768
|
+
of: {
|
|
2769
|
+
name: "Role",
|
|
2770
|
+
type: "string",
|
|
2771
|
+
enum: {
|
|
2772
|
+
admin: "Admin",
|
|
2773
|
+
editor: "Editor",
|
|
2774
|
+
viewer: "Viewer"
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
},
|
|
2778
|
+
passwordHash: {
|
|
2779
|
+
name: "Password Hash",
|
|
2780
|
+
type: "string",
|
|
2781
|
+
columnName: "password_hash",
|
|
2782
|
+
ui: {
|
|
2783
|
+
hideFromCollection: true,
|
|
2784
|
+
disabled: {
|
|
2785
|
+
hidden: true
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
},
|
|
2789
|
+
emailVerified: {
|
|
2760
2790
|
name: "Email Verified",
|
|
2761
2791
|
type: "boolean",
|
|
2762
|
-
|
|
2792
|
+
columnName: "email_verified",
|
|
2793
|
+
defaultValue: false,
|
|
2794
|
+
ui: {
|
|
2795
|
+
hideFromCollection: true,
|
|
2796
|
+
disabled: {
|
|
2797
|
+
hidden: true
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2763
2800
|
},
|
|
2764
|
-
|
|
2801
|
+
emailVerificationToken: {
|
|
2765
2802
|
name: "Email Verification Token",
|
|
2766
2803
|
type: "string",
|
|
2804
|
+
columnName: "email_verification_token",
|
|
2767
2805
|
ui: {
|
|
2768
|
-
hideFromCollection: true
|
|
2806
|
+
hideFromCollection: true,
|
|
2807
|
+
disabled: {
|
|
2808
|
+
hidden: true
|
|
2809
|
+
}
|
|
2769
2810
|
}
|
|
2770
2811
|
},
|
|
2771
|
-
|
|
2812
|
+
emailVerificationSentAt: {
|
|
2772
2813
|
name: "Email Verification Sent At",
|
|
2773
2814
|
type: "date",
|
|
2815
|
+
columnName: "email_verification_sent_at",
|
|
2774
2816
|
ui: {
|
|
2775
|
-
hideFromCollection: true
|
|
2817
|
+
hideFromCollection: true,
|
|
2818
|
+
disabled: {
|
|
2819
|
+
hidden: true
|
|
2820
|
+
}
|
|
2776
2821
|
}
|
|
2777
2822
|
},
|
|
2778
2823
|
metadata: {
|
|
@@ -2780,28 +2825,35 @@
|
|
|
2780
2825
|
type: "map",
|
|
2781
2826
|
defaultValue: {},
|
|
2782
2827
|
ui: {
|
|
2783
|
-
hideFromCollection: true
|
|
2828
|
+
hideFromCollection: true,
|
|
2829
|
+
disabled: {
|
|
2830
|
+
hidden: true
|
|
2831
|
+
}
|
|
2784
2832
|
}
|
|
2785
2833
|
},
|
|
2786
|
-
|
|
2834
|
+
createdAt: {
|
|
2787
2835
|
name: "Created At",
|
|
2788
2836
|
type: "date",
|
|
2789
|
-
|
|
2837
|
+
columnName: "created_at",
|
|
2790
2838
|
ui: {
|
|
2791
|
-
readOnly: true
|
|
2792
|
-
hideFromCollection: true
|
|
2839
|
+
readOnly: true
|
|
2793
2840
|
}
|
|
2794
2841
|
},
|
|
2795
|
-
|
|
2842
|
+
updatedAt: {
|
|
2796
2843
|
name: "Updated At",
|
|
2797
2844
|
type: "date",
|
|
2845
|
+
columnName: "updated_at",
|
|
2798
2846
|
autoValue: "on_update",
|
|
2799
2847
|
ui: {
|
|
2800
|
-
|
|
2801
|
-
|
|
2848
|
+
hideFromCollection: true,
|
|
2849
|
+
disabled: {
|
|
2850
|
+
hidden: true
|
|
2851
|
+
}
|
|
2802
2852
|
}
|
|
2803
2853
|
}
|
|
2804
|
-
}
|
|
2854
|
+
},
|
|
2855
|
+
listProperties: ["displayName", "email", "roles", "createdAt"],
|
|
2856
|
+
propertiesOrder: ["id", "email", "displayName", "roles", "createdAt"]
|
|
2805
2857
|
};
|
|
2806
2858
|
function mapOperator(op) {
|
|
2807
2859
|
switch (op) {
|
|
@@ -3049,6 +3101,9 @@
|
|
|
3049
3101
|
}
|
|
3050
3102
|
});
|
|
3051
3103
|
},
|
|
3104
|
+
deleteAll: driver.deleteAll ? async () => {
|
|
3105
|
+
return driver.deleteAll(slug);
|
|
3106
|
+
} : void 0,
|
|
3052
3107
|
count: driver.countEntities ? async (params) => {
|
|
3053
3108
|
return driver.countEntities({
|
|
3054
3109
|
path: slug,
|
|
@@ -6313,6 +6368,14 @@
|
|
|
6313
6368
|
const parsedId = parsedIdObj[idInfo.fieldName];
|
|
6314
6369
|
await this.db.delete(table).where(drizzleOrm.eq(idField, parsedId));
|
|
6315
6370
|
}
|
|
6371
|
+
/**
|
|
6372
|
+
* Delete all entities from a collection
|
|
6373
|
+
*/
|
|
6374
|
+
async deleteAll(collectionPath, _databaseId) {
|
|
6375
|
+
const collection = getCollectionByPath(collectionPath, this.registry);
|
|
6376
|
+
const table = getTableForCollection(collection, this.registry);
|
|
6377
|
+
await this.db.delete(table);
|
|
6378
|
+
}
|
|
6316
6379
|
/**
|
|
6317
6380
|
* Save an entity (create or update)
|
|
6318
6381
|
*/
|
|
@@ -6644,6 +6707,12 @@
|
|
|
6644
6707
|
async deleteEntity(collectionPath, entityId, databaseId) {
|
|
6645
6708
|
return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
|
|
6646
6709
|
}
|
|
6710
|
+
/**
|
|
6711
|
+
* Delete all entities from a collection
|
|
6712
|
+
*/
|
|
6713
|
+
async deleteAll(collectionPath, databaseId) {
|
|
6714
|
+
return this.persistService.deleteAll(collectionPath, databaseId);
|
|
6715
|
+
}
|
|
6647
6716
|
/**
|
|
6648
6717
|
* Execute raw SQL
|
|
6649
6718
|
*/
|
|
@@ -7344,6 +7413,10 @@
|
|
|
7344
7413
|
await this.realtimeService.notifyEntityUpdate(entity.path, entity.id.toString(), null, entity.databaseId || resolvedCollection?.databaseId);
|
|
7345
7414
|
}
|
|
7346
7415
|
}
|
|
7416
|
+
async deleteAll(path2) {
|
|
7417
|
+
await this.entityService.deleteAll(path2);
|
|
7418
|
+
await this.realtimeService.notifyEntityUpdate(path2, "*", null);
|
|
7419
|
+
}
|
|
7347
7420
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7348
7421
|
return this.entityService.checkUniqueField(path2, name, value, entityId, collection?.databaseId);
|
|
7349
7422
|
}
|
|
@@ -7613,11 +7686,11 @@
|
|
|
7613
7686
|
console.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object:", this.user);
|
|
7614
7687
|
userId = "anonymous";
|
|
7615
7688
|
}
|
|
7616
|
-
const
|
|
7689
|
+
const userRoles = this.user?.roles ?? [];
|
|
7617
7690
|
if (!this.user?.roles) {
|
|
7618
7691
|
console.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object:", this.user);
|
|
7619
7692
|
}
|
|
7620
|
-
const normalizedRoles =
|
|
7693
|
+
const normalizedRoles = userRoles.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
|
|
7621
7694
|
const rolesString = normalizedRoles.join(",");
|
|
7622
7695
|
await tx.execute(drizzleOrm.sql`
|
|
7623
7696
|
SELECT
|
|
@@ -7625,7 +7698,7 @@
|
|
|
7625
7698
|
set_config('app.user_roles', ${rolesString}, true),
|
|
7626
7699
|
set_config('app.jwt', ${JSON.stringify({
|
|
7627
7700
|
sub: userId,
|
|
7628
|
-
roles:
|
|
7701
|
+
roles: userRoles
|
|
7629
7702
|
})}, true)
|
|
7630
7703
|
`);
|
|
7631
7704
|
const txEntityService = new EntityService(tx, this.delegate.registry);
|
|
@@ -7680,6 +7753,9 @@
|
|
|
7680
7753
|
async deleteEntity(props) {
|
|
7681
7754
|
return this.withTransaction((delegate) => delegate.deleteEntity(props));
|
|
7682
7755
|
}
|
|
7756
|
+
async deleteAll(path2) {
|
|
7757
|
+
return this.delegate.deleteAll(path2);
|
|
7758
|
+
}
|
|
7683
7759
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7684
7760
|
return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
|
|
7685
7761
|
}
|
|
@@ -7759,11 +7835,10 @@
|
|
|
7759
7835
|
this.pools.clear();
|
|
7760
7836
|
}
|
|
7761
7837
|
}
|
|
7762
|
-
function createAuthSchema(
|
|
7763
|
-
const rolesSchema = rolesSchemaName === "public" ? null : pgCore.pgSchema(rolesSchemaName);
|
|
7838
|
+
function createAuthSchema(usersSchemaName = "rebase") {
|
|
7764
7839
|
const usersSchema2 = usersSchemaName === "public" ? null : pgCore.pgSchema(usersSchemaName);
|
|
7765
|
-
const
|
|
7766
|
-
const usersTableCreator =
|
|
7840
|
+
const tableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgCore.pgTable;
|
|
7841
|
+
const usersTableCreator = tableCreator;
|
|
7767
7842
|
const users2 = usersTableCreator("users", {
|
|
7768
7843
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7769
7844
|
email: pgCore.varchar("email", {
|
|
@@ -7785,37 +7860,12 @@
|
|
|
7785
7860
|
}),
|
|
7786
7861
|
emailVerificationSentAt: pgCore.timestamp("email_verification_sent_at"),
|
|
7787
7862
|
isAnonymous: pgCore.boolean("is_anonymous").default(false).notNull(),
|
|
7863
|
+
roles: pgCore.text("roles").array().default([]).notNull(),
|
|
7788
7864
|
metadata: pgCore.jsonb("metadata").$type().default({}).notNull(),
|
|
7789
7865
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
|
|
7790
7866
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7791
7867
|
});
|
|
7792
|
-
const
|
|
7793
|
-
id: pgCore.varchar("id", {
|
|
7794
|
-
length: 50
|
|
7795
|
-
}).primaryKey(),
|
|
7796
|
-
// 'admin', 'editor', 'viewer'
|
|
7797
|
-
name: pgCore.varchar("name", {
|
|
7798
|
-
length: 100
|
|
7799
|
-
}).notNull(),
|
|
7800
|
-
isAdmin: pgCore.boolean("is_admin").default(false).notNull(),
|
|
7801
|
-
defaultPermissions: pgCore.jsonb("default_permissions").$type(),
|
|
7802
|
-
collectionPermissions: pgCore.jsonb("collection_permissions").$type()
|
|
7803
|
-
});
|
|
7804
|
-
const userRoles2 = rolesTableCreator("user_roles", {
|
|
7805
|
-
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7806
|
-
onDelete: "cascade"
|
|
7807
|
-
}),
|
|
7808
|
-
roleId: pgCore.varchar("role_id", {
|
|
7809
|
-
length: 50
|
|
7810
|
-
}).notNull().references(() => roles2.id, {
|
|
7811
|
-
onDelete: "cascade"
|
|
7812
|
-
})
|
|
7813
|
-
}, (table) => ({
|
|
7814
|
-
pk: pgCore.primaryKey({
|
|
7815
|
-
columns: [table.userId, table.roleId]
|
|
7816
|
-
})
|
|
7817
|
-
}));
|
|
7818
|
-
const refreshTokens2 = rolesTableCreator("refresh_tokens", {
|
|
7868
|
+
const refreshTokens2 = tableCreator("refresh_tokens", {
|
|
7819
7869
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7820
7870
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7821
7871
|
onDelete: "cascade"
|
|
@@ -7834,7 +7884,7 @@
|
|
|
7834
7884
|
}, (table) => ({
|
|
7835
7885
|
uniqueDeviceSession: pgCore.unique("unique_device_session").on(table.userId, table.userAgent, table.ipAddress)
|
|
7836
7886
|
}));
|
|
7837
|
-
const passwordResetTokens2 =
|
|
7887
|
+
const passwordResetTokens2 = tableCreator("password_reset_tokens", {
|
|
7838
7888
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7839
7889
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7840
7890
|
onDelete: "cascade"
|
|
@@ -7846,14 +7896,14 @@
|
|
|
7846
7896
|
usedAt: pgCore.timestamp("used_at"),
|
|
7847
7897
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
7848
7898
|
});
|
|
7849
|
-
const appConfig2 =
|
|
7899
|
+
const appConfig2 = tableCreator("app_config", {
|
|
7850
7900
|
key: pgCore.varchar("key", {
|
|
7851
7901
|
length: 100
|
|
7852
7902
|
}).primaryKey(),
|
|
7853
7903
|
value: pgCore.jsonb("value").notNull(),
|
|
7854
7904
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7855
7905
|
});
|
|
7856
|
-
const userIdentities2 =
|
|
7906
|
+
const userIdentities2 = tableCreator("user_identities", {
|
|
7857
7907
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7858
7908
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7859
7909
|
onDelete: "cascade"
|
|
@@ -7871,7 +7921,7 @@
|
|
|
7871
7921
|
}, (table) => ({
|
|
7872
7922
|
uniqueProviderId: pgCore.unique("unique_provider_id").on(table.provider, table.providerId)
|
|
7873
7923
|
}));
|
|
7874
|
-
const mfaFactors2 =
|
|
7924
|
+
const mfaFactors2 = tableCreator("mfa_factors", {
|
|
7875
7925
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7876
7926
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7877
7927
|
onDelete: "cascade"
|
|
@@ -7890,7 +7940,7 @@
|
|
|
7890
7940
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
|
|
7891
7941
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7892
7942
|
});
|
|
7893
|
-
const mfaChallenges2 =
|
|
7943
|
+
const mfaChallenges2 = tableCreator("mfa_challenges", {
|
|
7894
7944
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7895
7945
|
factorId: pgCore.uuid("factor_id").notNull().references(() => mfaFactors2.id, {
|
|
7896
7946
|
onDelete: "cascade"
|
|
@@ -7902,7 +7952,7 @@
|
|
|
7902
7952
|
}),
|
|
7903
7953
|
expiresAt: pgCore.timestamp("expires_at").notNull()
|
|
7904
7954
|
});
|
|
7905
|
-
const recoveryCodes2 =
|
|
7955
|
+
const recoveryCodes2 = tableCreator("recovery_codes", {
|
|
7906
7956
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7907
7957
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7908
7958
|
onDelete: "cascade"
|
|
@@ -7914,11 +7964,8 @@
|
|
|
7914
7964
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
7915
7965
|
});
|
|
7916
7966
|
return {
|
|
7917
|
-
rolesSchema,
|
|
7918
7967
|
usersSchema: usersSchema2,
|
|
7919
7968
|
users: users2,
|
|
7920
|
-
roles: roles2,
|
|
7921
|
-
userRoles: userRoles2,
|
|
7922
7969
|
refreshTokens: refreshTokens2,
|
|
7923
7970
|
passwordResetTokens: passwordResetTokens2,
|
|
7924
7971
|
appConfig: appConfig2,
|
|
@@ -7928,12 +7975,9 @@
|
|
|
7928
7975
|
recoveryCodes: recoveryCodes2
|
|
7929
7976
|
};
|
|
7930
7977
|
}
|
|
7931
|
-
const defaultAuthSchema = createAuthSchema("rebase"
|
|
7932
|
-
const rebaseSchema = defaultAuthSchema.rolesSchema;
|
|
7978
|
+
const defaultAuthSchema = createAuthSchema("rebase");
|
|
7933
7979
|
const usersSchema = defaultAuthSchema.usersSchema;
|
|
7934
7980
|
const users = defaultAuthSchema.users;
|
|
7935
|
-
const roles = defaultAuthSchema.roles;
|
|
7936
|
-
const userRoles = defaultAuthSchema.userRoles;
|
|
7937
7981
|
const refreshTokens = defaultAuthSchema.refreshTokens;
|
|
7938
7982
|
const passwordResetTokens = defaultAuthSchema.passwordResetTokens;
|
|
7939
7983
|
const appConfig = defaultAuthSchema.appConfig;
|
|
@@ -7944,30 +7988,12 @@
|
|
|
7944
7988
|
const usersRelations = drizzleOrm.relations(users, ({
|
|
7945
7989
|
many
|
|
7946
7990
|
}) => ({
|
|
7947
|
-
userRoles: many(userRoles),
|
|
7948
7991
|
refreshTokens: many(refreshTokens),
|
|
7949
7992
|
passwordResetTokens: many(passwordResetTokens),
|
|
7950
7993
|
userIdentities: many(userIdentities),
|
|
7951
7994
|
mfaFactors: many(mfaFactors),
|
|
7952
7995
|
recoveryCodes: many(recoveryCodes)
|
|
7953
7996
|
}));
|
|
7954
|
-
const rolesRelations = drizzleOrm.relations(roles, ({
|
|
7955
|
-
many
|
|
7956
|
-
}) => ({
|
|
7957
|
-
userRoles: many(userRoles)
|
|
7958
|
-
}));
|
|
7959
|
-
const userRolesRelations = drizzleOrm.relations(userRoles, ({
|
|
7960
|
-
one
|
|
7961
|
-
}) => ({
|
|
7962
|
-
user: one(users, {
|
|
7963
|
-
fields: [userRoles.userId],
|
|
7964
|
-
references: [users.id]
|
|
7965
|
-
}),
|
|
7966
|
-
role: one(roles, {
|
|
7967
|
-
fields: [userRoles.roleId],
|
|
7968
|
-
references: [roles.id]
|
|
7969
|
-
})
|
|
7970
|
-
}));
|
|
7971
7997
|
const refreshTokensRelations = drizzleOrm.relations(refreshTokens, ({
|
|
7972
7998
|
one
|
|
7973
7999
|
}) => ({
|
|
@@ -8074,6 +8100,8 @@
|
|
|
8074
8100
|
columnDefinition = `${enumName}("${colName}")`;
|
|
8075
8101
|
} else if ("isId" in stringProp && stringProp.isId === "uuid") {
|
|
8076
8102
|
columnDefinition = `uuid("${colName}")`;
|
|
8103
|
+
} else if (stringProp.columnType === "uuid") {
|
|
8104
|
+
columnDefinition = `uuid("${colName}")`;
|
|
8077
8105
|
} else if (stringProp.columnType === "text") {
|
|
8078
8106
|
columnDefinition = `text("${colName}")`;
|
|
8079
8107
|
} else if (stringProp.columnType === "char") {
|
|
@@ -8141,11 +8169,38 @@
|
|
|
8141
8169
|
}
|
|
8142
8170
|
break;
|
|
8143
8171
|
}
|
|
8144
|
-
case "map":
|
|
8172
|
+
case "map": {
|
|
8173
|
+
const mapProp = prop;
|
|
8174
|
+
if (mapProp.columnType === "json") {
|
|
8175
|
+
columnDefinition = `json("${colName}")`;
|
|
8176
|
+
} else {
|
|
8177
|
+
columnDefinition = `jsonb("${colName}")`;
|
|
8178
|
+
}
|
|
8179
|
+
break;
|
|
8180
|
+
}
|
|
8145
8181
|
case "array": {
|
|
8146
|
-
const
|
|
8147
|
-
|
|
8182
|
+
const arrayProp = prop;
|
|
8183
|
+
let colType = arrayProp.columnType;
|
|
8184
|
+
if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {
|
|
8185
|
+
const ofProp = arrayProp.of;
|
|
8186
|
+
if (ofProp.type === "string") {
|
|
8187
|
+
colType = "text[]";
|
|
8188
|
+
} else if (ofProp.type === "number") {
|
|
8189
|
+
colType = ofProp.validation?.integer ? "integer[]" : "numeric[]";
|
|
8190
|
+
} else if (ofProp.type === "boolean") {
|
|
8191
|
+
colType = "boolean[]";
|
|
8192
|
+
}
|
|
8193
|
+
}
|
|
8194
|
+
if (colType === "json") {
|
|
8148
8195
|
columnDefinition = `json("${colName}")`;
|
|
8196
|
+
} else if (colType === "text[]") {
|
|
8197
|
+
columnDefinition = `text("${colName}").array()`;
|
|
8198
|
+
} else if (colType === "integer[]") {
|
|
8199
|
+
columnDefinition = `integer("${colName}").array()`;
|
|
8200
|
+
} else if (colType === "boolean[]") {
|
|
8201
|
+
columnDefinition = `boolean("${colName}").array()`;
|
|
8202
|
+
} else if (colType === "numeric[]") {
|
|
8203
|
+
columnDefinition = `numeric("${colName}").array()`;
|
|
8149
8204
|
} else {
|
|
8150
8205
|
columnDefinition = `jsonb("${colName}")`;
|
|
8151
8206
|
}
|
|
@@ -8229,8 +8284,8 @@
|
|
|
8229
8284
|
const resolved = expression.replace(/\{(\w+)\}/g, (_, col) => col);
|
|
8230
8285
|
return `sql\`${resolved}\``;
|
|
8231
8286
|
};
|
|
8232
|
-
const wrapWithRoleCheck = (clause,
|
|
8233
|
-
const rolesArrayString = `ARRAY[${
|
|
8287
|
+
const wrapWithRoleCheck = (clause, roles) => {
|
|
8288
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8234
8289
|
const roleCondition = `string_to_array(auth.roles(), ',') @> ${rolesArrayString}`;
|
|
8235
8290
|
return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
|
|
8236
8291
|
};
|
|
@@ -8283,22 +8338,22 @@
|
|
|
8283
8338
|
};
|
|
8284
8339
|
const generateSinglePolicyCode = (collection, rule, operation, policyName) => {
|
|
8285
8340
|
const mode = rule.mode ?? "permissive";
|
|
8286
|
-
const
|
|
8341
|
+
const roles = rule.roles ? [...rule.roles].sort() : void 0;
|
|
8287
8342
|
const needsUsing = operation !== "insert";
|
|
8288
8343
|
const needsWithCheck = operation !== "select" && operation !== "delete";
|
|
8289
8344
|
let usingClause = needsUsing ? buildUsingClause(rule, collection) : null;
|
|
8290
8345
|
let withCheckClause = needsWithCheck ? buildWithCheckClause(rule, collection) : null;
|
|
8291
|
-
if (
|
|
8346
|
+
if (roles && roles.length > 0) {
|
|
8292
8347
|
if (usingClause) {
|
|
8293
|
-
usingClause = wrapWithRoleCheck(usingClause,
|
|
8348
|
+
usingClause = wrapWithRoleCheck(usingClause, roles);
|
|
8294
8349
|
} else if (needsUsing) {
|
|
8295
|
-
const rolesArrayString = `ARRAY[${
|
|
8350
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8296
8351
|
usingClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8297
8352
|
}
|
|
8298
8353
|
if (withCheckClause) {
|
|
8299
|
-
withCheckClause = wrapWithRoleCheck(withCheckClause,
|
|
8354
|
+
withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
|
|
8300
8355
|
} else if (needsWithCheck) {
|
|
8301
|
-
const rolesArrayString = `ARRAY[${
|
|
8356
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8302
8357
|
withCheckClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8303
8358
|
}
|
|
8304
8359
|
}
|
|
@@ -10032,15 +10087,15 @@ ${tableRelations.join(",\n")}
|
|
|
10032
10087
|
wsDebug("👤 [WebSocket Server] Processing FETCH_ROLES request");
|
|
10033
10088
|
const delegate = await getScopedDelegate();
|
|
10034
10089
|
const admin = delegate.admin;
|
|
10035
|
-
let
|
|
10090
|
+
let roles = [];
|
|
10036
10091
|
if (isSQLAdmin(admin) && admin.fetchAvailableRoles) {
|
|
10037
|
-
|
|
10092
|
+
roles = await admin.fetchAvailableRoles();
|
|
10038
10093
|
}
|
|
10039
|
-
wsDebug(`👤 [WebSocket Server] Fetched ${
|
|
10094
|
+
wsDebug(`👤 [WebSocket Server] Fetched ${roles.length} roles.`);
|
|
10040
10095
|
const response = {
|
|
10041
10096
|
type: "FETCH_ROLES_SUCCESS",
|
|
10042
10097
|
payload: {
|
|
10043
|
-
roles
|
|
10098
|
+
roles
|
|
10044
10099
|
},
|
|
10045
10100
|
requestId
|
|
10046
10101
|
};
|
|
@@ -10294,37 +10349,6 @@ ${tableRelations.join(",\n")}
|
|
|
10294
10349
|
return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
|
|
10295
10350
|
}
|
|
10296
10351
|
}
|
|
10297
|
-
const DEFAULT_ROLES = [{
|
|
10298
|
-
id: "admin",
|
|
10299
|
-
name: "Admin",
|
|
10300
|
-
is_admin: true,
|
|
10301
|
-
default_permissions: {
|
|
10302
|
-
read: true,
|
|
10303
|
-
create: true,
|
|
10304
|
-
edit: true,
|
|
10305
|
-
delete: true
|
|
10306
|
-
}
|
|
10307
|
-
}, {
|
|
10308
|
-
id: "editor",
|
|
10309
|
-
name: "Editor",
|
|
10310
|
-
is_admin: false,
|
|
10311
|
-
default_permissions: {
|
|
10312
|
-
read: true,
|
|
10313
|
-
create: true,
|
|
10314
|
-
edit: true,
|
|
10315
|
-
delete: true
|
|
10316
|
-
}
|
|
10317
|
-
}, {
|
|
10318
|
-
id: "viewer",
|
|
10319
|
-
name: "Viewer",
|
|
10320
|
-
is_admin: false,
|
|
10321
|
-
default_permissions: {
|
|
10322
|
-
read: true,
|
|
10323
|
-
create: false,
|
|
10324
|
-
edit: false,
|
|
10325
|
-
delete: false
|
|
10326
|
-
}
|
|
10327
|
-
}];
|
|
10328
10352
|
async function ensureAuthTablesExist(db, registry) {
|
|
10329
10353
|
serverCore.logger.info("🔍 Checking auth tables...");
|
|
10330
10354
|
try {
|
|
@@ -10353,26 +10377,15 @@ ${tableRelations.join(",\n")}
|
|
|
10353
10377
|
}
|
|
10354
10378
|
}
|
|
10355
10379
|
}
|
|
10356
|
-
let rolesSchema = "rebase";
|
|
10357
|
-
if (registry) {
|
|
10358
|
-
const rolesTable = registry.getTable("roles");
|
|
10359
|
-
if (rolesTable) {
|
|
10360
|
-
rolesSchema = pgCore.getTableConfig(rolesTable).schema || "public";
|
|
10361
|
-
}
|
|
10362
|
-
}
|
|
10363
10380
|
if (usersSchema2 !== "public") {
|
|
10364
10381
|
await db.execute(drizzleOrm.sql`CREATE SCHEMA IF NOT EXISTS ${drizzleOrm.sql.raw(usersSchema2)}`);
|
|
10365
10382
|
}
|
|
10366
|
-
if (rolesSchema !== "public" && rolesSchema !== usersSchema2) {
|
|
10367
|
-
await db.execute(drizzleOrm.sql`CREATE SCHEMA IF NOT EXISTS ${drizzleOrm.sql.raw(rolesSchema)}`);
|
|
10368
|
-
}
|
|
10369
10383
|
await db.execute(drizzleOrm.sql`CREATE SCHEMA IF NOT EXISTS rebase`);
|
|
10370
|
-
const
|
|
10371
|
-
const
|
|
10372
|
-
const
|
|
10373
|
-
const
|
|
10374
|
-
const
|
|
10375
|
-
const appConfigTableName = `"${rolesSchema}"."app_config"`;
|
|
10384
|
+
const authSchema = usersSchema2 === "public" ? "rebase" : usersSchema2;
|
|
10385
|
+
const userIdentitiesTable = `"${authSchema}"."user_identities"`;
|
|
10386
|
+
const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
|
|
10387
|
+
const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
|
|
10388
|
+
const appConfigTableName = `"${authSchema}"."app_config"`;
|
|
10376
10389
|
await db.execute(drizzleOrm.sql`
|
|
10377
10390
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(userIdentitiesTable)} (
|
|
10378
10391
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10389,27 +10402,6 @@ ${tableRelations.join(",\n")}
|
|
|
10389
10402
|
CREATE INDEX IF NOT EXISTS idx_user_identities_user
|
|
10390
10403
|
ON ${drizzleOrm.sql.raw(userIdentitiesTable)}(user_id)
|
|
10391
10404
|
`);
|
|
10392
|
-
await db.execute(drizzleOrm.sql`
|
|
10393
|
-
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(rolesTableName)} (
|
|
10394
|
-
id TEXT PRIMARY KEY,
|
|
10395
|
-
name TEXT NOT NULL,
|
|
10396
|
-
is_admin BOOLEAN DEFAULT FALSE,
|
|
10397
|
-
default_permissions JSONB,
|
|
10398
|
-
collection_permissions JSONB,
|
|
10399
|
-
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
|
10400
|
-
)
|
|
10401
|
-
`);
|
|
10402
|
-
await db.execute(drizzleOrm.sql`
|
|
10403
|
-
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(userRolesTableName)} (
|
|
10404
|
-
user_id ${drizzleOrm.sql.raw(userIdType)} NOT NULL REFERENCES ${drizzleOrm.sql.raw(usersTableName)}(id) ON DELETE CASCADE,
|
|
10405
|
-
role_id TEXT NOT NULL REFERENCES ${drizzleOrm.sql.raw(rolesTableName)}(id) ON DELETE CASCADE,
|
|
10406
|
-
PRIMARY KEY (user_id, role_id)
|
|
10407
|
-
)
|
|
10408
|
-
`);
|
|
10409
|
-
await db.execute(drizzleOrm.sql`
|
|
10410
|
-
CREATE INDEX IF NOT EXISTS idx_user_roles_user
|
|
10411
|
-
ON ${drizzleOrm.sql.raw(userRolesTableName)}(user_id)
|
|
10412
|
-
`);
|
|
10413
10405
|
await db.execute(drizzleOrm.sql`
|
|
10414
10406
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(refreshTokensTableName)} (
|
|
10415
10407
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10477,14 +10469,43 @@ ${tableRelations.join(",\n")}
|
|
|
10477
10469
|
$$ LANGUAGE sql STABLE
|
|
10478
10470
|
`);
|
|
10479
10471
|
});
|
|
10480
|
-
await seedDefaultRoles(db, rolesTableName);
|
|
10481
10472
|
await db.execute(drizzleOrm.sql`
|
|
10482
10473
|
ALTER TABLE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10483
10474
|
ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
|
|
10484
10475
|
`);
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10476
|
+
await db.execute(drizzleOrm.sql`
|
|
10477
|
+
ALTER TABLE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10478
|
+
ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
|
|
10479
|
+
`);
|
|
10480
|
+
try {
|
|
10481
|
+
const legacyCheck = await db.execute(drizzleOrm.sql`
|
|
10482
|
+
SELECT EXISTS (
|
|
10483
|
+
SELECT 1 FROM information_schema.tables
|
|
10484
|
+
WHERE table_schema = 'rebase' AND table_name = 'user_roles'
|
|
10485
|
+
) AS has_user_roles
|
|
10486
|
+
`);
|
|
10487
|
+
const hasLegacyTables = legacyCheck.rows[0].has_user_roles;
|
|
10488
|
+
if (hasLegacyTables) {
|
|
10489
|
+
serverCore.logger.info("🔄 Migrating roles from legacy user_roles table...");
|
|
10490
|
+
await db.execute(drizzleOrm.sql`
|
|
10491
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)} u
|
|
10492
|
+
SET roles = COALESCE((
|
|
10493
|
+
SELECT array_agg(ur.role_id)
|
|
10494
|
+
FROM "rebase"."user_roles" ur
|
|
10495
|
+
WHERE ur.user_id = u.id
|
|
10496
|
+
), '{}')
|
|
10497
|
+
WHERE u.roles = '{}' OR u.roles IS NULL
|
|
10498
|
+
`);
|
|
10499
|
+
await db.execute(drizzleOrm.sql`DROP TABLE IF EXISTS "rebase"."user_roles" CASCADE`);
|
|
10500
|
+
await db.execute(drizzleOrm.sql`DROP TABLE IF EXISTS "rebase"."roles" CASCADE`);
|
|
10501
|
+
serverCore.logger.info("✅ Legacy roles tables migrated and dropped");
|
|
10502
|
+
}
|
|
10503
|
+
} catch (migrationError) {
|
|
10504
|
+
serverCore.logger.warn(`⚠️ Legacy roles migration skipped: ${migrationError instanceof Error ? migrationError.message : String(migrationError)}`);
|
|
10505
|
+
}
|
|
10506
|
+
const mfaFactorsTableName = `"${authSchema}"."mfa_factors"`;
|
|
10507
|
+
const mfaChallengesTableName = `"${authSchema}"."mfa_challenges"`;
|
|
10508
|
+
const recoveryCodesTableName = `"${authSchema}"."recovery_codes"`;
|
|
10488
10509
|
await db.execute(drizzleOrm.sql`
|
|
10489
10510
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(mfaFactorsTableName)} (
|
|
10490
10511
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10536,28 +10557,6 @@ ${tableRelations.join(",\n")}
|
|
|
10536
10557
|
serverCore.logger.warn("⚠️ Continuing without creating auth tables.");
|
|
10537
10558
|
}
|
|
10538
10559
|
}
|
|
10539
|
-
async function seedDefaultRoles(db, rolesTableName) {
|
|
10540
|
-
const result = await db.execute(drizzleOrm.sql`SELECT COUNT(*) as count FROM ${drizzleOrm.sql.raw(rolesTableName)}`);
|
|
10541
|
-
const count = parseInt(result.rows[0]?.count || "0", 10);
|
|
10542
|
-
if (count > 0) {
|
|
10543
|
-
serverCore.logger.info(`📋 Found ${count} existing roles`);
|
|
10544
|
-
return;
|
|
10545
|
-
}
|
|
10546
|
-
serverCore.logger.info("🌱 Seeding default roles...");
|
|
10547
|
-
for (const role of DEFAULT_ROLES) {
|
|
10548
|
-
await db.execute(drizzleOrm.sql`
|
|
10549
|
-
INSERT INTO ${drizzleOrm.sql.raw(rolesTableName)} (id, name, is_admin, default_permissions)
|
|
10550
|
-
VALUES (
|
|
10551
|
-
${role.id},
|
|
10552
|
-
${role.name},
|
|
10553
|
-
${role.is_admin},
|
|
10554
|
-
${JSON.stringify(role.default_permissions)}::jsonb
|
|
10555
|
-
)
|
|
10556
|
-
ON CONFLICT (id) DO NOTHING
|
|
10557
|
-
`);
|
|
10558
|
-
}
|
|
10559
|
-
serverCore.logger.info("✅ Default roles created: admin, editor, viewer");
|
|
10560
|
-
}
|
|
10561
10560
|
function getColumnKey(table, ...keys2) {
|
|
10562
10561
|
if (!table) return void 0;
|
|
10563
10562
|
for (const key of keys2) {
|
|
@@ -10577,24 +10576,18 @@ ${tableRelations.join(",\n")}
|
|
|
10577
10576
|
class UserService {
|
|
10578
10577
|
constructor(db, tableOrTables) {
|
|
10579
10578
|
this.db = db;
|
|
10580
|
-
if (tableOrTables &&
|
|
10579
|
+
if (tableOrTables && tableOrTables.users) {
|
|
10581
10580
|
const tables = tableOrTables;
|
|
10582
10581
|
this.usersTable = tables.users || users;
|
|
10583
10582
|
this.userIdentitiesTable = tables.userIdentities || userIdentities;
|
|
10584
|
-
this.userRolesTable = tables.userRoles || userRoles;
|
|
10585
|
-
this.rolesTable = tables.roles || roles;
|
|
10586
10583
|
} else {
|
|
10587
10584
|
const table = tableOrTables;
|
|
10588
10585
|
this.usersTable = table || users;
|
|
10589
10586
|
this.userIdentitiesTable = userIdentities;
|
|
10590
|
-
this.userRolesTable = userRoles;
|
|
10591
|
-
this.rolesTable = roles;
|
|
10592
10587
|
}
|
|
10593
10588
|
}
|
|
10594
10589
|
usersTable;
|
|
10595
10590
|
userIdentitiesTable;
|
|
10596
|
-
userRolesTable;
|
|
10597
|
-
rolesTable;
|
|
10598
10591
|
getQualifiedUsersTableName() {
|
|
10599
10592
|
const name = drizzleOrm.getTableName(this.usersTable);
|
|
10600
10593
|
const schema = pgCore.getTableConfig(this.usersTable).schema || "public";
|
|
@@ -10616,7 +10609,7 @@ ${tableRelations.join(",\n")}
|
|
|
10616
10609
|
const metadata = {
|
|
10617
10610
|
...row.metadata || {}
|
|
10618
10611
|
};
|
|
10619
|
-
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"]);
|
|
10612
|
+
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"]);
|
|
10620
10613
|
for (const [key, val] of Object.entries(row)) {
|
|
10621
10614
|
if (!knownKeys.has(key)) {
|
|
10622
10615
|
const camelKey = camelCase(key);
|
|
@@ -10767,19 +10760,18 @@ ${tableRelations.join(",\n")}
|
|
|
10767
10760
|
const displayNameCol = getColumn(this.usersTable, "displayName", "display_name");
|
|
10768
10761
|
const displayNameColumn = displayNameCol ? displayNameCol.name : "display_name";
|
|
10769
10762
|
const idCol = getColumn(this.usersTable, "id");
|
|
10770
|
-
|
|
10763
|
+
idCol ? idCol.name : "id";
|
|
10771
10764
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
10772
|
-
const rolesSchema = pgCore.getTableConfig(this.userRolesTable).schema || "public";
|
|
10773
10765
|
const conditions = [];
|
|
10774
10766
|
if (roleId) {
|
|
10775
|
-
conditions.push(drizzleOrm.sql
|
|
10767
|
+
conditions.push(drizzleOrm.sql`${roleId} = ANY(${drizzleOrm.sql.raw(usersTableName)}.roles)`);
|
|
10776
10768
|
}
|
|
10777
10769
|
if (search) {
|
|
10778
10770
|
const pattern = `%${search}%`;
|
|
10779
10771
|
conditions.push(drizzleOrm.sql`(${drizzleOrm.sql.raw(usersTableName)}.${drizzleOrm.sql.raw(emailColumn)} ILIKE ${pattern} OR ${drizzleOrm.sql.raw(usersTableName)}.${drizzleOrm.sql.raw(displayNameColumn)} ILIKE ${pattern})`);
|
|
10780
10772
|
}
|
|
10781
10773
|
const whereClause = conditions.length > 0 ? drizzleOrm.sql`WHERE ${drizzleOrm.sql.join(conditions, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
|
|
10782
|
-
const orderByClause = roleId ? drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.raw(usersTableName)}.${drizzleOrm.sql.raw(orderColumn)} ${direction}` : drizzleOrm.sql`ORDER BY (
|
|
10774
|
+
const orderByClause = roleId ? drizzleOrm.sql`ORDER BY ${drizzleOrm.sql.raw(usersTableName)}.${drizzleOrm.sql.raw(orderColumn)} ${direction}` : drizzleOrm.sql`ORDER BY array_length(${drizzleOrm.sql.raw(usersTableName)}.roles, 1) DESC NULLS LAST, ${drizzleOrm.sql.raw(usersTableName)}.${drizzleOrm.sql.raw(orderColumn)} ${direction}`;
|
|
10783
10775
|
const countResult = await this.db.execute(drizzleOrm.sql`
|
|
10784
10776
|
SELECT count(*)::int as total FROM ${drizzleOrm.sql.raw(usersTableName)}
|
|
10785
10777
|
${whereClause}
|
|
@@ -10853,54 +10845,57 @@ ${tableRelations.join(",\n")}
|
|
|
10853
10845
|
return row ? this.mapRowToUser(row) : null;
|
|
10854
10846
|
}
|
|
10855
10847
|
/**
|
|
10856
|
-
* Get roles for a user from database
|
|
10848
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
10857
10849
|
*/
|
|
10858
10850
|
async getUserRoles(userId) {
|
|
10859
|
-
const
|
|
10851
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10860
10852
|
const result = await this.db.execute(drizzleOrm.sql`
|
|
10861
|
-
SELECT
|
|
10862
|
-
FROM ${drizzleOrm.sql.raw(`"${rolesSchema}"."roles"`)} r
|
|
10863
|
-
INNER JOIN ${drizzleOrm.sql.raw(`"${rolesSchema}"."user_roles"`)} ur ON r.id = ur.role_id
|
|
10864
|
-
WHERE ur.user_id = ${userId}
|
|
10853
|
+
SELECT roles FROM ${drizzleOrm.sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10865
10854
|
`);
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10855
|
+
if (result.rows.length === 0) return [];
|
|
10856
|
+
const row = result.rows[0];
|
|
10857
|
+
const roleIds = row.roles ?? [];
|
|
10858
|
+
return roleIds.map((id) => ({
|
|
10859
|
+
id,
|
|
10860
|
+
name: id,
|
|
10861
|
+
isAdmin: id === "admin",
|
|
10862
|
+
defaultPermissions: null,
|
|
10863
|
+
collectionPermissions: null
|
|
10872
10864
|
}));
|
|
10873
10865
|
}
|
|
10874
10866
|
/**
|
|
10875
10867
|
* Get role IDs for a user
|
|
10876
10868
|
*/
|
|
10877
10869
|
async getUserRoleIds(userId) {
|
|
10878
|
-
const
|
|
10879
|
-
|
|
10870
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10871
|
+
const result = await this.db.execute(drizzleOrm.sql`
|
|
10872
|
+
SELECT roles FROM ${drizzleOrm.sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10873
|
+
`);
|
|
10874
|
+
if (result.rows.length === 0) return [];
|
|
10875
|
+
const row = result.rows[0];
|
|
10876
|
+
return row.roles ?? [];
|
|
10880
10877
|
}
|
|
10881
10878
|
/**
|
|
10882
|
-
* Set roles for a user
|
|
10879
|
+
* Set roles for a user (replaces existing roles)
|
|
10883
10880
|
*/
|
|
10884
10881
|
async setUserRoles(userId, roleIds) {
|
|
10885
|
-
const
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
`);
|
|
10893
|
-
}
|
|
10882
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10883
|
+
const rolesArray = `{${roleIds.join(",")}}`;
|
|
10884
|
+
await this.db.execute(drizzleOrm.sql`
|
|
10885
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10886
|
+
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
10887
|
+
WHERE id = ${userId}
|
|
10888
|
+
`);
|
|
10894
10889
|
}
|
|
10895
10890
|
/**
|
|
10896
|
-
* Assign a specific role to new user
|
|
10891
|
+
* Assign a specific role to new user (appends if not present)
|
|
10897
10892
|
*/
|
|
10898
10893
|
async assignDefaultRole(userId, roleId) {
|
|
10899
|
-
const
|
|
10894
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10900
10895
|
await this.db.execute(drizzleOrm.sql`
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10896
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10897
|
+
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
10898
|
+
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
10904
10899
|
`);
|
|
10905
10900
|
}
|
|
10906
10901
|
/**
|
|
@@ -10909,101 +10904,12 @@ ${tableRelations.join(",\n")}
|
|
|
10909
10904
|
async getUserWithRoles(userId) {
|
|
10910
10905
|
const user = await this.getUserById(userId);
|
|
10911
10906
|
if (!user) return null;
|
|
10912
|
-
const
|
|
10907
|
+
const roles = await this.getUserRoles(userId);
|
|
10913
10908
|
return {
|
|
10914
10909
|
user,
|
|
10915
|
-
roles
|
|
10916
|
-
};
|
|
10917
|
-
}
|
|
10918
|
-
}
|
|
10919
|
-
class RoleService {
|
|
10920
|
-
constructor(db, tableOrTables) {
|
|
10921
|
-
this.db = db;
|
|
10922
|
-
if (tableOrTables && (tableOrTables.roles || tableOrTables.users)) {
|
|
10923
|
-
this.rolesTable = tableOrTables.roles || roles;
|
|
10924
|
-
} else {
|
|
10925
|
-
this.rolesTable = tableOrTables || roles;
|
|
10926
|
-
}
|
|
10927
|
-
}
|
|
10928
|
-
rolesTable;
|
|
10929
|
-
getQualifiedRolesTableName() {
|
|
10930
|
-
const name = drizzleOrm.getTableName(this.rolesTable);
|
|
10931
|
-
const schema = pgCore.getTableConfig(this.rolesTable).schema || "public";
|
|
10932
|
-
return `"${schema}"."${name}"`;
|
|
10933
|
-
}
|
|
10934
|
-
async getRoleById(id) {
|
|
10935
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10936
|
-
const result = await this.db.execute(drizzleOrm.sql`
|
|
10937
|
-
SELECT id, name, is_admin, default_permissions, collection_permissions
|
|
10938
|
-
FROM ${drizzleOrm.sql.raw(tableName)}
|
|
10939
|
-
WHERE id = ${id}
|
|
10940
|
-
`);
|
|
10941
|
-
if (result.rows.length === 0) return null;
|
|
10942
|
-
const row = result.rows[0];
|
|
10943
|
-
return {
|
|
10944
|
-
id: row.id,
|
|
10945
|
-
name: row.name,
|
|
10946
|
-
isAdmin: row.is_admin,
|
|
10947
|
-
defaultPermissions: row.default_permissions,
|
|
10948
|
-
collectionPermissions: row.collection_permissions
|
|
10949
|
-
};
|
|
10950
|
-
}
|
|
10951
|
-
async listRoles() {
|
|
10952
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10953
|
-
const result = await this.db.execute(drizzleOrm.sql`
|
|
10954
|
-
SELECT id, name, is_admin, default_permissions, collection_permissions
|
|
10955
|
-
FROM ${drizzleOrm.sql.raw(tableName)}
|
|
10956
|
-
ORDER BY name
|
|
10957
|
-
`);
|
|
10958
|
-
return result.rows.map((row) => ({
|
|
10959
|
-
id: row.id,
|
|
10960
|
-
name: row.name,
|
|
10961
|
-
isAdmin: row.is_admin,
|
|
10962
|
-
defaultPermissions: row.default_permissions,
|
|
10963
|
-
collectionPermissions: row.collection_permissions
|
|
10964
|
-
}));
|
|
10965
|
-
}
|
|
10966
|
-
async createRole(data) {
|
|
10967
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10968
|
-
const result = await this.db.execute(drizzleOrm.sql`
|
|
10969
|
-
INSERT INTO ${drizzleOrm.sql.raw(tableName)} (id, name, is_admin, default_permissions, collection_permissions)
|
|
10970
|
-
VALUES (
|
|
10971
|
-
${data.id},
|
|
10972
|
-
${data.name},
|
|
10973
|
-
${data.isAdmin ?? false},
|
|
10974
|
-
${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : null}::jsonb,
|
|
10975
|
-
${data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null}::jsonb
|
|
10976
|
-
)
|
|
10977
|
-
RETURNING id, name, is_admin, default_permissions, collection_permissions
|
|
10978
|
-
`);
|
|
10979
|
-
const row = result.rows[0];
|
|
10980
|
-
return {
|
|
10981
|
-
id: row.id,
|
|
10982
|
-
name: row.name,
|
|
10983
|
-
isAdmin: row.is_admin,
|
|
10984
|
-
defaultPermissions: row.default_permissions,
|
|
10985
|
-
collectionPermissions: row.collection_permissions
|
|
10910
|
+
roles
|
|
10986
10911
|
};
|
|
10987
10912
|
}
|
|
10988
|
-
async updateRole(id, data) {
|
|
10989
|
-
const existing = await this.getRoleById(id);
|
|
10990
|
-
if (!existing) return null;
|
|
10991
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
10992
|
-
await this.db.execute(drizzleOrm.sql`
|
|
10993
|
-
UPDATE ${drizzleOrm.sql.raw(tableName)}
|
|
10994
|
-
SET
|
|
10995
|
-
name = ${data.name ?? existing.name},
|
|
10996
|
-
is_admin = ${data.isAdmin ?? existing.isAdmin},
|
|
10997
|
-
default_permissions = ${data.defaultPermissions ? JSON.stringify(data.defaultPermissions) : JSON.stringify(existing.defaultPermissions)}::jsonb,
|
|
10998
|
-
collection_permissions = ${data.collectionPermissions !== void 0 ? data.collectionPermissions ? JSON.stringify(data.collectionPermissions) : null : existing.collectionPermissions ? JSON.stringify(existing.collectionPermissions) : null}::jsonb
|
|
10999
|
-
WHERE id = ${id}
|
|
11000
|
-
`);
|
|
11001
|
-
return this.getRoleById(id);
|
|
11002
|
-
}
|
|
11003
|
-
async deleteRole(id) {
|
|
11004
|
-
const tableName = this.getQualifiedRolesTableName();
|
|
11005
|
-
await this.db.execute(drizzleOrm.sql`DELETE FROM ${drizzleOrm.sql.raw(tableName)} WHERE id = ${id}`);
|
|
11006
|
-
}
|
|
11007
10913
|
}
|
|
11008
10914
|
class RefreshTokenService {
|
|
11009
10915
|
constructor(db, tableOrTables) {
|
|
@@ -11199,11 +11105,9 @@ ${tableRelations.join(",\n")}
|
|
|
11199
11105
|
constructor(db, tableOrTables) {
|
|
11200
11106
|
this.db = db;
|
|
11201
11107
|
this.userService = new UserService(db, tableOrTables);
|
|
11202
|
-
this.roleService = new RoleService(db, tableOrTables);
|
|
11203
11108
|
this.tokenRepository = new PostgresTokenRepository(db, tableOrTables);
|
|
11204
11109
|
}
|
|
11205
11110
|
userService;
|
|
11206
|
-
roleService;
|
|
11207
11111
|
tokenRepository;
|
|
11208
11112
|
// User operations (delegate to UserService)
|
|
11209
11113
|
async createUser(data) {
|
|
@@ -11263,25 +11167,56 @@ ${tableRelations.join(",\n")}
|
|
|
11263
11167
|
async getUserWithRoles(userId) {
|
|
11264
11168
|
return this.userService.getUserWithRoles(userId);
|
|
11265
11169
|
}
|
|
11266
|
-
// Role operations (
|
|
11170
|
+
// Role operations (roles are inline on users, synthesized from string IDs)
|
|
11267
11171
|
async getRoleById(id) {
|
|
11268
|
-
return
|
|
11172
|
+
return {
|
|
11173
|
+
id,
|
|
11174
|
+
name: id,
|
|
11175
|
+
isAdmin: id === "admin",
|
|
11176
|
+
defaultPermissions: null,
|
|
11177
|
+
collectionPermissions: null
|
|
11178
|
+
};
|
|
11269
11179
|
}
|
|
11270
11180
|
async listRoles() {
|
|
11271
|
-
return
|
|
11181
|
+
return [{
|
|
11182
|
+
id: "admin",
|
|
11183
|
+
name: "Admin",
|
|
11184
|
+
isAdmin: true,
|
|
11185
|
+
defaultPermissions: null,
|
|
11186
|
+
collectionPermissions: null
|
|
11187
|
+
}, {
|
|
11188
|
+
id: "editor",
|
|
11189
|
+
name: "Editor",
|
|
11190
|
+
isAdmin: false,
|
|
11191
|
+
defaultPermissions: null,
|
|
11192
|
+
collectionPermissions: null
|
|
11193
|
+
}, {
|
|
11194
|
+
id: "viewer",
|
|
11195
|
+
name: "Viewer",
|
|
11196
|
+
isAdmin: false,
|
|
11197
|
+
defaultPermissions: null,
|
|
11198
|
+
collectionPermissions: null
|
|
11199
|
+
}];
|
|
11200
|
+
}
|
|
11201
|
+
async createRole(_data) {
|
|
11202
|
+
return {
|
|
11203
|
+
id: _data.id,
|
|
11204
|
+
name: _data.name,
|
|
11205
|
+
isAdmin: _data.isAdmin ?? false,
|
|
11206
|
+
defaultPermissions: _data.defaultPermissions ?? null,
|
|
11207
|
+
collectionPermissions: _data.collectionPermissions ?? null
|
|
11208
|
+
};
|
|
11272
11209
|
}
|
|
11273
|
-
async
|
|
11274
|
-
return
|
|
11275
|
-
|
|
11210
|
+
async updateRole(id, data) {
|
|
11211
|
+
return {
|
|
11212
|
+
id,
|
|
11213
|
+
name: data.name ?? id,
|
|
11214
|
+
isAdmin: data.isAdmin ?? id === "admin",
|
|
11276
11215
|
defaultPermissions: data.defaultPermissions ?? null,
|
|
11277
11216
|
collectionPermissions: data.collectionPermissions ?? null
|
|
11278
|
-
}
|
|
11279
|
-
}
|
|
11280
|
-
async updateRole(id, data) {
|
|
11281
|
-
return this.roleService.updateRole(id, data);
|
|
11217
|
+
};
|
|
11282
11218
|
}
|
|
11283
|
-
async deleteRole(
|
|
11284
|
-
await this.roleService.deleteRole(id);
|
|
11219
|
+
async deleteRole(_id) {
|
|
11285
11220
|
}
|
|
11286
11221
|
// Token operations (delegate to PostgresTokenRepository)
|
|
11287
11222
|
async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
|
|
@@ -11837,28 +11772,19 @@ ${tableRelations.join(",\n")}
|
|
|
11837
11772
|
emailService = serverCore.createEmailService(authConfig.email);
|
|
11838
11773
|
}
|
|
11839
11774
|
const customUsersTable = registry?.getTable("users");
|
|
11840
|
-
const customRolesTable = registry?.getTable("roles");
|
|
11841
11775
|
let usersSchemaName = "rebase";
|
|
11842
|
-
let rolesSchemaName = "rebase";
|
|
11843
11776
|
if (customUsersTable) {
|
|
11844
11777
|
usersSchemaName = pgCore.getTableConfig(customUsersTable).schema || "public";
|
|
11845
11778
|
}
|
|
11846
|
-
|
|
11847
|
-
rolesSchemaName = pgCore.getTableConfig(customRolesTable).schema || "public";
|
|
11848
|
-
}
|
|
11849
|
-
const authTables = createAuthSchema(rolesSchemaName, usersSchemaName);
|
|
11779
|
+
const authTables = createAuthSchema(usersSchemaName);
|
|
11850
11780
|
if (customUsersTable) {
|
|
11851
11781
|
authTables.users = customUsersTable;
|
|
11852
11782
|
}
|
|
11853
|
-
if (customRolesTable) {
|
|
11854
|
-
authTables.roles = customRolesTable;
|
|
11855
|
-
}
|
|
11856
11783
|
const userService = new UserService(db, authTables);
|
|
11857
|
-
const roleService = new RoleService(db, authTables);
|
|
11858
11784
|
const authRepository = new PostgresAuthRepository(db, authTables);
|
|
11859
11785
|
return {
|
|
11860
11786
|
userService,
|
|
11861
|
-
roleService,
|
|
11787
|
+
roleService: userService,
|
|
11862
11788
|
emailService,
|
|
11863
11789
|
authRepository
|
|
11864
11790
|
};
|
|
@@ -11962,17 +11888,12 @@ ${tableRelations.join(",\n")}
|
|
|
11962
11888
|
exports2.mfaFactorsRelations = mfaFactorsRelations;
|
|
11963
11889
|
exports2.passwordResetTokens = passwordResetTokens;
|
|
11964
11890
|
exports2.passwordResetTokensRelations = passwordResetTokensRelations;
|
|
11965
|
-
exports2.rebaseSchema = rebaseSchema;
|
|
11966
11891
|
exports2.recoveryCodes = recoveryCodes;
|
|
11967
11892
|
exports2.recoveryCodesRelations = recoveryCodesRelations;
|
|
11968
11893
|
exports2.refreshTokens = refreshTokens;
|
|
11969
11894
|
exports2.refreshTokensRelations = refreshTokensRelations;
|
|
11970
|
-
exports2.roles = roles;
|
|
11971
|
-
exports2.rolesRelations = rolesRelations;
|
|
11972
11895
|
exports2.userIdentities = userIdentities;
|
|
11973
11896
|
exports2.userIdentitiesRelations = userIdentitiesRelations;
|
|
11974
|
-
exports2.userRoles = userRoles;
|
|
11975
|
-
exports2.userRolesRelations = userRolesRelations;
|
|
11976
11897
|
exports2.users = users;
|
|
11977
11898
|
exports2.usersRelations = usersRelations;
|
|
11978
11899
|
exports2.usersSchema = usersSchema;
|