@rebasepro/server-postgresql 0.3.0 → 0.4.0
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/common/src/data/query_builder.d.ts +6 -2
- package/dist/index.es.js +301 -500
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +297 -496
- package/dist/index.umd.js.map +1 -1
- package/dist/server-postgresql/src/PostgresBackendDriver.d.ts +2 -0
- package/dist/server-postgresql/src/auth/ensure-tables.d.ts +7 -4
- 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/EntityFetchService.d.ts +2 -1
- package/dist/server-postgresql/src/services/EntityPersistService.d.ts +4 -0
- package/dist/server-postgresql/src/services/entityService.d.ts +4 -0
- package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +5 -1
- 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 +21 -3
- package/dist/types/src/controllers/data_driver.d.ts +5 -0
- package/dist/types/src/controllers/email.d.ts +2 -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/collections.d.ts +9 -5
- package/dist/types/src/types/entity_views.d.ts +19 -28
- package/dist/types/src/types/properties.d.ts +9 -7
- 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 +25 -21
- package/src/auth/ensure-tables.ts +82 -129
- 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/generate-drizzle-schema.ts +2 -6
- package/src/schema/introspect-db-logic.ts +7 -0
- package/src/services/EntityFetchService.ts +13 -1
- package/src/services/EntityPersistService.ts +9 -0
- package/src/services/entityService.ts +7 -0
- package/src/utils/drizzle-conditions.ts +40 -5
- package/src/websocket.ts +1 -3
- package/test/auth-services.test.ts +7 -150
- package/test/doctor.test.ts +6 -2
- package/test/relation-pipeline-gaps.test.ts +315 -0
- 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
|
-
import {
|
|
4
|
-
import { PgVarchar, PgText, PgChar, pgSchema, pgTable, timestamp, jsonb, boolean, varchar, uuid,
|
|
3
|
+
import { or, and, sql, inArray, eq, ilike, asc, desc, gt, lt, getTableName as getTableName$1, count, relations, isTable } from "drizzle-orm";
|
|
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";
|
|
@@ -2711,114 +2711,6 @@ class CollectionRegistry {
|
|
|
2711
2711
|
};
|
|
2712
2712
|
}
|
|
2713
2713
|
}
|
|
2714
|
-
const defaultUsersCollection = {
|
|
2715
|
-
name: "Users",
|
|
2716
|
-
singularName: "User",
|
|
2717
|
-
slug: "users",
|
|
2718
|
-
table: "users",
|
|
2719
|
-
schema: "rebase",
|
|
2720
|
-
icon: "Users",
|
|
2721
|
-
group: "Settings",
|
|
2722
|
-
properties: {
|
|
2723
|
-
id: {
|
|
2724
|
-
name: "ID",
|
|
2725
|
-
type: "string",
|
|
2726
|
-
isId: "uuid"
|
|
2727
|
-
},
|
|
2728
|
-
email: {
|
|
2729
|
-
name: "Email",
|
|
2730
|
-
type: "string",
|
|
2731
|
-
validation: {
|
|
2732
|
-
required: true,
|
|
2733
|
-
unique: true
|
|
2734
|
-
}
|
|
2735
|
-
},
|
|
2736
|
-
password_hash: {
|
|
2737
|
-
name: "Password Hash",
|
|
2738
|
-
type: "string",
|
|
2739
|
-
ui: {
|
|
2740
|
-
hideFromCollection: true
|
|
2741
|
-
}
|
|
2742
|
-
},
|
|
2743
|
-
display_name: {
|
|
2744
|
-
name: "Display Name",
|
|
2745
|
-
type: "string"
|
|
2746
|
-
},
|
|
2747
|
-
photo_url: {
|
|
2748
|
-
name: "Photo URL",
|
|
2749
|
-
type: "string"
|
|
2750
|
-
},
|
|
2751
|
-
email_verified: {
|
|
2752
|
-
name: "Email Verified",
|
|
2753
|
-
type: "boolean",
|
|
2754
|
-
defaultValue: false
|
|
2755
|
-
},
|
|
2756
|
-
email_verification_token: {
|
|
2757
|
-
name: "Email Verification Token",
|
|
2758
|
-
type: "string",
|
|
2759
|
-
ui: {
|
|
2760
|
-
hideFromCollection: true
|
|
2761
|
-
}
|
|
2762
|
-
},
|
|
2763
|
-
email_verification_sent_at: {
|
|
2764
|
-
name: "Email Verification Sent At",
|
|
2765
|
-
type: "date",
|
|
2766
|
-
ui: {
|
|
2767
|
-
hideFromCollection: true
|
|
2768
|
-
}
|
|
2769
|
-
},
|
|
2770
|
-
metadata: {
|
|
2771
|
-
name: "Metadata",
|
|
2772
|
-
type: "map",
|
|
2773
|
-
defaultValue: {},
|
|
2774
|
-
ui: {
|
|
2775
|
-
hideFromCollection: true
|
|
2776
|
-
}
|
|
2777
|
-
},
|
|
2778
|
-
created_at: {
|
|
2779
|
-
name: "Created At",
|
|
2780
|
-
type: "date",
|
|
2781
|
-
autoValue: "on_create",
|
|
2782
|
-
ui: {
|
|
2783
|
-
readOnly: true,
|
|
2784
|
-
hideFromCollection: true
|
|
2785
|
-
}
|
|
2786
|
-
},
|
|
2787
|
-
updated_at: {
|
|
2788
|
-
name: "Updated At",
|
|
2789
|
-
type: "date",
|
|
2790
|
-
autoValue: "on_update",
|
|
2791
|
-
ui: {
|
|
2792
|
-
readOnly: true,
|
|
2793
|
-
hideFromCollection: true
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
};
|
|
2798
|
-
function mapOperator(op) {
|
|
2799
|
-
switch (op) {
|
|
2800
|
-
case "==":
|
|
2801
|
-
return "eq";
|
|
2802
|
-
case "!=":
|
|
2803
|
-
return "neq";
|
|
2804
|
-
case ">":
|
|
2805
|
-
return "gt";
|
|
2806
|
-
case ">=":
|
|
2807
|
-
return "gte";
|
|
2808
|
-
case "<":
|
|
2809
|
-
return "lt";
|
|
2810
|
-
case "<=":
|
|
2811
|
-
return "lte";
|
|
2812
|
-
case "array-contains":
|
|
2813
|
-
return "cs";
|
|
2814
|
-
case "array-contains-any":
|
|
2815
|
-
return "csa";
|
|
2816
|
-
case "not-in":
|
|
2817
|
-
return "nin";
|
|
2818
|
-
default:
|
|
2819
|
-
return op;
|
|
2820
|
-
}
|
|
2821
|
-
}
|
|
2822
2714
|
class QueryBuilder {
|
|
2823
2715
|
constructor(collection) {
|
|
2824
2716
|
this.collection = collection;
|
|
@@ -2826,23 +2718,30 @@ class QueryBuilder {
|
|
|
2826
2718
|
params = {
|
|
2827
2719
|
where: {}
|
|
2828
2720
|
};
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
where(column, operator, value) {
|
|
2721
|
+
where(columnOrCondition, operator, value) {
|
|
2722
|
+
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
2723
|
+
this.params.logical = columnOrCondition;
|
|
2724
|
+
return this;
|
|
2725
|
+
}
|
|
2835
2726
|
if (!this.params.where) {
|
|
2836
2727
|
this.params.where = {};
|
|
2837
2728
|
}
|
|
2838
|
-
const
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2729
|
+
const column = columnOrCondition;
|
|
2730
|
+
const condition = [operator, value];
|
|
2731
|
+
const existing = this.params.where[column];
|
|
2732
|
+
if (existing === void 0) {
|
|
2733
|
+
this.params.where[column] = condition;
|
|
2734
|
+
} else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
|
|
2735
|
+
this.params.where[column].push(condition);
|
|
2736
|
+
} else {
|
|
2737
|
+
let firstCondition;
|
|
2738
|
+
if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
|
|
2739
|
+
firstCondition = existing;
|
|
2740
|
+
} else {
|
|
2741
|
+
firstCondition = ["==", existing];
|
|
2742
|
+
}
|
|
2743
|
+
this.params.where[column] = [firstCondition, condition];
|
|
2844
2744
|
}
|
|
2845
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2846
2745
|
return this;
|
|
2847
2746
|
}
|
|
2848
2747
|
/**
|
|
@@ -2944,10 +2843,13 @@ function convertWhereToFilter(where) {
|
|
|
2944
2843
|
filter[field] = ["==", rawValue];
|
|
2945
2844
|
continue;
|
|
2946
2845
|
}
|
|
2947
|
-
if (Array.isArray(rawValue)
|
|
2948
|
-
const [
|
|
2949
|
-
const
|
|
2950
|
-
|
|
2846
|
+
if (Array.isArray(rawValue)) {
|
|
2847
|
+
const conditions = Array.isArray(rawValue[0]) ? rawValue : [rawValue];
|
|
2848
|
+
const mappedConditions = conditions.map(([rawOp, val]) => {
|
|
2849
|
+
const mappedOp = operatorMap[rawOp] ?? "==";
|
|
2850
|
+
return [mappedOp, val];
|
|
2851
|
+
});
|
|
2852
|
+
filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
|
|
2951
2853
|
continue;
|
|
2952
2854
|
}
|
|
2953
2855
|
if (typeof rawValue === "string") {
|
|
@@ -3041,6 +2943,9 @@ function createDriverAccessor(driver, slug) {
|
|
|
3041
2943
|
}
|
|
3042
2944
|
});
|
|
3043
2945
|
},
|
|
2946
|
+
deleteAll: driver.deleteAll ? async () => {
|
|
2947
|
+
return driver.deleteAll(slug);
|
|
2948
|
+
} : void 0,
|
|
3044
2949
|
count: driver.countEntities ? async (params) => {
|
|
3045
2950
|
return driver.countEntities({
|
|
3046
2951
|
path: slug,
|
|
@@ -3082,8 +2987,12 @@ function createDriverAccessor(driver, slug) {
|
|
|
3082
2987
|
});
|
|
3083
2988
|
} : void 0,
|
|
3084
2989
|
// Fluent Query Builder
|
|
3085
|
-
where(
|
|
3086
|
-
|
|
2990
|
+
where(columnOrCondition, operator, value) {
|
|
2991
|
+
const builder = new QueryBuilder(accessor);
|
|
2992
|
+
if (typeof columnOrCondition === "object") {
|
|
2993
|
+
return builder.where(columnOrCondition);
|
|
2994
|
+
}
|
|
2995
|
+
return builder.where(columnOrCondition, operator, value);
|
|
3087
2996
|
},
|
|
3088
2997
|
orderBy(column, ascending) {
|
|
3089
2998
|
return new QueryBuilder(accessor).orderBy(column, ascending);
|
|
@@ -3134,7 +3043,6 @@ class DrizzleConditionBuilder {
|
|
|
3134
3043
|
const conditions = [];
|
|
3135
3044
|
for (const [field, filterParam] of Object.entries(filter)) {
|
|
3136
3045
|
if (!filterParam) continue;
|
|
3137
|
-
const [op, value] = filterParam;
|
|
3138
3046
|
let fieldColumn = table[field];
|
|
3139
3047
|
if (!fieldColumn) {
|
|
3140
3048
|
const relationKey = `${field}_id`;
|
|
@@ -3146,13 +3054,39 @@ class DrizzleConditionBuilder {
|
|
|
3146
3054
|
console.warn(`Filtering by field '${field}', but it does not exist in table for collection '${collectionPath}'`);
|
|
3147
3055
|
continue;
|
|
3148
3056
|
}
|
|
3149
|
-
const
|
|
3150
|
-
|
|
3151
|
-
|
|
3057
|
+
const paramsList = Array.isArray(filterParam) && filterParam.length > 0 && Array.isArray(filterParam[0]) ? filterParam : [filterParam];
|
|
3058
|
+
for (const [op, value] of paramsList) {
|
|
3059
|
+
const condition = this.buildSingleFilterCondition(fieldColumn, op, value);
|
|
3060
|
+
if (condition) {
|
|
3061
|
+
conditions.push(condition);
|
|
3062
|
+
}
|
|
3152
3063
|
}
|
|
3153
3064
|
}
|
|
3154
3065
|
return conditions;
|
|
3155
3066
|
}
|
|
3067
|
+
/**
|
|
3068
|
+
* Build logical conditions recursively from LogicalCondition or FilterCondition
|
|
3069
|
+
*/
|
|
3070
|
+
static buildLogicalConditions(cond, table, collectionPath) {
|
|
3071
|
+
if ("type" in cond) {
|
|
3072
|
+
const subSQLs = cond.conditions.map((c) => this.buildLogicalConditions(c, table, collectionPath)).filter((sql2) => sql2 !== null);
|
|
3073
|
+
if (subSQLs.length === 0) return null;
|
|
3074
|
+
return (cond.type === "or" ? or(...subSQLs) : and(...subSQLs)) ?? null;
|
|
3075
|
+
} else {
|
|
3076
|
+
let fieldColumn = table[cond.column];
|
|
3077
|
+
if (!fieldColumn) {
|
|
3078
|
+
const relationKey = `${cond.column}_id`;
|
|
3079
|
+
if (relationKey in table) {
|
|
3080
|
+
fieldColumn = table[relationKey];
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
if (!fieldColumn) {
|
|
3084
|
+
console.warn(`Filtering by field '${cond.column}', but it does not exist in table for collection '${collectionPath}'`);
|
|
3085
|
+
return null;
|
|
3086
|
+
}
|
|
3087
|
+
return this.buildSingleFilterCondition(fieldColumn, cond.operator, cond.value);
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3156
3090
|
/**
|
|
3157
3091
|
* Build a single filter condition for a specific operator and value
|
|
3158
3092
|
*/
|
|
@@ -5549,6 +5483,10 @@ class EntityFetchService {
|
|
|
5549
5483
|
const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
|
|
5550
5484
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
5551
5485
|
}
|
|
5486
|
+
if (options.logical) {
|
|
5487
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
|
|
5488
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
5489
|
+
}
|
|
5552
5490
|
if (options.startAfter) {
|
|
5553
5491
|
const cursorConditions = this.buildCursorConditions(table, idField, idInfo, options, collectionPath);
|
|
5554
5492
|
if (cursorConditions.length > 0) allConditions.push(...cursorConditions);
|
|
@@ -5720,6 +5658,10 @@ class EntityFetchService {
|
|
|
5720
5658
|
const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
|
|
5721
5659
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
5722
5660
|
}
|
|
5661
|
+
if (options.logical) {
|
|
5662
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
|
|
5663
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
5664
|
+
}
|
|
5723
5665
|
if (vectorMeta?.filter) {
|
|
5724
5666
|
allConditions.push(vectorMeta.filter);
|
|
5725
5667
|
}
|
|
@@ -6305,6 +6247,14 @@ class EntityPersistService {
|
|
|
6305
6247
|
const parsedId = parsedIdObj[idInfo.fieldName];
|
|
6306
6248
|
await this.db.delete(table).where(eq(idField, parsedId));
|
|
6307
6249
|
}
|
|
6250
|
+
/**
|
|
6251
|
+
* Delete all entities from a collection
|
|
6252
|
+
*/
|
|
6253
|
+
async deleteAll(collectionPath, _databaseId) {
|
|
6254
|
+
const collection = getCollectionByPath(collectionPath, this.registry);
|
|
6255
|
+
const table = getTableForCollection(collection, this.registry);
|
|
6256
|
+
await this.db.delete(table);
|
|
6257
|
+
}
|
|
6308
6258
|
/**
|
|
6309
6259
|
* Save an entity (create or update)
|
|
6310
6260
|
*/
|
|
@@ -6636,6 +6586,12 @@ class EntityService {
|
|
|
6636
6586
|
async deleteEntity(collectionPath, entityId, databaseId) {
|
|
6637
6587
|
return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
|
|
6638
6588
|
}
|
|
6589
|
+
/**
|
|
6590
|
+
* Delete all entities from a collection
|
|
6591
|
+
*/
|
|
6592
|
+
async deleteAll(collectionPath, databaseId) {
|
|
6593
|
+
return this.persistService.deleteAll(collectionPath, databaseId);
|
|
6594
|
+
}
|
|
6639
6595
|
/**
|
|
6640
6596
|
* Execute raw SQL
|
|
6641
6597
|
*/
|
|
@@ -7336,6 +7292,10 @@ class PostgresBackendDriver {
|
|
|
7336
7292
|
await this.realtimeService.notifyEntityUpdate(entity.path, entity.id.toString(), null, entity.databaseId || resolvedCollection?.databaseId);
|
|
7337
7293
|
}
|
|
7338
7294
|
}
|
|
7295
|
+
async deleteAll(path2) {
|
|
7296
|
+
await this.entityService.deleteAll(path2);
|
|
7297
|
+
await this.realtimeService.notifyEntityUpdate(path2, "*", null);
|
|
7298
|
+
}
|
|
7339
7299
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7340
7300
|
return this.entityService.checkUniqueField(path2, name, value, entityId, collection?.databaseId);
|
|
7341
7301
|
}
|
|
@@ -7605,11 +7565,11 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7605
7565
|
console.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object:", this.user);
|
|
7606
7566
|
userId = "anonymous";
|
|
7607
7567
|
}
|
|
7608
|
-
const
|
|
7568
|
+
const userRoles = this.user?.roles ?? [];
|
|
7609
7569
|
if (!this.user?.roles) {
|
|
7610
7570
|
console.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object:", this.user);
|
|
7611
7571
|
}
|
|
7612
|
-
const normalizedRoles =
|
|
7572
|
+
const normalizedRoles = userRoles.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
|
|
7613
7573
|
const rolesString = normalizedRoles.join(",");
|
|
7614
7574
|
await tx.execute(sql`
|
|
7615
7575
|
SELECT
|
|
@@ -7617,7 +7577,7 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7617
7577
|
set_config('app.user_roles', ${rolesString}, true),
|
|
7618
7578
|
set_config('app.jwt', ${JSON.stringify({
|
|
7619
7579
|
sub: userId,
|
|
7620
|
-
roles:
|
|
7580
|
+
roles: userRoles
|
|
7621
7581
|
})}, true)
|
|
7622
7582
|
`);
|
|
7623
7583
|
const txEntityService = new EntityService(tx, this.delegate.registry);
|
|
@@ -7672,6 +7632,9 @@ class AuthenticatedPostgresBackendDriver {
|
|
|
7672
7632
|
async deleteEntity(props) {
|
|
7673
7633
|
return this.withTransaction((delegate) => delegate.deleteEntity(props));
|
|
7674
7634
|
}
|
|
7635
|
+
async deleteAll(path2) {
|
|
7636
|
+
return this.delegate.deleteAll(path2);
|
|
7637
|
+
}
|
|
7675
7638
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7676
7639
|
return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
|
|
7677
7640
|
}
|
|
@@ -7751,11 +7714,10 @@ class DatabasePoolManager {
|
|
|
7751
7714
|
this.pools.clear();
|
|
7752
7715
|
}
|
|
7753
7716
|
}
|
|
7754
|
-
function createAuthSchema(
|
|
7755
|
-
const rolesSchema = rolesSchemaName === "public" ? null : pgSchema(rolesSchemaName);
|
|
7717
|
+
function createAuthSchema(usersSchemaName = "rebase") {
|
|
7756
7718
|
const usersSchema2 = usersSchemaName === "public" ? null : pgSchema(usersSchemaName);
|
|
7757
|
-
const
|
|
7758
|
-
const usersTableCreator =
|
|
7719
|
+
const tableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgTable;
|
|
7720
|
+
const usersTableCreator = tableCreator;
|
|
7759
7721
|
const users2 = usersTableCreator("users", {
|
|
7760
7722
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7761
7723
|
email: varchar("email", {
|
|
@@ -7777,37 +7739,12 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7777
7739
|
}),
|
|
7778
7740
|
emailVerificationSentAt: timestamp("email_verification_sent_at"),
|
|
7779
7741
|
isAnonymous: boolean("is_anonymous").default(false).notNull(),
|
|
7742
|
+
roles: text("roles").array().default([]).notNull(),
|
|
7780
7743
|
metadata: jsonb("metadata").$type().default({}).notNull(),
|
|
7781
7744
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
7782
7745
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7783
7746
|
});
|
|
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", {
|
|
7747
|
+
const refreshTokens2 = tableCreator("refresh_tokens", {
|
|
7811
7748
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7812
7749
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7813
7750
|
onDelete: "cascade"
|
|
@@ -7826,7 +7763,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7826
7763
|
}, (table) => ({
|
|
7827
7764
|
uniqueDeviceSession: unique("unique_device_session").on(table.userId, table.userAgent, table.ipAddress)
|
|
7828
7765
|
}));
|
|
7829
|
-
const passwordResetTokens2 =
|
|
7766
|
+
const passwordResetTokens2 = tableCreator("password_reset_tokens", {
|
|
7830
7767
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7831
7768
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7832
7769
|
onDelete: "cascade"
|
|
@@ -7838,14 +7775,14 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7838
7775
|
usedAt: timestamp("used_at"),
|
|
7839
7776
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7840
7777
|
});
|
|
7841
|
-
const appConfig2 =
|
|
7778
|
+
const appConfig2 = tableCreator("app_config", {
|
|
7842
7779
|
key: varchar("key", {
|
|
7843
7780
|
length: 100
|
|
7844
7781
|
}).primaryKey(),
|
|
7845
7782
|
value: jsonb("value").notNull(),
|
|
7846
7783
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7847
7784
|
});
|
|
7848
|
-
const userIdentities2 =
|
|
7785
|
+
const userIdentities2 = tableCreator("user_identities", {
|
|
7849
7786
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7850
7787
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7851
7788
|
onDelete: "cascade"
|
|
@@ -7863,7 +7800,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7863
7800
|
}, (table) => ({
|
|
7864
7801
|
uniqueProviderId: unique("unique_provider_id").on(table.provider, table.providerId)
|
|
7865
7802
|
}));
|
|
7866
|
-
const mfaFactors2 =
|
|
7803
|
+
const mfaFactors2 = tableCreator("mfa_factors", {
|
|
7867
7804
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7868
7805
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7869
7806
|
onDelete: "cascade"
|
|
@@ -7882,7 +7819,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7882
7819
|
createdAt: timestamp("created_at").defaultNow().notNull(),
|
|
7883
7820
|
updatedAt: timestamp("updated_at").defaultNow().notNull()
|
|
7884
7821
|
});
|
|
7885
|
-
const mfaChallenges2 =
|
|
7822
|
+
const mfaChallenges2 = tableCreator("mfa_challenges", {
|
|
7886
7823
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7887
7824
|
factorId: uuid("factor_id").notNull().references(() => mfaFactors2.id, {
|
|
7888
7825
|
onDelete: "cascade"
|
|
@@ -7894,7 +7831,7 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7894
7831
|
}),
|
|
7895
7832
|
expiresAt: timestamp("expires_at").notNull()
|
|
7896
7833
|
});
|
|
7897
|
-
const recoveryCodes2 =
|
|
7834
|
+
const recoveryCodes2 = tableCreator("recovery_codes", {
|
|
7898
7835
|
id: uuid("id").defaultRandom().primaryKey(),
|
|
7899
7836
|
userId: uuid("user_id").notNull().references(() => users2.id, {
|
|
7900
7837
|
onDelete: "cascade"
|
|
@@ -7906,11 +7843,8 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7906
7843
|
createdAt: timestamp("created_at").defaultNow().notNull()
|
|
7907
7844
|
});
|
|
7908
7845
|
return {
|
|
7909
|
-
rolesSchema,
|
|
7910
7846
|
usersSchema: usersSchema2,
|
|
7911
7847
|
users: users2,
|
|
7912
|
-
roles: roles2,
|
|
7913
|
-
userRoles: userRoles2,
|
|
7914
7848
|
refreshTokens: refreshTokens2,
|
|
7915
7849
|
passwordResetTokens: passwordResetTokens2,
|
|
7916
7850
|
appConfig: appConfig2,
|
|
@@ -7920,12 +7854,9 @@ function createAuthSchema(rolesSchemaName = "rebase", usersSchemaName = "rebase"
|
|
|
7920
7854
|
recoveryCodes: recoveryCodes2
|
|
7921
7855
|
};
|
|
7922
7856
|
}
|
|
7923
|
-
const defaultAuthSchema = createAuthSchema("rebase"
|
|
7924
|
-
const rebaseSchema = defaultAuthSchema.rolesSchema;
|
|
7857
|
+
const defaultAuthSchema = createAuthSchema("rebase");
|
|
7925
7858
|
const usersSchema = defaultAuthSchema.usersSchema;
|
|
7926
7859
|
const users = defaultAuthSchema.users;
|
|
7927
|
-
const roles = defaultAuthSchema.roles;
|
|
7928
|
-
const userRoles = defaultAuthSchema.userRoles;
|
|
7929
7860
|
const refreshTokens = defaultAuthSchema.refreshTokens;
|
|
7930
7861
|
const passwordResetTokens = defaultAuthSchema.passwordResetTokens;
|
|
7931
7862
|
const appConfig = defaultAuthSchema.appConfig;
|
|
@@ -7936,30 +7867,12 @@ const recoveryCodes = defaultAuthSchema.recoveryCodes;
|
|
|
7936
7867
|
const usersRelations = relations(users, ({
|
|
7937
7868
|
many
|
|
7938
7869
|
}) => ({
|
|
7939
|
-
userRoles: many(userRoles),
|
|
7940
7870
|
refreshTokens: many(refreshTokens),
|
|
7941
7871
|
passwordResetTokens: many(passwordResetTokens),
|
|
7942
7872
|
userIdentities: many(userIdentities),
|
|
7943
7873
|
mfaFactors: many(mfaFactors),
|
|
7944
7874
|
recoveryCodes: many(recoveryCodes)
|
|
7945
7875
|
}));
|
|
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
7876
|
const refreshTokensRelations = relations(refreshTokens, ({
|
|
7964
7877
|
one
|
|
7965
7878
|
}) => ({
|
|
@@ -8066,6 +7979,8 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
8066
7979
|
columnDefinition = `${enumName}("${colName}")`;
|
|
8067
7980
|
} else if ("isId" in stringProp && stringProp.isId === "uuid") {
|
|
8068
7981
|
columnDefinition = `uuid("${colName}")`;
|
|
7982
|
+
} else if (stringProp.columnType === "uuid") {
|
|
7983
|
+
columnDefinition = `uuid("${colName}")`;
|
|
8069
7984
|
} else if (stringProp.columnType === "text") {
|
|
8070
7985
|
columnDefinition = `text("${colName}")`;
|
|
8071
7986
|
} else if (stringProp.columnType === "char") {
|
|
@@ -8133,11 +8048,38 @@ const getDrizzleColumn = (propName, prop, collection, collections) => {
|
|
|
8133
8048
|
}
|
|
8134
8049
|
break;
|
|
8135
8050
|
}
|
|
8136
|
-
case "map":
|
|
8051
|
+
case "map": {
|
|
8052
|
+
const mapProp = prop;
|
|
8053
|
+
if (mapProp.columnType === "json") {
|
|
8054
|
+
columnDefinition = `json("${colName}")`;
|
|
8055
|
+
} else {
|
|
8056
|
+
columnDefinition = `jsonb("${colName}")`;
|
|
8057
|
+
}
|
|
8058
|
+
break;
|
|
8059
|
+
}
|
|
8137
8060
|
case "array": {
|
|
8138
|
-
const
|
|
8139
|
-
|
|
8061
|
+
const arrayProp = prop;
|
|
8062
|
+
let colType = arrayProp.columnType;
|
|
8063
|
+
if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {
|
|
8064
|
+
const ofProp = arrayProp.of;
|
|
8065
|
+
if (ofProp.type === "string") {
|
|
8066
|
+
colType = "text[]";
|
|
8067
|
+
} else if (ofProp.type === "number") {
|
|
8068
|
+
colType = ofProp.validation?.integer ? "integer[]" : "numeric[]";
|
|
8069
|
+
} else if (ofProp.type === "boolean") {
|
|
8070
|
+
colType = "boolean[]";
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
if (colType === "json") {
|
|
8140
8074
|
columnDefinition = `json("${colName}")`;
|
|
8075
|
+
} else if (colType === "text[]") {
|
|
8076
|
+
columnDefinition = `text("${colName}").array()`;
|
|
8077
|
+
} else if (colType === "integer[]") {
|
|
8078
|
+
columnDefinition = `integer("${colName}").array()`;
|
|
8079
|
+
} else if (colType === "boolean[]") {
|
|
8080
|
+
columnDefinition = `boolean("${colName}").array()`;
|
|
8081
|
+
} else if (colType === "numeric[]") {
|
|
8082
|
+
columnDefinition = `numeric("${colName}").array()`;
|
|
8141
8083
|
} else {
|
|
8142
8084
|
columnDefinition = `jsonb("${colName}")`;
|
|
8143
8085
|
}
|
|
@@ -8221,8 +8163,8 @@ const resolveRawSql = (expression) => {
|
|
|
8221
8163
|
const resolved = expression.replace(/\{(\w+)\}/g, (_, col) => col);
|
|
8222
8164
|
return `sql\`${resolved}\``;
|
|
8223
8165
|
};
|
|
8224
|
-
const wrapWithRoleCheck = (clause,
|
|
8225
|
-
const rolesArrayString = `ARRAY[${
|
|
8166
|
+
const wrapWithRoleCheck = (clause, roles) => {
|
|
8167
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8226
8168
|
const roleCondition = `string_to_array(auth.roles(), ',') @> ${rolesArrayString}`;
|
|
8227
8169
|
return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
|
|
8228
8170
|
};
|
|
@@ -8275,22 +8217,22 @@ const generatePolicyCode = (collection, rule, index) => {
|
|
|
8275
8217
|
};
|
|
8276
8218
|
const generateSinglePolicyCode = (collection, rule, operation, policyName) => {
|
|
8277
8219
|
const mode = rule.mode ?? "permissive";
|
|
8278
|
-
const
|
|
8220
|
+
const roles = rule.roles ? [...rule.roles].sort() : void 0;
|
|
8279
8221
|
const needsUsing = operation !== "insert";
|
|
8280
8222
|
const needsWithCheck = operation !== "select" && operation !== "delete";
|
|
8281
8223
|
let usingClause = needsUsing ? buildUsingClause(rule, collection) : null;
|
|
8282
8224
|
let withCheckClause = needsWithCheck ? buildWithCheckClause(rule, collection) : null;
|
|
8283
|
-
if (
|
|
8225
|
+
if (roles && roles.length > 0) {
|
|
8284
8226
|
if (usingClause) {
|
|
8285
|
-
usingClause = wrapWithRoleCheck(usingClause,
|
|
8227
|
+
usingClause = wrapWithRoleCheck(usingClause, roles);
|
|
8286
8228
|
} else if (needsUsing) {
|
|
8287
|
-
const rolesArrayString = `ARRAY[${
|
|
8229
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8288
8230
|
usingClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8289
8231
|
}
|
|
8290
8232
|
if (withCheckClause) {
|
|
8291
|
-
withCheckClause = wrapWithRoleCheck(withCheckClause,
|
|
8233
|
+
withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
|
|
8292
8234
|
} else if (needsWithCheck) {
|
|
8293
|
-
const rolesArrayString = `ARRAY[${
|
|
8235
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8294
8236
|
withCheckClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8295
8237
|
}
|
|
8296
8238
|
}
|
|
@@ -8613,7 +8555,7 @@ ${tableRelations.join(",\n")}
|
|
|
8613
8555
|
schemaContent += tablesExport + enumsExport + relationsExport;
|
|
8614
8556
|
return schemaContent;
|
|
8615
8557
|
};
|
|
8616
|
-
const formatTerminalText = (
|
|
8558
|
+
const formatTerminalText = (text2, options = {}) => {
|
|
8617
8559
|
let codes = "";
|
|
8618
8560
|
if (options.bold) codes += "\x1B[1m";
|
|
8619
8561
|
if (options.backgroundColor) {
|
|
@@ -8640,7 +8582,7 @@ const formatTerminalText = (text, options = {}) => {
|
|
|
8640
8582
|
};
|
|
8641
8583
|
codes += textColors[options.textColor];
|
|
8642
8584
|
}
|
|
8643
|
-
return `${codes}${
|
|
8585
|
+
return `${codes}${text2}\x1B[0m`;
|
|
8644
8586
|
};
|
|
8645
8587
|
const runGeneration = async (collectionsFilePath, outputPath) => {
|
|
8646
8588
|
try {
|
|
@@ -8678,7 +8620,6 @@ const runGeneration = async (collectionsFilePath, outputPath) => {
|
|
|
8678
8620
|
if (!collections || !Array.isArray(collections)) {
|
|
8679
8621
|
collections = [];
|
|
8680
8622
|
}
|
|
8681
|
-
collections = Array.from(new Map([defaultUsersCollection, ...collections].map((c) => [c.slug, c])).values());
|
|
8682
8623
|
collections.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
8683
8624
|
const schemaContent = await generateSchema(collections);
|
|
8684
8625
|
if (outputPath) {
|
|
@@ -10024,15 +9965,15 @@ function createPostgresWebSocket(server, realtimeService, driver, authConfig, au
|
|
|
10024
9965
|
wsDebug("👤 [WebSocket Server] Processing FETCH_ROLES request");
|
|
10025
9966
|
const delegate = await getScopedDelegate();
|
|
10026
9967
|
const admin = delegate.admin;
|
|
10027
|
-
let
|
|
9968
|
+
let roles = [];
|
|
10028
9969
|
if (isSQLAdmin(admin) && admin.fetchAvailableRoles) {
|
|
10029
|
-
|
|
9970
|
+
roles = await admin.fetchAvailableRoles();
|
|
10030
9971
|
}
|
|
10031
|
-
wsDebug(`👤 [WebSocket Server] Fetched ${
|
|
9972
|
+
wsDebug(`👤 [WebSocket Server] Fetched ${roles.length} roles.`);
|
|
10032
9973
|
const response = {
|
|
10033
9974
|
type: "FETCH_ROLES_SUCCESS",
|
|
10034
9975
|
payload: {
|
|
10035
|
-
roles
|
|
9976
|
+
roles
|
|
10036
9977
|
},
|
|
10037
9978
|
requestId
|
|
10038
9979
|
};
|
|
@@ -10286,85 +10227,35 @@ class PostgresCollectionRegistry extends CollectionRegistry {
|
|
|
10286
10227
|
return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
|
|
10287
10228
|
}
|
|
10288
10229
|
}
|
|
10289
|
-
|
|
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
|
-
async function ensureAuthTablesExist(db, registry) {
|
|
10230
|
+
async function ensureAuthTablesExist(db, collection) {
|
|
10321
10231
|
logger.info("🔍 Checking auth tables...");
|
|
10322
10232
|
try {
|
|
10323
|
-
let usersTableName = '"users"';
|
|
10233
|
+
let usersTableName = '"rebase"."users"';
|
|
10324
10234
|
let userIdType = "TEXT";
|
|
10325
|
-
let usersSchema2 = "
|
|
10326
|
-
if (
|
|
10327
|
-
const
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
const columnType = meta.columnType;
|
|
10338
|
-
if (columnType === "PgUUID") {
|
|
10339
|
-
userIdType = "UUID";
|
|
10340
|
-
} else if (columnType === "PgSerial" || columnType === "PgInteger") {
|
|
10341
|
-
userIdType = "INTEGER";
|
|
10342
|
-
} else if (columnType === "PgBigInt" || columnType === "PgBigSerial") {
|
|
10343
|
-
userIdType = "BIGINT";
|
|
10344
|
-
}
|
|
10235
|
+
let usersSchema2 = "rebase";
|
|
10236
|
+
if (collection) {
|
|
10237
|
+
const rawTable = "table" in collection && typeof collection.table === "string" ? collection.table : collection.slug;
|
|
10238
|
+
usersSchema2 = "schema" in collection && typeof collection.schema === "string" ? collection.schema : "public";
|
|
10239
|
+
usersTableName = usersSchema2 === "public" ? `"${rawTable}"` : `"${usersSchema2}"."${rawTable}"`;
|
|
10240
|
+
const idProp = collection.properties?.id;
|
|
10241
|
+
if (idProp) {
|
|
10242
|
+
const isId = "isId" in idProp ? idProp.isId : void 0;
|
|
10243
|
+
if (isId === "uuid") {
|
|
10244
|
+
userIdType = "UUID";
|
|
10245
|
+
} else if (isId === "autoincrement") {
|
|
10246
|
+
userIdType = "INTEGER";
|
|
10345
10247
|
}
|
|
10346
10248
|
}
|
|
10347
10249
|
}
|
|
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
10250
|
if (usersSchema2 !== "public") {
|
|
10356
10251
|
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(usersSchema2)}`);
|
|
10357
10252
|
}
|
|
10358
|
-
if (rolesSchema !== "public" && rolesSchema !== usersSchema2) {
|
|
10359
|
-
await db.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.raw(rolesSchema)}`);
|
|
10360
|
-
}
|
|
10361
10253
|
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"`;
|
|
10254
|
+
const authSchema = usersSchema2 === "public" ? "rebase" : usersSchema2;
|
|
10255
|
+
const userIdentitiesTable = `"${authSchema}"."user_identities"`;
|
|
10256
|
+
const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
|
|
10257
|
+
const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
|
|
10258
|
+
const appConfigTableName = `"${authSchema}"."app_config"`;
|
|
10368
10259
|
await db.execute(sql`
|
|
10369
10260
|
CREATE TABLE IF NOT EXISTS ${sql.raw(userIdentitiesTable)} (
|
|
10370
10261
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10381,27 +10272,6 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10381
10272
|
CREATE INDEX IF NOT EXISTS idx_user_identities_user
|
|
10382
10273
|
ON ${sql.raw(userIdentitiesTable)}(user_id)
|
|
10383
10274
|
`);
|
|
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
10275
|
await db.execute(sql`
|
|
10406
10276
|
CREATE TABLE IF NOT EXISTS ${sql.raw(refreshTokensTableName)} (
|
|
10407
10277
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10469,14 +10339,43 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10469
10339
|
$$ LANGUAGE sql STABLE
|
|
10470
10340
|
`);
|
|
10471
10341
|
});
|
|
10472
|
-
await seedDefaultRoles(db, rolesTableName);
|
|
10473
10342
|
await db.execute(sql`
|
|
10474
10343
|
ALTER TABLE ${sql.raw(usersTableName)}
|
|
10475
10344
|
ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
|
|
10476
10345
|
`);
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10346
|
+
await db.execute(sql`
|
|
10347
|
+
ALTER TABLE ${sql.raw(usersTableName)}
|
|
10348
|
+
ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
|
|
10349
|
+
`);
|
|
10350
|
+
try {
|
|
10351
|
+
const legacyCheck = await db.execute(sql`
|
|
10352
|
+
SELECT EXISTS (
|
|
10353
|
+
SELECT 1 FROM information_schema.tables
|
|
10354
|
+
WHERE table_schema = 'rebase' AND table_name = 'user_roles'
|
|
10355
|
+
) AS has_user_roles
|
|
10356
|
+
`);
|
|
10357
|
+
const hasLegacyTables = legacyCheck.rows[0].has_user_roles;
|
|
10358
|
+
if (hasLegacyTables) {
|
|
10359
|
+
logger.info("🔄 Migrating roles from legacy user_roles table...");
|
|
10360
|
+
await db.execute(sql`
|
|
10361
|
+
UPDATE ${sql.raw(usersTableName)} u
|
|
10362
|
+
SET roles = COALESCE((
|
|
10363
|
+
SELECT array_agg(ur.role_id)
|
|
10364
|
+
FROM "rebase"."user_roles" ur
|
|
10365
|
+
WHERE ur.user_id = u.id
|
|
10366
|
+
), '{}')
|
|
10367
|
+
WHERE u.roles = '{}' OR u.roles IS NULL
|
|
10368
|
+
`);
|
|
10369
|
+
await db.execute(sql`DROP TABLE IF EXISTS "rebase"."user_roles" CASCADE`);
|
|
10370
|
+
await db.execute(sql`DROP TABLE IF EXISTS "rebase"."roles" CASCADE`);
|
|
10371
|
+
logger.info("✅ Legacy roles tables migrated and dropped");
|
|
10372
|
+
}
|
|
10373
|
+
} catch (migrationError) {
|
|
10374
|
+
logger.warn(`⚠️ Legacy roles migration skipped: ${migrationError instanceof Error ? migrationError.message : String(migrationError)}`);
|
|
10375
|
+
}
|
|
10376
|
+
const mfaFactorsTableName = `"${authSchema}"."mfa_factors"`;
|
|
10377
|
+
const mfaChallengesTableName = `"${authSchema}"."mfa_challenges"`;
|
|
10378
|
+
const recoveryCodesTableName = `"${authSchema}"."recovery_codes"`;
|
|
10480
10379
|
await db.execute(sql`
|
|
10481
10380
|
CREATE TABLE IF NOT EXISTS ${sql.raw(mfaFactorsTableName)} (
|
|
10482
10381
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10528,28 +10427,6 @@ async function ensureAuthTablesExist(db, registry) {
|
|
|
10528
10427
|
logger.warn("⚠️ Continuing without creating auth tables.");
|
|
10529
10428
|
}
|
|
10530
10429
|
}
|
|
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
10430
|
function getColumnKey(table, ...keys2) {
|
|
10554
10431
|
if (!table) return void 0;
|
|
10555
10432
|
for (const key of keys2) {
|
|
@@ -10569,24 +10446,18 @@ function getColumn(table, ...keys2) {
|
|
|
10569
10446
|
class UserService {
|
|
10570
10447
|
constructor(db, tableOrTables) {
|
|
10571
10448
|
this.db = db;
|
|
10572
|
-
if (tableOrTables &&
|
|
10449
|
+
if (tableOrTables && tableOrTables.users) {
|
|
10573
10450
|
const tables = tableOrTables;
|
|
10574
10451
|
this.usersTable = tables.users || users;
|
|
10575
10452
|
this.userIdentitiesTable = tables.userIdentities || userIdentities;
|
|
10576
|
-
this.userRolesTable = tables.userRoles || userRoles;
|
|
10577
|
-
this.rolesTable = tables.roles || roles;
|
|
10578
10453
|
} else {
|
|
10579
10454
|
const table = tableOrTables;
|
|
10580
10455
|
this.usersTable = table || users;
|
|
10581
10456
|
this.userIdentitiesTable = userIdentities;
|
|
10582
|
-
this.userRolesTable = userRoles;
|
|
10583
|
-
this.rolesTable = roles;
|
|
10584
10457
|
}
|
|
10585
10458
|
}
|
|
10586
10459
|
usersTable;
|
|
10587
10460
|
userIdentitiesTable;
|
|
10588
|
-
userRolesTable;
|
|
10589
|
-
rolesTable;
|
|
10590
10461
|
getQualifiedUsersTableName() {
|
|
10591
10462
|
const name = getTableName$1(this.usersTable);
|
|
10592
10463
|
const schema = getTableConfig(this.usersTable).schema || "public";
|
|
@@ -10608,7 +10479,7 @@ class UserService {
|
|
|
10608
10479
|
const metadata = {
|
|
10609
10480
|
...row.metadata || {}
|
|
10610
10481
|
};
|
|
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"]);
|
|
10482
|
+
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
10483
|
for (const [key, val] of Object.entries(row)) {
|
|
10613
10484
|
if (!knownKeys.has(key)) {
|
|
10614
10485
|
const camelKey = camelCase(key);
|
|
@@ -10759,19 +10630,18 @@ class UserService {
|
|
|
10759
10630
|
const displayNameCol = getColumn(this.usersTable, "displayName", "display_name");
|
|
10760
10631
|
const displayNameColumn = displayNameCol ? displayNameCol.name : "display_name";
|
|
10761
10632
|
const idCol = getColumn(this.usersTable, "id");
|
|
10762
|
-
|
|
10633
|
+
idCol ? idCol.name : "id";
|
|
10763
10634
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
10764
|
-
const rolesSchema = getTableConfig(this.userRolesTable).schema || "public";
|
|
10765
10635
|
const conditions = [];
|
|
10766
10636
|
if (roleId) {
|
|
10767
|
-
conditions.push(sql
|
|
10637
|
+
conditions.push(sql`${roleId} = ANY(${sql.raw(usersTableName)}.roles)`);
|
|
10768
10638
|
}
|
|
10769
10639
|
if (search) {
|
|
10770
10640
|
const pattern = `%${search}%`;
|
|
10771
10641
|
conditions.push(sql`(${sql.raw(usersTableName)}.${sql.raw(emailColumn)} ILIKE ${pattern} OR ${sql.raw(usersTableName)}.${sql.raw(displayNameColumn)} ILIKE ${pattern})`);
|
|
10772
10642
|
}
|
|
10773
10643
|
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 (
|
|
10644
|
+
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
10645
|
const countResult = await this.db.execute(sql`
|
|
10776
10646
|
SELECT count(*)::int as total FROM ${sql.raw(usersTableName)}
|
|
10777
10647
|
${whereClause}
|
|
@@ -10845,54 +10715,57 @@ class UserService {
|
|
|
10845
10715
|
return row ? this.mapRowToUser(row) : null;
|
|
10846
10716
|
}
|
|
10847
10717
|
/**
|
|
10848
|
-
* Get roles for a user from database
|
|
10718
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
10849
10719
|
*/
|
|
10850
10720
|
async getUserRoles(userId) {
|
|
10851
|
-
const
|
|
10721
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10852
10722
|
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}
|
|
10723
|
+
SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10857
10724
|
`);
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10725
|
+
if (result.rows.length === 0) return [];
|
|
10726
|
+
const row = result.rows[0];
|
|
10727
|
+
const roleIds = row.roles ?? [];
|
|
10728
|
+
return roleIds.map((id) => ({
|
|
10729
|
+
id,
|
|
10730
|
+
name: id,
|
|
10731
|
+
isAdmin: id === "admin",
|
|
10732
|
+
defaultPermissions: null,
|
|
10733
|
+
collectionPermissions: null
|
|
10864
10734
|
}));
|
|
10865
10735
|
}
|
|
10866
10736
|
/**
|
|
10867
10737
|
* Get role IDs for a user
|
|
10868
10738
|
*/
|
|
10869
10739
|
async getUserRoleIds(userId) {
|
|
10870
|
-
const
|
|
10871
|
-
|
|
10740
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10741
|
+
const result = await this.db.execute(sql`
|
|
10742
|
+
SELECT roles FROM ${sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10743
|
+
`);
|
|
10744
|
+
if (result.rows.length === 0) return [];
|
|
10745
|
+
const row = result.rows[0];
|
|
10746
|
+
return row.roles ?? [];
|
|
10872
10747
|
}
|
|
10873
10748
|
/**
|
|
10874
|
-
* Set roles for a user
|
|
10749
|
+
* Set roles for a user (replaces existing roles)
|
|
10875
10750
|
*/
|
|
10876
10751
|
async setUserRoles(userId, roleIds) {
|
|
10877
|
-
const
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
`);
|
|
10885
|
-
}
|
|
10752
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10753
|
+
const rolesArray = `{${roleIds.join(",")}}`;
|
|
10754
|
+
await this.db.execute(sql`
|
|
10755
|
+
UPDATE ${sql.raw(usersTableName)}
|
|
10756
|
+
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
10757
|
+
WHERE id = ${userId}
|
|
10758
|
+
`);
|
|
10886
10759
|
}
|
|
10887
10760
|
/**
|
|
10888
|
-
* Assign a specific role to new user
|
|
10761
|
+
* Assign a specific role to new user (appends if not present)
|
|
10889
10762
|
*/
|
|
10890
10763
|
async assignDefaultRole(userId, roleId) {
|
|
10891
|
-
const
|
|
10764
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10892
10765
|
await this.db.execute(sql`
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10766
|
+
UPDATE ${sql.raw(usersTableName)}
|
|
10767
|
+
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
10768
|
+
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
10896
10769
|
`);
|
|
10897
10770
|
}
|
|
10898
10771
|
/**
|
|
@@ -10901,101 +10774,12 @@ class UserService {
|
|
|
10901
10774
|
async getUserWithRoles(userId) {
|
|
10902
10775
|
const user = await this.getUserById(userId);
|
|
10903
10776
|
if (!user) return null;
|
|
10904
|
-
const
|
|
10777
|
+
const roles = await this.getUserRoles(userId);
|
|
10905
10778
|
return {
|
|
10906
10779
|
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
|
|
10780
|
+
roles
|
|
10978
10781
|
};
|
|
10979
10782
|
}
|
|
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
10783
|
}
|
|
11000
10784
|
class RefreshTokenService {
|
|
11001
10785
|
constructor(db, tableOrTables) {
|
|
@@ -11191,11 +10975,9 @@ class PostgresAuthRepository {
|
|
|
11191
10975
|
constructor(db, tableOrTables) {
|
|
11192
10976
|
this.db = db;
|
|
11193
10977
|
this.userService = new UserService(db, tableOrTables);
|
|
11194
|
-
this.roleService = new RoleService(db, tableOrTables);
|
|
11195
10978
|
this.tokenRepository = new PostgresTokenRepository(db, tableOrTables);
|
|
11196
10979
|
}
|
|
11197
10980
|
userService;
|
|
11198
|
-
roleService;
|
|
11199
10981
|
tokenRepository;
|
|
11200
10982
|
// User operations (delegate to UserService)
|
|
11201
10983
|
async createUser(data) {
|
|
@@ -11255,25 +11037,56 @@ class PostgresAuthRepository {
|
|
|
11255
11037
|
async getUserWithRoles(userId) {
|
|
11256
11038
|
return this.userService.getUserWithRoles(userId);
|
|
11257
11039
|
}
|
|
11258
|
-
// Role operations (
|
|
11040
|
+
// Role operations (roles are inline on users, synthesized from string IDs)
|
|
11259
11041
|
async getRoleById(id) {
|
|
11260
|
-
return
|
|
11042
|
+
return {
|
|
11043
|
+
id,
|
|
11044
|
+
name: id,
|
|
11045
|
+
isAdmin: id === "admin",
|
|
11046
|
+
defaultPermissions: null,
|
|
11047
|
+
collectionPermissions: null
|
|
11048
|
+
};
|
|
11261
11049
|
}
|
|
11262
11050
|
async listRoles() {
|
|
11263
|
-
return
|
|
11051
|
+
return [{
|
|
11052
|
+
id: "admin",
|
|
11053
|
+
name: "Admin",
|
|
11054
|
+
isAdmin: true,
|
|
11055
|
+
defaultPermissions: null,
|
|
11056
|
+
collectionPermissions: null
|
|
11057
|
+
}, {
|
|
11058
|
+
id: "editor",
|
|
11059
|
+
name: "Editor",
|
|
11060
|
+
isAdmin: false,
|
|
11061
|
+
defaultPermissions: null,
|
|
11062
|
+
collectionPermissions: null
|
|
11063
|
+
}, {
|
|
11064
|
+
id: "viewer",
|
|
11065
|
+
name: "Viewer",
|
|
11066
|
+
isAdmin: false,
|
|
11067
|
+
defaultPermissions: null,
|
|
11068
|
+
collectionPermissions: null
|
|
11069
|
+
}];
|
|
11070
|
+
}
|
|
11071
|
+
async createRole(_data) {
|
|
11072
|
+
return {
|
|
11073
|
+
id: _data.id,
|
|
11074
|
+
name: _data.name,
|
|
11075
|
+
isAdmin: _data.isAdmin ?? false,
|
|
11076
|
+
defaultPermissions: _data.defaultPermissions ?? null,
|
|
11077
|
+
collectionPermissions: _data.collectionPermissions ?? null
|
|
11078
|
+
};
|
|
11264
11079
|
}
|
|
11265
|
-
async
|
|
11266
|
-
return
|
|
11267
|
-
|
|
11080
|
+
async updateRole(id, data) {
|
|
11081
|
+
return {
|
|
11082
|
+
id,
|
|
11083
|
+
name: data.name ?? id,
|
|
11084
|
+
isAdmin: data.isAdmin ?? id === "admin",
|
|
11268
11085
|
defaultPermissions: data.defaultPermissions ?? null,
|
|
11269
11086
|
collectionPermissions: data.collectionPermissions ?? null
|
|
11270
|
-
}
|
|
11271
|
-
}
|
|
11272
|
-
async updateRole(id, data) {
|
|
11273
|
-
return this.roleService.updateRole(id, data);
|
|
11087
|
+
};
|
|
11274
11088
|
}
|
|
11275
|
-
async deleteRole(
|
|
11276
|
-
await this.roleService.deleteRole(id);
|
|
11089
|
+
async deleteRole(_id) {
|
|
11277
11090
|
}
|
|
11278
11091
|
// Token operations (delegate to PostgresTokenRepository)
|
|
11279
11092
|
async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
|
|
@@ -11823,34 +11636,27 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11823
11636
|
const internals = driverResult.internals;
|
|
11824
11637
|
const db = internals.db;
|
|
11825
11638
|
const registry = internals.registry;
|
|
11826
|
-
|
|
11639
|
+
const authCollection = authConfig.collection;
|
|
11640
|
+
await ensureAuthTablesExist(db, authCollection);
|
|
11827
11641
|
let emailService;
|
|
11828
11642
|
if (authConfig.email) {
|
|
11829
11643
|
emailService = createEmailService(authConfig.email);
|
|
11830
11644
|
}
|
|
11831
|
-
const
|
|
11832
|
-
const
|
|
11645
|
+
const tableName = authCollection ? "table" in authCollection && typeof authCollection.table === "string" ? authCollection.table : authCollection.slug : void 0;
|
|
11646
|
+
const usersTable = tableName ? registry.getTable(tableName) : void 0;
|
|
11833
11647
|
let usersSchemaName = "rebase";
|
|
11834
|
-
|
|
11835
|
-
|
|
11836
|
-
usersSchemaName = getTableConfig(customUsersTable).schema || "public";
|
|
11648
|
+
if (authCollection && "schema" in authCollection && typeof authCollection.schema === "string") {
|
|
11649
|
+
usersSchemaName = authCollection.schema;
|
|
11837
11650
|
}
|
|
11838
|
-
|
|
11839
|
-
|
|
11840
|
-
|
|
11841
|
-
const authTables = createAuthSchema(rolesSchemaName, usersSchemaName);
|
|
11842
|
-
if (customUsersTable) {
|
|
11843
|
-
authTables.users = customUsersTable;
|
|
11844
|
-
}
|
|
11845
|
-
if (customRolesTable) {
|
|
11846
|
-
authTables.roles = customRolesTable;
|
|
11651
|
+
const authTables = createAuthSchema(usersSchemaName);
|
|
11652
|
+
if (usersTable) {
|
|
11653
|
+
authTables.users = usersTable;
|
|
11847
11654
|
}
|
|
11848
11655
|
const userService = new UserService(db, authTables);
|
|
11849
|
-
const roleService = new RoleService(db, authTables);
|
|
11850
11656
|
const authRepository = new PostgresAuthRepository(db, authTables);
|
|
11851
11657
|
return {
|
|
11852
11658
|
userService,
|
|
11853
|
-
roleService,
|
|
11659
|
+
roleService: userService,
|
|
11854
11660
|
emailService,
|
|
11855
11661
|
authRepository
|
|
11856
11662
|
};
|
|
@@ -11955,17 +11761,12 @@ export {
|
|
|
11955
11761
|
mfaFactorsRelations,
|
|
11956
11762
|
passwordResetTokens,
|
|
11957
11763
|
passwordResetTokensRelations,
|
|
11958
|
-
rebaseSchema,
|
|
11959
11764
|
recoveryCodes,
|
|
11960
11765
|
recoveryCodesRelations,
|
|
11961
11766
|
refreshTokens,
|
|
11962
11767
|
refreshTokensRelations,
|
|
11963
|
-
roles,
|
|
11964
|
-
rolesRelations,
|
|
11965
11768
|
userIdentities,
|
|
11966
11769
|
userIdentitiesRelations,
|
|
11967
|
-
userRoles,
|
|
11968
|
-
userRolesRelations,
|
|
11969
11770
|
users,
|
|
11970
11771
|
usersRelations,
|
|
11971
11772
|
usersSchema
|