@rebasepro/server-postgresql 0.3.0 → 0.5.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/README.md +69 -89
- package/dist/common/src/collections/default-collections.d.ts +5 -8
- package/dist/common/src/data/query_builder.d.ts +6 -2
- package/dist/common/src/util/permissions.d.ts +14 -6
- package/dist/index.es.js +379 -611
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +375 -607
- 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 +17 -42
- package/dist/server-postgresql/src/data-transformer.d.ts +0 -3
- package/dist/server-postgresql/src/databasePoolManager.d.ts +1 -1
- 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/types.d.ts +3 -0
- package/dist/server-postgresql/src/utils/drizzle-conditions.d.ts +5 -1
- package/dist/server-postgresql/src/websocket.d.ts +8 -3
- 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 +38 -3
- package/dist/types/src/types/backend_hooks.d.ts +2 -17
- package/dist/types/src/types/collections.d.ts +30 -6
- package/dist/types/src/types/entity_views.d.ts +19 -28
- package/dist/types/src/types/properties.d.ts +9 -15
- 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 +27 -22
- package/src/auth/ensure-tables.ts +82 -129
- package/src/auth/services.ts +99 -197
- package/src/cli.ts +50 -23
- package/src/data-transformer.ts +57 -95
- package/src/databasePoolManager.ts +2 -1
- 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 +38 -12
- package/src/services/entityService.ts +7 -0
- package/src/types.ts +4 -0
- package/src/utils/drizzle-conditions.ts +40 -5
- package/src/websocket.ts +38 -25
- 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/drizzle.test.config.ts +0 -10
- package/src/schema/default-collections.ts +0 -69
package/dist/index.umd.js
CHANGED
|
@@ -2719,114 +2719,6 @@
|
|
|
2719
2719
|
};
|
|
2720
2720
|
}
|
|
2721
2721
|
}
|
|
2722
|
-
const defaultUsersCollection = {
|
|
2723
|
-
name: "Users",
|
|
2724
|
-
singularName: "User",
|
|
2725
|
-
slug: "users",
|
|
2726
|
-
table: "users",
|
|
2727
|
-
schema: "rebase",
|
|
2728
|
-
icon: "Users",
|
|
2729
|
-
group: "Settings",
|
|
2730
|
-
properties: {
|
|
2731
|
-
id: {
|
|
2732
|
-
name: "ID",
|
|
2733
|
-
type: "string",
|
|
2734
|
-
isId: "uuid"
|
|
2735
|
-
},
|
|
2736
|
-
email: {
|
|
2737
|
-
name: "Email",
|
|
2738
|
-
type: "string",
|
|
2739
|
-
validation: {
|
|
2740
|
-
required: true,
|
|
2741
|
-
unique: true
|
|
2742
|
-
}
|
|
2743
|
-
},
|
|
2744
|
-
password_hash: {
|
|
2745
|
-
name: "Password Hash",
|
|
2746
|
-
type: "string",
|
|
2747
|
-
ui: {
|
|
2748
|
-
hideFromCollection: true
|
|
2749
|
-
}
|
|
2750
|
-
},
|
|
2751
|
-
display_name: {
|
|
2752
|
-
name: "Display Name",
|
|
2753
|
-
type: "string"
|
|
2754
|
-
},
|
|
2755
|
-
photo_url: {
|
|
2756
|
-
name: "Photo URL",
|
|
2757
|
-
type: "string"
|
|
2758
|
-
},
|
|
2759
|
-
email_verified: {
|
|
2760
|
-
name: "Email Verified",
|
|
2761
|
-
type: "boolean",
|
|
2762
|
-
defaultValue: false
|
|
2763
|
-
},
|
|
2764
|
-
email_verification_token: {
|
|
2765
|
-
name: "Email Verification Token",
|
|
2766
|
-
type: "string",
|
|
2767
|
-
ui: {
|
|
2768
|
-
hideFromCollection: true
|
|
2769
|
-
}
|
|
2770
|
-
},
|
|
2771
|
-
email_verification_sent_at: {
|
|
2772
|
-
name: "Email Verification Sent At",
|
|
2773
|
-
type: "date",
|
|
2774
|
-
ui: {
|
|
2775
|
-
hideFromCollection: true
|
|
2776
|
-
}
|
|
2777
|
-
},
|
|
2778
|
-
metadata: {
|
|
2779
|
-
name: "Metadata",
|
|
2780
|
-
type: "map",
|
|
2781
|
-
defaultValue: {},
|
|
2782
|
-
ui: {
|
|
2783
|
-
hideFromCollection: true
|
|
2784
|
-
}
|
|
2785
|
-
},
|
|
2786
|
-
created_at: {
|
|
2787
|
-
name: "Created At",
|
|
2788
|
-
type: "date",
|
|
2789
|
-
autoValue: "on_create",
|
|
2790
|
-
ui: {
|
|
2791
|
-
readOnly: true,
|
|
2792
|
-
hideFromCollection: true
|
|
2793
|
-
}
|
|
2794
|
-
},
|
|
2795
|
-
updated_at: {
|
|
2796
|
-
name: "Updated At",
|
|
2797
|
-
type: "date",
|
|
2798
|
-
autoValue: "on_update",
|
|
2799
|
-
ui: {
|
|
2800
|
-
readOnly: true,
|
|
2801
|
-
hideFromCollection: true
|
|
2802
|
-
}
|
|
2803
|
-
}
|
|
2804
|
-
}
|
|
2805
|
-
};
|
|
2806
|
-
function mapOperator(op) {
|
|
2807
|
-
switch (op) {
|
|
2808
|
-
case "==":
|
|
2809
|
-
return "eq";
|
|
2810
|
-
case "!=":
|
|
2811
|
-
return "neq";
|
|
2812
|
-
case ">":
|
|
2813
|
-
return "gt";
|
|
2814
|
-
case ">=":
|
|
2815
|
-
return "gte";
|
|
2816
|
-
case "<":
|
|
2817
|
-
return "lt";
|
|
2818
|
-
case "<=":
|
|
2819
|
-
return "lte";
|
|
2820
|
-
case "array-contains":
|
|
2821
|
-
return "cs";
|
|
2822
|
-
case "array-contains-any":
|
|
2823
|
-
return "csa";
|
|
2824
|
-
case "not-in":
|
|
2825
|
-
return "nin";
|
|
2826
|
-
default:
|
|
2827
|
-
return op;
|
|
2828
|
-
}
|
|
2829
|
-
}
|
|
2830
2722
|
class QueryBuilder {
|
|
2831
2723
|
constructor(collection) {
|
|
2832
2724
|
this.collection = collection;
|
|
@@ -2834,23 +2726,30 @@
|
|
|
2834
2726
|
params = {
|
|
2835
2727
|
where: {}
|
|
2836
2728
|
};
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
where(column, operator, value) {
|
|
2729
|
+
where(columnOrCondition, operator, value) {
|
|
2730
|
+
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
2731
|
+
this.params.logical = columnOrCondition;
|
|
2732
|
+
return this;
|
|
2733
|
+
}
|
|
2843
2734
|
if (!this.params.where) {
|
|
2844
2735
|
this.params.where = {};
|
|
2845
2736
|
}
|
|
2846
|
-
const
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2737
|
+
const column = columnOrCondition;
|
|
2738
|
+
const condition = [operator, value];
|
|
2739
|
+
const existing = this.params.where[column];
|
|
2740
|
+
if (existing === void 0) {
|
|
2741
|
+
this.params.where[column] = condition;
|
|
2742
|
+
} else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
|
|
2743
|
+
this.params.where[column].push(condition);
|
|
2744
|
+
} else {
|
|
2745
|
+
let firstCondition;
|
|
2746
|
+
if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
|
|
2747
|
+
firstCondition = existing;
|
|
2748
|
+
} else {
|
|
2749
|
+
firstCondition = ["==", existing];
|
|
2750
|
+
}
|
|
2751
|
+
this.params.where[column] = [firstCondition, condition];
|
|
2852
2752
|
}
|
|
2853
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2854
2753
|
return this;
|
|
2855
2754
|
}
|
|
2856
2755
|
/**
|
|
@@ -2952,10 +2851,13 @@
|
|
|
2952
2851
|
filter[field] = ["==", rawValue];
|
|
2953
2852
|
continue;
|
|
2954
2853
|
}
|
|
2955
|
-
if (Array.isArray(rawValue)
|
|
2956
|
-
const [
|
|
2957
|
-
const
|
|
2958
|
-
|
|
2854
|
+
if (Array.isArray(rawValue)) {
|
|
2855
|
+
const conditions = Array.isArray(rawValue[0]) ? rawValue : [rawValue];
|
|
2856
|
+
const mappedConditions = conditions.map(([rawOp, val]) => {
|
|
2857
|
+
const mappedOp = operatorMap[rawOp] ?? "==";
|
|
2858
|
+
return [mappedOp, val];
|
|
2859
|
+
});
|
|
2860
|
+
filter[field] = Array.isArray(rawValue[0]) ? mappedConditions : mappedConditions[0];
|
|
2959
2861
|
continue;
|
|
2960
2862
|
}
|
|
2961
2863
|
if (typeof rawValue === "string") {
|
|
@@ -3049,6 +2951,9 @@
|
|
|
3049
2951
|
}
|
|
3050
2952
|
});
|
|
3051
2953
|
},
|
|
2954
|
+
deleteAll: driver.deleteAll ? async () => {
|
|
2955
|
+
return driver.deleteAll(slug);
|
|
2956
|
+
} : void 0,
|
|
3052
2957
|
count: driver.countEntities ? async (params) => {
|
|
3053
2958
|
return driver.countEntities({
|
|
3054
2959
|
path: slug,
|
|
@@ -3090,8 +2995,12 @@
|
|
|
3090
2995
|
});
|
|
3091
2996
|
} : void 0,
|
|
3092
2997
|
// Fluent Query Builder
|
|
3093
|
-
where(
|
|
3094
|
-
|
|
2998
|
+
where(columnOrCondition, operator, value) {
|
|
2999
|
+
const builder = new QueryBuilder(accessor);
|
|
3000
|
+
if (typeof columnOrCondition === "object") {
|
|
3001
|
+
return builder.where(columnOrCondition);
|
|
3002
|
+
}
|
|
3003
|
+
return builder.where(columnOrCondition, operator, value);
|
|
3095
3004
|
},
|
|
3096
3005
|
orderBy(column, ascending) {
|
|
3097
3006
|
return new QueryBuilder(accessor).orderBy(column, ascending);
|
|
@@ -3142,7 +3051,6 @@
|
|
|
3142
3051
|
const conditions = [];
|
|
3143
3052
|
for (const [field, filterParam] of Object.entries(filter)) {
|
|
3144
3053
|
if (!filterParam) continue;
|
|
3145
|
-
const [op, value] = filterParam;
|
|
3146
3054
|
let fieldColumn = table[field];
|
|
3147
3055
|
if (!fieldColumn) {
|
|
3148
3056
|
const relationKey = `${field}_id`;
|
|
@@ -3154,13 +3062,39 @@
|
|
|
3154
3062
|
console.warn(`Filtering by field '${field}', but it does not exist in table for collection '${collectionPath}'`);
|
|
3155
3063
|
continue;
|
|
3156
3064
|
}
|
|
3157
|
-
const
|
|
3158
|
-
|
|
3159
|
-
|
|
3065
|
+
const paramsList = Array.isArray(filterParam) && filterParam.length > 0 && Array.isArray(filterParam[0]) ? filterParam : [filterParam];
|
|
3066
|
+
for (const [op, value] of paramsList) {
|
|
3067
|
+
const condition = this.buildSingleFilterCondition(fieldColumn, op, value);
|
|
3068
|
+
if (condition) {
|
|
3069
|
+
conditions.push(condition);
|
|
3070
|
+
}
|
|
3160
3071
|
}
|
|
3161
3072
|
}
|
|
3162
3073
|
return conditions;
|
|
3163
3074
|
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Build logical conditions recursively from LogicalCondition or FilterCondition
|
|
3077
|
+
*/
|
|
3078
|
+
static buildLogicalConditions(cond, table, collectionPath) {
|
|
3079
|
+
if ("type" in cond) {
|
|
3080
|
+
const subSQLs = cond.conditions.map((c) => this.buildLogicalConditions(c, table, collectionPath)).filter((sql2) => sql2 !== null);
|
|
3081
|
+
if (subSQLs.length === 0) return null;
|
|
3082
|
+
return (cond.type === "or" ? drizzleOrm.or(...subSQLs) : drizzleOrm.and(...subSQLs)) ?? null;
|
|
3083
|
+
} else {
|
|
3084
|
+
let fieldColumn = table[cond.column];
|
|
3085
|
+
if (!fieldColumn) {
|
|
3086
|
+
const relationKey = `${cond.column}_id`;
|
|
3087
|
+
if (relationKey in table) {
|
|
3088
|
+
fieldColumn = table[relationKey];
|
|
3089
|
+
}
|
|
3090
|
+
}
|
|
3091
|
+
if (!fieldColumn) {
|
|
3092
|
+
console.warn(`Filtering by field '${cond.column}', but it does not exist in table for collection '${collectionPath}'`);
|
|
3093
|
+
return null;
|
|
3094
|
+
}
|
|
3095
|
+
return this.buildSingleFilterCondition(fieldColumn, cond.operator, cond.value);
|
|
3096
|
+
}
|
|
3097
|
+
}
|
|
3164
3098
|
/**
|
|
3165
3099
|
* Build a single filter condition for a specific operator and value
|
|
3166
3100
|
*/
|
|
@@ -4038,39 +3972,18 @@
|
|
|
4038
3972
|
}
|
|
4039
3973
|
return value;
|
|
4040
3974
|
}
|
|
4041
|
-
case "binary":
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
if (base64Data) {
|
|
4046
|
-
return Buffer.from(base64Data, "base64");
|
|
4047
|
-
}
|
|
4048
|
-
}
|
|
4049
|
-
}
|
|
4050
|
-
if (Buffer.isBuffer(value)) {
|
|
4051
|
-
return value;
|
|
4052
|
-
}
|
|
3975
|
+
case "binary": {
|
|
3976
|
+
const decoded = tryDecodeBase64DataUrl(value);
|
|
3977
|
+
if (decoded) return decoded;
|
|
3978
|
+
if (Buffer.isBuffer(value)) return value;
|
|
4053
3979
|
return value;
|
|
3980
|
+
}
|
|
4054
3981
|
case "string":
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
if (base64Data) {
|
|
4059
|
-
return Buffer.from(base64Data, "base64");
|
|
4060
|
-
}
|
|
4061
|
-
}
|
|
4062
|
-
}
|
|
4063
|
-
return value;
|
|
4064
|
-
default:
|
|
4065
|
-
if (typeof value === "string") {
|
|
4066
|
-
if (value.startsWith("data:application/octet-stream;base64,")) {
|
|
4067
|
-
const base64Data = value.split(",")[1];
|
|
4068
|
-
if (base64Data) {
|
|
4069
|
-
return Buffer.from(base64Data, "base64");
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
}
|
|
3982
|
+
default: {
|
|
3983
|
+
const decoded = tryDecodeBase64DataUrl(value);
|
|
3984
|
+
if (decoded) return decoded;
|
|
4073
3985
|
return value;
|
|
3986
|
+
}
|
|
4074
3987
|
}
|
|
4075
3988
|
}
|
|
4076
3989
|
async function parseDataFromServer(data, collection, db, registry) {
|
|
@@ -4190,21 +4103,36 @@
|
|
|
4190
4103
|
}
|
|
4191
4104
|
return result;
|
|
4192
4105
|
}
|
|
4193
|
-
function
|
|
4194
|
-
if (
|
|
4195
|
-
|
|
4106
|
+
function tryDecodeBase64DataUrl(value) {
|
|
4107
|
+
if (typeof value !== "string") return null;
|
|
4108
|
+
if (!value.startsWith("data:application/octet-stream;base64,")) return null;
|
|
4109
|
+
const base64Data = value.split(",")[1];
|
|
4110
|
+
return base64Data ? Buffer.from(base64Data, "base64") : null;
|
|
4111
|
+
}
|
|
4112
|
+
function tryResolveBuffer(value) {
|
|
4113
|
+
if (Buffer.isBuffer(value)) return value;
|
|
4114
|
+
if (typeof value === "object" && value !== null) {
|
|
4115
|
+
const rawVal = value;
|
|
4116
|
+
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4117
|
+
return Buffer.from(rawVal.data);
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
return null;
|
|
4121
|
+
}
|
|
4122
|
+
function bufferToStringOrBase64(buf) {
|
|
4123
|
+
for (let i = 0; i < buf.length; i++) {
|
|
4124
|
+
const b = buf[i];
|
|
4125
|
+
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4126
|
+
return `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4127
|
+
}
|
|
4196
4128
|
}
|
|
4129
|
+
return buf.toString("utf8");
|
|
4130
|
+
}
|
|
4131
|
+
function parsePropertyFromServer(value, property, collection, propertyKey) {
|
|
4132
|
+
if (value === null || value === void 0) return value;
|
|
4197
4133
|
switch (property.type) {
|
|
4198
4134
|
case "binary": {
|
|
4199
|
-
|
|
4200
|
-
if (Buffer.isBuffer(value)) {
|
|
4201
|
-
buf = value;
|
|
4202
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4203
|
-
const rawVal = value;
|
|
4204
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4205
|
-
buf = Buffer.from(rawVal.data);
|
|
4206
|
-
}
|
|
4207
|
-
}
|
|
4135
|
+
const buf = tryResolveBuffer(value);
|
|
4208
4136
|
if (buf) {
|
|
4209
4137
|
return `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4210
4138
|
}
|
|
@@ -4212,28 +4140,9 @@
|
|
|
4212
4140
|
}
|
|
4213
4141
|
case "string": {
|
|
4214
4142
|
if (typeof value === "string") return value;
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
isBuffer = true;
|
|
4219
|
-
buf = value;
|
|
4220
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4221
|
-
const rawVal = value;
|
|
4222
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4223
|
-
isBuffer = true;
|
|
4224
|
-
buf = Buffer.from(rawVal.data);
|
|
4225
|
-
}
|
|
4226
|
-
}
|
|
4227
|
-
if (isBuffer && buf) {
|
|
4228
|
-
let isPrintable = true;
|
|
4229
|
-
for (let i = 0; i < buf.length; i++) {
|
|
4230
|
-
const b = buf[i];
|
|
4231
|
-
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4232
|
-
isPrintable = false;
|
|
4233
|
-
break;
|
|
4234
|
-
}
|
|
4235
|
-
}
|
|
4236
|
-
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4143
|
+
const buf = tryResolveBuffer(value);
|
|
4144
|
+
if (buf) {
|
|
4145
|
+
return bufferToStringOrBase64(buf);
|
|
4237
4146
|
}
|
|
4238
4147
|
if (typeof value === "object" && value !== null) {
|
|
4239
4148
|
try {
|
|
@@ -4347,28 +4256,9 @@
|
|
|
4347
4256
|
return null;
|
|
4348
4257
|
}
|
|
4349
4258
|
default: {
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
isBuffer = true;
|
|
4354
|
-
buf = value;
|
|
4355
|
-
} else if (typeof value === "object" && value !== null) {
|
|
4356
|
-
const rawVal = value;
|
|
4357
|
-
if (rawVal.type === "Buffer" && Array.isArray(rawVal.data)) {
|
|
4358
|
-
isBuffer = true;
|
|
4359
|
-
buf = Buffer.from(rawVal.data);
|
|
4360
|
-
}
|
|
4361
|
-
}
|
|
4362
|
-
if (isBuffer && buf) {
|
|
4363
|
-
let isPrintable = true;
|
|
4364
|
-
for (let i = 0; i < buf.length; i++) {
|
|
4365
|
-
const b = buf[i];
|
|
4366
|
-
if ((b < 32 || b > 126) && b !== 9 && b !== 10 && b !== 13) {
|
|
4367
|
-
isPrintable = false;
|
|
4368
|
-
break;
|
|
4369
|
-
}
|
|
4370
|
-
}
|
|
4371
|
-
return isPrintable ? buf.toString("utf8") : `data:application/octet-stream;base64,${buf.toString("base64")}`;
|
|
4259
|
+
const buf = tryResolveBuffer(value);
|
|
4260
|
+
if (buf) {
|
|
4261
|
+
return bufferToStringOrBase64(buf);
|
|
4372
4262
|
}
|
|
4373
4263
|
return value;
|
|
4374
4264
|
}
|
|
@@ -5557,6 +5447,10 @@
|
|
|
5557
5447
|
const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
|
|
5558
5448
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
5559
5449
|
}
|
|
5450
|
+
if (options.logical) {
|
|
5451
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
|
|
5452
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
5453
|
+
}
|
|
5560
5454
|
if (options.startAfter) {
|
|
5561
5455
|
const cursorConditions = this.buildCursorConditions(table, idField, idInfo, options, collectionPath);
|
|
5562
5456
|
if (cursorConditions.length > 0) allConditions.push(...cursorConditions);
|
|
@@ -5728,6 +5622,10 @@
|
|
|
5728
5622
|
const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
|
|
5729
5623
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
5730
5624
|
}
|
|
5625
|
+
if (options.logical) {
|
|
5626
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, collectionPath);
|
|
5627
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
5628
|
+
}
|
|
5731
5629
|
if (vectorMeta?.filter) {
|
|
5732
5630
|
allConditions.push(vectorMeta.filter);
|
|
5733
5631
|
}
|
|
@@ -6313,6 +6211,14 @@
|
|
|
6313
6211
|
const parsedId = parsedIdObj[idInfo.fieldName];
|
|
6314
6212
|
await this.db.delete(table).where(drizzleOrm.eq(idField, parsedId));
|
|
6315
6213
|
}
|
|
6214
|
+
/**
|
|
6215
|
+
* Delete all entities from a collection
|
|
6216
|
+
*/
|
|
6217
|
+
async deleteAll(collectionPath, _databaseId) {
|
|
6218
|
+
const collection = getCollectionByPath(collectionPath, this.registry);
|
|
6219
|
+
const table = getTableForCollection(collection, this.registry);
|
|
6220
|
+
await this.db.delete(table);
|
|
6221
|
+
}
|
|
6316
6222
|
/**
|
|
6317
6223
|
* Save an entity (create or update)
|
|
6318
6224
|
*/
|
|
@@ -6545,12 +6451,12 @@
|
|
|
6545
6451
|
*/
|
|
6546
6452
|
extractCauseMessage(error) {
|
|
6547
6453
|
if (!error || typeof error !== "object") return null;
|
|
6548
|
-
|
|
6549
|
-
if (
|
|
6550
|
-
const deeper = this.extractCauseMessage(
|
|
6454
|
+
if (!(error instanceof Error)) return null;
|
|
6455
|
+
if (error.cause && typeof error.cause === "object") {
|
|
6456
|
+
const deeper = this.extractCauseMessage(error.cause);
|
|
6551
6457
|
if (deeper) return deeper;
|
|
6552
|
-
if (
|
|
6553
|
-
return
|
|
6458
|
+
if (error.cause instanceof Error && error.cause.message) {
|
|
6459
|
+
return error.cause.message;
|
|
6554
6460
|
}
|
|
6555
6461
|
}
|
|
6556
6462
|
return null;
|
|
@@ -6571,12 +6477,17 @@
|
|
|
6571
6477
|
*/
|
|
6572
6478
|
extractPgError(error) {
|
|
6573
6479
|
if (!error || typeof error !== "object") return null;
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6480
|
+
if (!(error instanceof Error)) {
|
|
6481
|
+
if ("cause" in error && error.cause && typeof error.cause === "object") {
|
|
6482
|
+
return this.extractPgError(error.cause);
|
|
6483
|
+
}
|
|
6484
|
+
return null;
|
|
6485
|
+
}
|
|
6486
|
+
if ("code" in error && typeof error.code === "string" && /^[0-9A-Z]{5}$/.test(error.code)) {
|
|
6487
|
+
return error;
|
|
6577
6488
|
}
|
|
6578
|
-
if (
|
|
6579
|
-
return this.extractPgError(
|
|
6489
|
+
if (error.cause && typeof error.cause === "object") {
|
|
6490
|
+
return this.extractPgError(error.cause);
|
|
6580
6491
|
}
|
|
6581
6492
|
return null;
|
|
6582
6493
|
}
|
|
@@ -6644,6 +6555,12 @@
|
|
|
6644
6555
|
async deleteEntity(collectionPath, entityId, databaseId) {
|
|
6645
6556
|
return this.persistService.deleteEntity(collectionPath, entityId, databaseId);
|
|
6646
6557
|
}
|
|
6558
|
+
/**
|
|
6559
|
+
* Delete all entities from a collection
|
|
6560
|
+
*/
|
|
6561
|
+
async deleteAll(collectionPath, databaseId) {
|
|
6562
|
+
return this.persistService.deleteAll(collectionPath, databaseId);
|
|
6563
|
+
}
|
|
6647
6564
|
/**
|
|
6648
6565
|
* Execute raw SQL
|
|
6649
6566
|
*/
|
|
@@ -7344,6 +7261,10 @@
|
|
|
7344
7261
|
await this.realtimeService.notifyEntityUpdate(entity.path, entity.id.toString(), null, entity.databaseId || resolvedCollection?.databaseId);
|
|
7345
7262
|
}
|
|
7346
7263
|
}
|
|
7264
|
+
async deleteAll(path2) {
|
|
7265
|
+
await this.entityService.deleteAll(path2);
|
|
7266
|
+
await this.realtimeService.notifyEntityUpdate(path2, "*", null);
|
|
7267
|
+
}
|
|
7347
7268
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7348
7269
|
return this.entityService.checkUniqueField(path2, name, value, entityId, collection?.databaseId);
|
|
7349
7270
|
}
|
|
@@ -7613,11 +7534,11 @@
|
|
|
7613
7534
|
console.warn("[DataDriver] User ID (uid) is missing for authenticated delegate. Using 'anonymous'. User object:", this.user);
|
|
7614
7535
|
userId = "anonymous";
|
|
7615
7536
|
}
|
|
7616
|
-
const
|
|
7537
|
+
const userRoles = this.user?.roles ?? [];
|
|
7617
7538
|
if (!this.user?.roles) {
|
|
7618
7539
|
console.warn("[DataDriver] User roles are missing for authenticated delegate. Using empty array. User object:", this.user);
|
|
7619
7540
|
}
|
|
7620
|
-
const normalizedRoles =
|
|
7541
|
+
const normalizedRoles = userRoles.map((r) => typeof r === "string" ? r : r?.id ?? String(r));
|
|
7621
7542
|
const rolesString = normalizedRoles.join(",");
|
|
7622
7543
|
await tx.execute(drizzleOrm.sql`
|
|
7623
7544
|
SELECT
|
|
@@ -7625,7 +7546,7 @@
|
|
|
7625
7546
|
set_config('app.user_roles', ${rolesString}, true),
|
|
7626
7547
|
set_config('app.jwt', ${JSON.stringify({
|
|
7627
7548
|
sub: userId,
|
|
7628
|
-
roles:
|
|
7549
|
+
roles: userRoles
|
|
7629
7550
|
})}, true)
|
|
7630
7551
|
`);
|
|
7631
7552
|
const txEntityService = new EntityService(tx, this.delegate.registry);
|
|
@@ -7680,6 +7601,9 @@
|
|
|
7680
7601
|
async deleteEntity(props) {
|
|
7681
7602
|
return this.withTransaction((delegate) => delegate.deleteEntity(props));
|
|
7682
7603
|
}
|
|
7604
|
+
async deleteAll(path2) {
|
|
7605
|
+
return this.withTransaction((delegate) => delegate.deleteAll(path2));
|
|
7606
|
+
}
|
|
7683
7607
|
async checkUniqueField(path2, name, value, entityId, collection) {
|
|
7684
7608
|
return this.withTransaction((delegate) => delegate.checkUniqueField(path2, name, value, entityId, collection));
|
|
7685
7609
|
}
|
|
@@ -7757,13 +7681,13 @@
|
|
|
7757
7681
|
}
|
|
7758
7682
|
await Promise.all(promises);
|
|
7759
7683
|
this.pools.clear();
|
|
7684
|
+
this.drizzleInstances.clear();
|
|
7760
7685
|
}
|
|
7761
7686
|
}
|
|
7762
|
-
function createAuthSchema(
|
|
7763
|
-
const rolesSchema = rolesSchemaName === "public" ? null : pgCore.pgSchema(rolesSchemaName);
|
|
7687
|
+
function createAuthSchema(usersSchemaName = "rebase") {
|
|
7764
7688
|
const usersSchema2 = usersSchemaName === "public" ? null : pgCore.pgSchema(usersSchemaName);
|
|
7765
|
-
const
|
|
7766
|
-
const usersTableCreator =
|
|
7689
|
+
const tableCreator = usersSchema2 ? usersSchema2.table.bind(usersSchema2) : pgCore.pgTable;
|
|
7690
|
+
const usersTableCreator = tableCreator;
|
|
7767
7691
|
const users2 = usersTableCreator("users", {
|
|
7768
7692
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7769
7693
|
email: pgCore.varchar("email", {
|
|
@@ -7785,37 +7709,12 @@
|
|
|
7785
7709
|
}),
|
|
7786
7710
|
emailVerificationSentAt: pgCore.timestamp("email_verification_sent_at"),
|
|
7787
7711
|
isAnonymous: pgCore.boolean("is_anonymous").default(false).notNull(),
|
|
7712
|
+
roles: pgCore.text("roles").array().default([]).notNull(),
|
|
7788
7713
|
metadata: pgCore.jsonb("metadata").$type().default({}).notNull(),
|
|
7789
7714
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
|
|
7790
7715
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7791
7716
|
});
|
|
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", {
|
|
7717
|
+
const refreshTokens2 = tableCreator("refresh_tokens", {
|
|
7819
7718
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7820
7719
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7821
7720
|
onDelete: "cascade"
|
|
@@ -7834,7 +7733,7 @@
|
|
|
7834
7733
|
}, (table) => ({
|
|
7835
7734
|
uniqueDeviceSession: pgCore.unique("unique_device_session").on(table.userId, table.userAgent, table.ipAddress)
|
|
7836
7735
|
}));
|
|
7837
|
-
const passwordResetTokens2 =
|
|
7736
|
+
const passwordResetTokens2 = tableCreator("password_reset_tokens", {
|
|
7838
7737
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7839
7738
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7840
7739
|
onDelete: "cascade"
|
|
@@ -7846,14 +7745,14 @@
|
|
|
7846
7745
|
usedAt: pgCore.timestamp("used_at"),
|
|
7847
7746
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
7848
7747
|
});
|
|
7849
|
-
const appConfig2 =
|
|
7748
|
+
const appConfig2 = tableCreator("app_config", {
|
|
7850
7749
|
key: pgCore.varchar("key", {
|
|
7851
7750
|
length: 100
|
|
7852
7751
|
}).primaryKey(),
|
|
7853
7752
|
value: pgCore.jsonb("value").notNull(),
|
|
7854
7753
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7855
7754
|
});
|
|
7856
|
-
const userIdentities2 =
|
|
7755
|
+
const userIdentities2 = tableCreator("user_identities", {
|
|
7857
7756
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7858
7757
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7859
7758
|
onDelete: "cascade"
|
|
@@ -7871,7 +7770,7 @@
|
|
|
7871
7770
|
}, (table) => ({
|
|
7872
7771
|
uniqueProviderId: pgCore.unique("unique_provider_id").on(table.provider, table.providerId)
|
|
7873
7772
|
}));
|
|
7874
|
-
const mfaFactors2 =
|
|
7773
|
+
const mfaFactors2 = tableCreator("mfa_factors", {
|
|
7875
7774
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7876
7775
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7877
7776
|
onDelete: "cascade"
|
|
@@ -7890,7 +7789,7 @@
|
|
|
7890
7789
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull(),
|
|
7891
7790
|
updatedAt: pgCore.timestamp("updated_at").defaultNow().notNull()
|
|
7892
7791
|
});
|
|
7893
|
-
const mfaChallenges2 =
|
|
7792
|
+
const mfaChallenges2 = tableCreator("mfa_challenges", {
|
|
7894
7793
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7895
7794
|
factorId: pgCore.uuid("factor_id").notNull().references(() => mfaFactors2.id, {
|
|
7896
7795
|
onDelete: "cascade"
|
|
@@ -7902,7 +7801,7 @@
|
|
|
7902
7801
|
}),
|
|
7903
7802
|
expiresAt: pgCore.timestamp("expires_at").notNull()
|
|
7904
7803
|
});
|
|
7905
|
-
const recoveryCodes2 =
|
|
7804
|
+
const recoveryCodes2 = tableCreator("recovery_codes", {
|
|
7906
7805
|
id: pgCore.uuid("id").defaultRandom().primaryKey(),
|
|
7907
7806
|
userId: pgCore.uuid("user_id").notNull().references(() => users2.id, {
|
|
7908
7807
|
onDelete: "cascade"
|
|
@@ -7914,11 +7813,8 @@
|
|
|
7914
7813
|
createdAt: pgCore.timestamp("created_at").defaultNow().notNull()
|
|
7915
7814
|
});
|
|
7916
7815
|
return {
|
|
7917
|
-
rolesSchema,
|
|
7918
7816
|
usersSchema: usersSchema2,
|
|
7919
7817
|
users: users2,
|
|
7920
|
-
roles: roles2,
|
|
7921
|
-
userRoles: userRoles2,
|
|
7922
7818
|
refreshTokens: refreshTokens2,
|
|
7923
7819
|
passwordResetTokens: passwordResetTokens2,
|
|
7924
7820
|
appConfig: appConfig2,
|
|
@@ -7928,12 +7824,9 @@
|
|
|
7928
7824
|
recoveryCodes: recoveryCodes2
|
|
7929
7825
|
};
|
|
7930
7826
|
}
|
|
7931
|
-
const defaultAuthSchema = createAuthSchema("rebase"
|
|
7932
|
-
const rebaseSchema = defaultAuthSchema.rolesSchema;
|
|
7827
|
+
const defaultAuthSchema = createAuthSchema("rebase");
|
|
7933
7828
|
const usersSchema = defaultAuthSchema.usersSchema;
|
|
7934
7829
|
const users = defaultAuthSchema.users;
|
|
7935
|
-
const roles = defaultAuthSchema.roles;
|
|
7936
|
-
const userRoles = defaultAuthSchema.userRoles;
|
|
7937
7830
|
const refreshTokens = defaultAuthSchema.refreshTokens;
|
|
7938
7831
|
const passwordResetTokens = defaultAuthSchema.passwordResetTokens;
|
|
7939
7832
|
const appConfig = defaultAuthSchema.appConfig;
|
|
@@ -7944,30 +7837,12 @@
|
|
|
7944
7837
|
const usersRelations = drizzleOrm.relations(users, ({
|
|
7945
7838
|
many
|
|
7946
7839
|
}) => ({
|
|
7947
|
-
userRoles: many(userRoles),
|
|
7948
7840
|
refreshTokens: many(refreshTokens),
|
|
7949
7841
|
passwordResetTokens: many(passwordResetTokens),
|
|
7950
7842
|
userIdentities: many(userIdentities),
|
|
7951
7843
|
mfaFactors: many(mfaFactors),
|
|
7952
7844
|
recoveryCodes: many(recoveryCodes)
|
|
7953
7845
|
}));
|
|
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
7846
|
const refreshTokensRelations = drizzleOrm.relations(refreshTokens, ({
|
|
7972
7847
|
one
|
|
7973
7848
|
}) => ({
|
|
@@ -8074,6 +7949,8 @@
|
|
|
8074
7949
|
columnDefinition = `${enumName}("${colName}")`;
|
|
8075
7950
|
} else if ("isId" in stringProp && stringProp.isId === "uuid") {
|
|
8076
7951
|
columnDefinition = `uuid("${colName}")`;
|
|
7952
|
+
} else if (stringProp.columnType === "uuid") {
|
|
7953
|
+
columnDefinition = `uuid("${colName}")`;
|
|
8077
7954
|
} else if (stringProp.columnType === "text") {
|
|
8078
7955
|
columnDefinition = `text("${colName}")`;
|
|
8079
7956
|
} else if (stringProp.columnType === "char") {
|
|
@@ -8141,11 +8018,38 @@
|
|
|
8141
8018
|
}
|
|
8142
8019
|
break;
|
|
8143
8020
|
}
|
|
8144
|
-
case "map":
|
|
8021
|
+
case "map": {
|
|
8022
|
+
const mapProp = prop;
|
|
8023
|
+
if (mapProp.columnType === "json") {
|
|
8024
|
+
columnDefinition = `json("${colName}")`;
|
|
8025
|
+
} else {
|
|
8026
|
+
columnDefinition = `jsonb("${colName}")`;
|
|
8027
|
+
}
|
|
8028
|
+
break;
|
|
8029
|
+
}
|
|
8145
8030
|
case "array": {
|
|
8146
|
-
const
|
|
8147
|
-
|
|
8031
|
+
const arrayProp = prop;
|
|
8032
|
+
let colType = arrayProp.columnType;
|
|
8033
|
+
if (!colType && arrayProp.of && !Array.isArray(arrayProp.of)) {
|
|
8034
|
+
const ofProp = arrayProp.of;
|
|
8035
|
+
if (ofProp.type === "string") {
|
|
8036
|
+
colType = "text[]";
|
|
8037
|
+
} else if (ofProp.type === "number") {
|
|
8038
|
+
colType = ofProp.validation?.integer ? "integer[]" : "numeric[]";
|
|
8039
|
+
} else if (ofProp.type === "boolean") {
|
|
8040
|
+
colType = "boolean[]";
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
if (colType === "json") {
|
|
8148
8044
|
columnDefinition = `json("${colName}")`;
|
|
8045
|
+
} else if (colType === "text[]") {
|
|
8046
|
+
columnDefinition = `text("${colName}").array()`;
|
|
8047
|
+
} else if (colType === "integer[]") {
|
|
8048
|
+
columnDefinition = `integer("${colName}").array()`;
|
|
8049
|
+
} else if (colType === "boolean[]") {
|
|
8050
|
+
columnDefinition = `boolean("${colName}").array()`;
|
|
8051
|
+
} else if (colType === "numeric[]") {
|
|
8052
|
+
columnDefinition = `numeric("${colName}").array()`;
|
|
8149
8053
|
} else {
|
|
8150
8054
|
columnDefinition = `jsonb("${colName}")`;
|
|
8151
8055
|
}
|
|
@@ -8229,8 +8133,8 @@
|
|
|
8229
8133
|
const resolved = expression.replace(/\{(\w+)\}/g, (_, col) => col);
|
|
8230
8134
|
return `sql\`${resolved}\``;
|
|
8231
8135
|
};
|
|
8232
|
-
const wrapWithRoleCheck = (clause,
|
|
8233
|
-
const rolesArrayString = `ARRAY[${
|
|
8136
|
+
const wrapWithRoleCheck = (clause, roles) => {
|
|
8137
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8234
8138
|
const roleCondition = `string_to_array(auth.roles(), ',') @> ${rolesArrayString}`;
|
|
8235
8139
|
return `sql\`(${unwrapSql(clause)}) AND (${roleCondition})\``;
|
|
8236
8140
|
};
|
|
@@ -8283,22 +8187,22 @@
|
|
|
8283
8187
|
};
|
|
8284
8188
|
const generateSinglePolicyCode = (collection, rule, operation, policyName) => {
|
|
8285
8189
|
const mode = rule.mode ?? "permissive";
|
|
8286
|
-
const
|
|
8190
|
+
const roles = rule.roles ? [...rule.roles].sort() : void 0;
|
|
8287
8191
|
const needsUsing = operation !== "insert";
|
|
8288
8192
|
const needsWithCheck = operation !== "select" && operation !== "delete";
|
|
8289
8193
|
let usingClause = needsUsing ? buildUsingClause(rule, collection) : null;
|
|
8290
8194
|
let withCheckClause = needsWithCheck ? buildWithCheckClause(rule, collection) : null;
|
|
8291
|
-
if (
|
|
8195
|
+
if (roles && roles.length > 0) {
|
|
8292
8196
|
if (usingClause) {
|
|
8293
|
-
usingClause = wrapWithRoleCheck(usingClause,
|
|
8197
|
+
usingClause = wrapWithRoleCheck(usingClause, roles);
|
|
8294
8198
|
} else if (needsUsing) {
|
|
8295
|
-
const rolesArrayString = `ARRAY[${
|
|
8199
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8296
8200
|
usingClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8297
8201
|
}
|
|
8298
8202
|
if (withCheckClause) {
|
|
8299
|
-
withCheckClause = wrapWithRoleCheck(withCheckClause,
|
|
8203
|
+
withCheckClause = wrapWithRoleCheck(withCheckClause, roles);
|
|
8300
8204
|
} else if (needsWithCheck) {
|
|
8301
|
-
const rolesArrayString = `ARRAY[${
|
|
8205
|
+
const rolesArrayString = `ARRAY[${roles.map((r) => `'${r}'`).join(",")}]`;
|
|
8302
8206
|
withCheckClause = `sql\`string_to_array(auth.roles(), ',') @> ${rolesArrayString}\``;
|
|
8303
8207
|
}
|
|
8304
8208
|
}
|
|
@@ -8686,7 +8590,6 @@ ${tableRelations.join(",\n")}
|
|
|
8686
8590
|
if (!collections || !Array.isArray(collections)) {
|
|
8687
8591
|
collections = [];
|
|
8688
8592
|
}
|
|
8689
|
-
collections = Array.from(new Map([defaultUsersCollection, ...collections].map((c) => [c.slug, c])).values());
|
|
8690
8593
|
collections.sort((a, b) => a.slug.localeCompare(b.slug));
|
|
8691
8594
|
const schemaContent = await generateSchema(collections);
|
|
8692
8595
|
if (outputPath) {
|
|
@@ -9672,20 +9575,19 @@ ${tableRelations.join(",\n")}
|
|
|
9672
9575
|
}
|
|
9673
9576
|
}
|
|
9674
9577
|
const PostgresRealtimeProvider = RealtimeService;
|
|
9675
|
-
const clientSessions = /* @__PURE__ */ new Map();
|
|
9676
9578
|
const WS_RATE_LIMIT = 2e3;
|
|
9677
9579
|
const WS_RATE_WINDOW_MS = 6e4;
|
|
9678
9580
|
const ADMIN_ONLY_TYPES = /* @__PURE__ */ new Set(["EXECUTE_SQL", "FETCH_DATABASES", "FETCH_ROLES", "FETCH_UNMAPPED_TABLES", "FETCH_TABLE_METADATA", "FETCH_CURRENT_DATABASE", "CREATE_BRANCH", "DELETE_BRANCH", "LIST_BRANCHES"]);
|
|
9679
9581
|
function extractErrorMessage(error) {
|
|
9680
9582
|
if (!error) return "Unknown error";
|
|
9681
|
-
if (
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
return extractErrorMessage(err.cause);
|
|
9685
|
-
}
|
|
9686
|
-
if (typeof err.message === "string") {
|
|
9687
|
-
return err.message;
|
|
9583
|
+
if (error instanceof Error) {
|
|
9584
|
+
if ("cause" in error && error.cause) {
|
|
9585
|
+
return extractErrorMessage(error.cause);
|
|
9688
9586
|
}
|
|
9587
|
+
return error.message;
|
|
9588
|
+
}
|
|
9589
|
+
if (typeof error === "object" && "message" in error && typeof error.message === "string") {
|
|
9590
|
+
return error.message;
|
|
9689
9591
|
}
|
|
9690
9592
|
return String(error);
|
|
9691
9593
|
}
|
|
@@ -9693,14 +9595,10 @@ ${tableRelations.join(",\n")}
|
|
|
9693
9595
|
if (!session?.user) return false;
|
|
9694
9596
|
if (session.user.isAdmin) return true;
|
|
9695
9597
|
if (!session.user.roles) return false;
|
|
9696
|
-
return session.user.roles.some((r) =>
|
|
9697
|
-
if (typeof r === "string") return r === "admin";
|
|
9698
|
-
if (r && typeof r === "object" && "isAdmin" in r) return r.isAdmin;
|
|
9699
|
-
if (r && typeof r === "object" && "id" in r) return r.id === "admin";
|
|
9700
|
-
return false;
|
|
9701
|
-
});
|
|
9598
|
+
return session.user.roles.some((r) => r === "admin");
|
|
9702
9599
|
}
|
|
9703
9600
|
function createPostgresWebSocket(server, realtimeService, driver, authConfig, authAdapter) {
|
|
9601
|
+
const clientSessions = /* @__PURE__ */ new Map();
|
|
9704
9602
|
const isProduction = process.env.NODE_ENV === "production";
|
|
9705
9603
|
const wsDebug = (...args) => {
|
|
9706
9604
|
if (!isProduction) console.debug(...args);
|
|
@@ -9839,13 +9737,23 @@ ${tableRelations.join(",\n")}
|
|
|
9839
9737
|
}
|
|
9840
9738
|
const getScopedDelegate = async () => {
|
|
9841
9739
|
const session = clientSessions.get(clientId);
|
|
9842
|
-
if (
|
|
9740
|
+
if (typeof driver.withAuth === "function") {
|
|
9843
9741
|
try {
|
|
9844
9742
|
const userForAuth = session?.user ? {
|
|
9845
9743
|
uid: session.user.userId,
|
|
9744
|
+
displayName: null,
|
|
9745
|
+
email: null,
|
|
9746
|
+
photoURL: null,
|
|
9747
|
+
providerId: "websocket",
|
|
9748
|
+
isAnonymous: false,
|
|
9846
9749
|
roles: session.user.roles ?? []
|
|
9847
9750
|
} : {
|
|
9848
9751
|
uid: "anon",
|
|
9752
|
+
displayName: null,
|
|
9753
|
+
email: null,
|
|
9754
|
+
photoURL: null,
|
|
9755
|
+
providerId: "websocket",
|
|
9756
|
+
isAnonymous: true,
|
|
9849
9757
|
roles: ["anon"]
|
|
9850
9758
|
};
|
|
9851
9759
|
return await driver.withAuth(userForAuth);
|
|
@@ -10032,15 +9940,15 @@ ${tableRelations.join(",\n")}
|
|
|
10032
9940
|
wsDebug("👤 [WebSocket Server] Processing FETCH_ROLES request");
|
|
10033
9941
|
const delegate = await getScopedDelegate();
|
|
10034
9942
|
const admin = delegate.admin;
|
|
10035
|
-
let
|
|
9943
|
+
let roles = [];
|
|
10036
9944
|
if (isSQLAdmin(admin) && admin.fetchAvailableRoles) {
|
|
10037
|
-
|
|
9945
|
+
roles = await admin.fetchAvailableRoles();
|
|
10038
9946
|
}
|
|
10039
|
-
wsDebug(`👤 [WebSocket Server] Fetched ${
|
|
9947
|
+
wsDebug(`👤 [WebSocket Server] Fetched ${roles.length} roles.`);
|
|
10040
9948
|
const response = {
|
|
10041
9949
|
type: "FETCH_ROLES_SUCCESS",
|
|
10042
9950
|
payload: {
|
|
10043
|
-
roles
|
|
9951
|
+
roles
|
|
10044
9952
|
},
|
|
10045
9953
|
requestId
|
|
10046
9954
|
};
|
|
@@ -10294,85 +10202,35 @@ ${tableRelations.join(",\n")}
|
|
|
10294
10202
|
return collection.relations.map((r) => r.relationName || r.localKey || "").filter(Boolean);
|
|
10295
10203
|
}
|
|
10296
10204
|
}
|
|
10297
|
-
|
|
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
|
-
async function ensureAuthTablesExist(db, registry) {
|
|
10205
|
+
async function ensureAuthTablesExist(db, collection) {
|
|
10329
10206
|
serverCore.logger.info("🔍 Checking auth tables...");
|
|
10330
10207
|
try {
|
|
10331
|
-
let usersTableName = '"users"';
|
|
10208
|
+
let usersTableName = '"rebase"."users"';
|
|
10332
10209
|
let userIdType = "TEXT";
|
|
10333
|
-
let usersSchema2 = "
|
|
10334
|
-
if (
|
|
10335
|
-
const
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
const columnType = meta.columnType;
|
|
10346
|
-
if (columnType === "PgUUID") {
|
|
10347
|
-
userIdType = "UUID";
|
|
10348
|
-
} else if (columnType === "PgSerial" || columnType === "PgInteger") {
|
|
10349
|
-
userIdType = "INTEGER";
|
|
10350
|
-
} else if (columnType === "PgBigInt" || columnType === "PgBigSerial") {
|
|
10351
|
-
userIdType = "BIGINT";
|
|
10352
|
-
}
|
|
10210
|
+
let usersSchema2 = "rebase";
|
|
10211
|
+
if (collection) {
|
|
10212
|
+
const rawTable = "table" in collection && typeof collection.table === "string" ? collection.table : collection.slug;
|
|
10213
|
+
usersSchema2 = "schema" in collection && typeof collection.schema === "string" ? collection.schema : "public";
|
|
10214
|
+
usersTableName = usersSchema2 === "public" ? `"${rawTable}"` : `"${usersSchema2}"."${rawTable}"`;
|
|
10215
|
+
const idProp = collection.properties?.id;
|
|
10216
|
+
if (idProp) {
|
|
10217
|
+
const isId = "isId" in idProp ? idProp.isId : void 0;
|
|
10218
|
+
if (isId === "uuid") {
|
|
10219
|
+
userIdType = "UUID";
|
|
10220
|
+
} else if (isId === "autoincrement") {
|
|
10221
|
+
userIdType = "INTEGER";
|
|
10353
10222
|
}
|
|
10354
10223
|
}
|
|
10355
10224
|
}
|
|
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
10225
|
if (usersSchema2 !== "public") {
|
|
10364
10226
|
await db.execute(drizzleOrm.sql`CREATE SCHEMA IF NOT EXISTS ${drizzleOrm.sql.raw(usersSchema2)}`);
|
|
10365
10227
|
}
|
|
10366
|
-
if (rolesSchema !== "public" && rolesSchema !== usersSchema2) {
|
|
10367
|
-
await db.execute(drizzleOrm.sql`CREATE SCHEMA IF NOT EXISTS ${drizzleOrm.sql.raw(rolesSchema)}`);
|
|
10368
|
-
}
|
|
10369
10228
|
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"`;
|
|
10229
|
+
const authSchema = usersSchema2 === "public" ? "rebase" : usersSchema2;
|
|
10230
|
+
const userIdentitiesTable = `"${authSchema}"."user_identities"`;
|
|
10231
|
+
const refreshTokensTableName = `"${authSchema}"."refresh_tokens"`;
|
|
10232
|
+
const passwordResetTokensTableName = `"${authSchema}"."password_reset_tokens"`;
|
|
10233
|
+
const appConfigTableName = `"${authSchema}"."app_config"`;
|
|
10376
10234
|
await db.execute(drizzleOrm.sql`
|
|
10377
10235
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(userIdentitiesTable)} (
|
|
10378
10236
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10389,27 +10247,6 @@ ${tableRelations.join(",\n")}
|
|
|
10389
10247
|
CREATE INDEX IF NOT EXISTS idx_user_identities_user
|
|
10390
10248
|
ON ${drizzleOrm.sql.raw(userIdentitiesTable)}(user_id)
|
|
10391
10249
|
`);
|
|
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
10250
|
await db.execute(drizzleOrm.sql`
|
|
10414
10251
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(refreshTokensTableName)} (
|
|
10415
10252
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10477,14 +10314,43 @@ ${tableRelations.join(",\n")}
|
|
|
10477
10314
|
$$ LANGUAGE sql STABLE
|
|
10478
10315
|
`);
|
|
10479
10316
|
});
|
|
10480
|
-
await seedDefaultRoles(db, rolesTableName);
|
|
10481
10317
|
await db.execute(drizzleOrm.sql`
|
|
10482
10318
|
ALTER TABLE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10483
10319
|
ADD COLUMN IF NOT EXISTS is_anonymous BOOLEAN DEFAULT FALSE
|
|
10484
10320
|
`);
|
|
10485
|
-
|
|
10486
|
-
|
|
10487
|
-
|
|
10321
|
+
await db.execute(drizzleOrm.sql`
|
|
10322
|
+
ALTER TABLE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10323
|
+
ADD COLUMN IF NOT EXISTS roles TEXT[] DEFAULT '{}' NOT NULL
|
|
10324
|
+
`);
|
|
10325
|
+
try {
|
|
10326
|
+
const legacyCheck = await db.execute(drizzleOrm.sql`
|
|
10327
|
+
SELECT EXISTS (
|
|
10328
|
+
SELECT 1 FROM information_schema.tables
|
|
10329
|
+
WHERE table_schema = 'rebase' AND table_name = 'user_roles'
|
|
10330
|
+
) AS has_user_roles
|
|
10331
|
+
`);
|
|
10332
|
+
const hasLegacyTables = legacyCheck.rows[0].has_user_roles;
|
|
10333
|
+
if (hasLegacyTables) {
|
|
10334
|
+
serverCore.logger.info("🔄 Migrating roles from legacy user_roles table...");
|
|
10335
|
+
await db.execute(drizzleOrm.sql`
|
|
10336
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)} u
|
|
10337
|
+
SET roles = COALESCE((
|
|
10338
|
+
SELECT array_agg(ur.role_id)
|
|
10339
|
+
FROM "rebase"."user_roles" ur
|
|
10340
|
+
WHERE ur.user_id = u.id
|
|
10341
|
+
), '{}')
|
|
10342
|
+
WHERE u.roles = '{}' OR u.roles IS NULL
|
|
10343
|
+
`);
|
|
10344
|
+
await db.execute(drizzleOrm.sql`DROP TABLE IF EXISTS "rebase"."user_roles" CASCADE`);
|
|
10345
|
+
await db.execute(drizzleOrm.sql`DROP TABLE IF EXISTS "rebase"."roles" CASCADE`);
|
|
10346
|
+
serverCore.logger.info("✅ Legacy roles tables migrated and dropped");
|
|
10347
|
+
}
|
|
10348
|
+
} catch (migrationError) {
|
|
10349
|
+
serverCore.logger.warn(`⚠️ Legacy roles migration skipped: ${migrationError instanceof Error ? migrationError.message : String(migrationError)}`);
|
|
10350
|
+
}
|
|
10351
|
+
const mfaFactorsTableName = `"${authSchema}"."mfa_factors"`;
|
|
10352
|
+
const mfaChallengesTableName = `"${authSchema}"."mfa_challenges"`;
|
|
10353
|
+
const recoveryCodesTableName = `"${authSchema}"."recovery_codes"`;
|
|
10488
10354
|
await db.execute(drizzleOrm.sql`
|
|
10489
10355
|
CREATE TABLE IF NOT EXISTS ${drizzleOrm.sql.raw(mfaFactorsTableName)} (
|
|
10490
10356
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
@@ -10536,28 +10402,6 @@ ${tableRelations.join(",\n")}
|
|
|
10536
10402
|
serverCore.logger.warn("⚠️ Continuing without creating auth tables.");
|
|
10537
10403
|
}
|
|
10538
10404
|
}
|
|
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
10405
|
function getColumnKey(table, ...keys2) {
|
|
10562
10406
|
if (!table) return void 0;
|
|
10563
10407
|
for (const key of keys2) {
|
|
@@ -10577,24 +10421,18 @@ ${tableRelations.join(",\n")}
|
|
|
10577
10421
|
class UserService {
|
|
10578
10422
|
constructor(db, tableOrTables) {
|
|
10579
10423
|
this.db = db;
|
|
10580
|
-
if (tableOrTables &&
|
|
10424
|
+
if (tableOrTables && tableOrTables.users) {
|
|
10581
10425
|
const tables = tableOrTables;
|
|
10582
10426
|
this.usersTable = tables.users || users;
|
|
10583
10427
|
this.userIdentitiesTable = tables.userIdentities || userIdentities;
|
|
10584
|
-
this.userRolesTable = tables.userRoles || userRoles;
|
|
10585
|
-
this.rolesTable = tables.roles || roles;
|
|
10586
10428
|
} else {
|
|
10587
10429
|
const table = tableOrTables;
|
|
10588
10430
|
this.usersTable = table || users;
|
|
10589
10431
|
this.userIdentitiesTable = userIdentities;
|
|
10590
|
-
this.userRolesTable = userRoles;
|
|
10591
|
-
this.rolesTable = roles;
|
|
10592
10432
|
}
|
|
10593
10433
|
}
|
|
10594
10434
|
usersTable;
|
|
10595
10435
|
userIdentitiesTable;
|
|
10596
|
-
userRolesTable;
|
|
10597
|
-
rolesTable;
|
|
10598
10436
|
getQualifiedUsersTableName() {
|
|
10599
10437
|
const name = drizzleOrm.getTableName(this.usersTable);
|
|
10600
10438
|
const schema = pgCore.getTableConfig(this.usersTable).schema || "public";
|
|
@@ -10616,7 +10454,7 @@ ${tableRelations.join(",\n")}
|
|
|
10616
10454
|
const metadata = {
|
|
10617
10455
|
...row.metadata || {}
|
|
10618
10456
|
};
|
|
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"]);
|
|
10457
|
+
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
10458
|
for (const [key, val] of Object.entries(row)) {
|
|
10621
10459
|
if (!knownKeys.has(key)) {
|
|
10622
10460
|
const camelKey = camelCase(key);
|
|
@@ -10767,19 +10605,18 @@ ${tableRelations.join(",\n")}
|
|
|
10767
10605
|
const displayNameCol = getColumn(this.usersTable, "displayName", "display_name");
|
|
10768
10606
|
const displayNameColumn = displayNameCol ? displayNameCol.name : "display_name";
|
|
10769
10607
|
const idCol = getColumn(this.usersTable, "id");
|
|
10770
|
-
|
|
10608
|
+
idCol ? idCol.name : "id";
|
|
10771
10609
|
const usersTableName = this.getQualifiedUsersTableName();
|
|
10772
|
-
const rolesSchema = pgCore.getTableConfig(this.userRolesTable).schema || "public";
|
|
10773
10610
|
const conditions = [];
|
|
10774
10611
|
if (roleId) {
|
|
10775
|
-
conditions.push(drizzleOrm.sql
|
|
10612
|
+
conditions.push(drizzleOrm.sql`${roleId} = ANY(${drizzleOrm.sql.raw(usersTableName)}.roles)`);
|
|
10776
10613
|
}
|
|
10777
10614
|
if (search) {
|
|
10778
10615
|
const pattern = `%${search}%`;
|
|
10779
10616
|
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
10617
|
}
|
|
10781
10618
|
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 (
|
|
10619
|
+
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
10620
|
const countResult = await this.db.execute(drizzleOrm.sql`
|
|
10784
10621
|
SELECT count(*)::int as total FROM ${drizzleOrm.sql.raw(usersTableName)}
|
|
10785
10622
|
${whereClause}
|
|
@@ -10853,54 +10690,57 @@ ${tableRelations.join(",\n")}
|
|
|
10853
10690
|
return row ? this.mapRowToUser(row) : null;
|
|
10854
10691
|
}
|
|
10855
10692
|
/**
|
|
10856
|
-
* Get roles for a user from database
|
|
10693
|
+
* Get roles for a user from database (inline TEXT[] column)
|
|
10857
10694
|
*/
|
|
10858
10695
|
async getUserRoles(userId) {
|
|
10859
|
-
const
|
|
10696
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10860
10697
|
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}
|
|
10698
|
+
SELECT roles FROM ${drizzleOrm.sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10865
10699
|
`);
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10700
|
+
if (result.rows.length === 0) return [];
|
|
10701
|
+
const row = result.rows[0];
|
|
10702
|
+
const roleIds = row.roles ?? [];
|
|
10703
|
+
return roleIds.map((id) => ({
|
|
10704
|
+
id,
|
|
10705
|
+
name: id,
|
|
10706
|
+
isAdmin: id === "admin",
|
|
10707
|
+
defaultPermissions: null,
|
|
10708
|
+
collectionPermissions: null
|
|
10872
10709
|
}));
|
|
10873
10710
|
}
|
|
10874
10711
|
/**
|
|
10875
10712
|
* Get role IDs for a user
|
|
10876
10713
|
*/
|
|
10877
10714
|
async getUserRoleIds(userId) {
|
|
10878
|
-
const
|
|
10879
|
-
|
|
10715
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10716
|
+
const result = await this.db.execute(drizzleOrm.sql`
|
|
10717
|
+
SELECT roles FROM ${drizzleOrm.sql.raw(usersTableName)} WHERE id = ${userId}
|
|
10718
|
+
`);
|
|
10719
|
+
if (result.rows.length === 0) return [];
|
|
10720
|
+
const row = result.rows[0];
|
|
10721
|
+
return row.roles ?? [];
|
|
10880
10722
|
}
|
|
10881
10723
|
/**
|
|
10882
|
-
* Set roles for a user
|
|
10724
|
+
* Set roles for a user (replaces existing roles)
|
|
10883
10725
|
*/
|
|
10884
10726
|
async setUserRoles(userId, roleIds) {
|
|
10885
|
-
const
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
`);
|
|
10893
|
-
}
|
|
10727
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10728
|
+
const rolesArray = `{${roleIds.join(",")}}`;
|
|
10729
|
+
await this.db.execute(drizzleOrm.sql`
|
|
10730
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10731
|
+
SET roles = ${rolesArray}::text[], updated_at = NOW()
|
|
10732
|
+
WHERE id = ${userId}
|
|
10733
|
+
`);
|
|
10894
10734
|
}
|
|
10895
10735
|
/**
|
|
10896
|
-
* Assign a specific role to new user
|
|
10736
|
+
* Assign a specific role to new user (appends if not present)
|
|
10897
10737
|
*/
|
|
10898
10738
|
async assignDefaultRole(userId, roleId) {
|
|
10899
|
-
const
|
|
10739
|
+
const usersTableName = this.getQualifiedUsersTableName();
|
|
10900
10740
|
await this.db.execute(drizzleOrm.sql`
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10741
|
+
UPDATE ${drizzleOrm.sql.raw(usersTableName)}
|
|
10742
|
+
SET roles = array_append(roles, ${roleId}), updated_at = NOW()
|
|
10743
|
+
WHERE id = ${userId} AND NOT (${roleId} = ANY(roles))
|
|
10904
10744
|
`);
|
|
10905
10745
|
}
|
|
10906
10746
|
/**
|
|
@@ -10909,101 +10749,12 @@ ${tableRelations.join(",\n")}
|
|
|
10909
10749
|
async getUserWithRoles(userId) {
|
|
10910
10750
|
const user = await this.getUserById(userId);
|
|
10911
10751
|
if (!user) return null;
|
|
10912
|
-
const
|
|
10752
|
+
const roles = await this.getUserRoles(userId);
|
|
10913
10753
|
return {
|
|
10914
10754
|
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
|
|
10755
|
+
roles
|
|
10986
10756
|
};
|
|
10987
10757
|
}
|
|
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
10758
|
}
|
|
11008
10759
|
class RefreshTokenService {
|
|
11009
10760
|
constructor(db, tableOrTables) {
|
|
@@ -11199,11 +10950,9 @@ ${tableRelations.join(",\n")}
|
|
|
11199
10950
|
constructor(db, tableOrTables) {
|
|
11200
10951
|
this.db = db;
|
|
11201
10952
|
this.userService = new UserService(db, tableOrTables);
|
|
11202
|
-
this.roleService = new RoleService(db, tableOrTables);
|
|
11203
10953
|
this.tokenRepository = new PostgresTokenRepository(db, tableOrTables);
|
|
11204
10954
|
}
|
|
11205
10955
|
userService;
|
|
11206
|
-
roleService;
|
|
11207
10956
|
tokenRepository;
|
|
11208
10957
|
// User operations (delegate to UserService)
|
|
11209
10958
|
async createUser(data) {
|
|
@@ -11263,25 +11012,56 @@ ${tableRelations.join(",\n")}
|
|
|
11263
11012
|
async getUserWithRoles(userId) {
|
|
11264
11013
|
return this.userService.getUserWithRoles(userId);
|
|
11265
11014
|
}
|
|
11266
|
-
// Role operations (
|
|
11015
|
+
// Role operations (roles are inline on users, synthesized from string IDs)
|
|
11267
11016
|
async getRoleById(id) {
|
|
11268
|
-
return
|
|
11017
|
+
return {
|
|
11018
|
+
id,
|
|
11019
|
+
name: id,
|
|
11020
|
+
isAdmin: id === "admin",
|
|
11021
|
+
defaultPermissions: null,
|
|
11022
|
+
collectionPermissions: null
|
|
11023
|
+
};
|
|
11269
11024
|
}
|
|
11270
11025
|
async listRoles() {
|
|
11271
|
-
return
|
|
11026
|
+
return [{
|
|
11027
|
+
id: "admin",
|
|
11028
|
+
name: "Admin",
|
|
11029
|
+
isAdmin: true,
|
|
11030
|
+
defaultPermissions: null,
|
|
11031
|
+
collectionPermissions: null
|
|
11032
|
+
}, {
|
|
11033
|
+
id: "editor",
|
|
11034
|
+
name: "Editor",
|
|
11035
|
+
isAdmin: false,
|
|
11036
|
+
defaultPermissions: null,
|
|
11037
|
+
collectionPermissions: null
|
|
11038
|
+
}, {
|
|
11039
|
+
id: "viewer",
|
|
11040
|
+
name: "Viewer",
|
|
11041
|
+
isAdmin: false,
|
|
11042
|
+
defaultPermissions: null,
|
|
11043
|
+
collectionPermissions: null
|
|
11044
|
+
}];
|
|
11045
|
+
}
|
|
11046
|
+
async createRole(_data) {
|
|
11047
|
+
return {
|
|
11048
|
+
id: _data.id,
|
|
11049
|
+
name: _data.name,
|
|
11050
|
+
isAdmin: _data.isAdmin ?? false,
|
|
11051
|
+
defaultPermissions: _data.defaultPermissions ?? null,
|
|
11052
|
+
collectionPermissions: _data.collectionPermissions ?? null
|
|
11053
|
+
};
|
|
11272
11054
|
}
|
|
11273
|
-
async
|
|
11274
|
-
return
|
|
11275
|
-
|
|
11055
|
+
async updateRole(id, data) {
|
|
11056
|
+
return {
|
|
11057
|
+
id,
|
|
11058
|
+
name: data.name ?? id,
|
|
11059
|
+
isAdmin: data.isAdmin ?? id === "admin",
|
|
11276
11060
|
defaultPermissions: data.defaultPermissions ?? null,
|
|
11277
11061
|
collectionPermissions: data.collectionPermissions ?? null
|
|
11278
|
-
}
|
|
11279
|
-
}
|
|
11280
|
-
async updateRole(id, data) {
|
|
11281
|
-
return this.roleService.updateRole(id, data);
|
|
11062
|
+
};
|
|
11282
11063
|
}
|
|
11283
|
-
async deleteRole(
|
|
11284
|
-
await this.roleService.deleteRole(id);
|
|
11064
|
+
async deleteRole(_id) {
|
|
11285
11065
|
}
|
|
11286
11066
|
// Token operations (delegate to PostgresTokenRepository)
|
|
11287
11067
|
async createRefreshToken(userId, tokenHash, expiresAt, userAgent, ipAddress) {
|
|
@@ -11831,34 +11611,27 @@ ${tableRelations.join(",\n")}
|
|
|
11831
11611
|
const internals = driverResult.internals;
|
|
11832
11612
|
const db = internals.db;
|
|
11833
11613
|
const registry = internals.registry;
|
|
11834
|
-
|
|
11614
|
+
const authCollection = authConfig.collection;
|
|
11615
|
+
await ensureAuthTablesExist(db, authCollection);
|
|
11835
11616
|
let emailService;
|
|
11836
11617
|
if (authConfig.email) {
|
|
11837
11618
|
emailService = serverCore.createEmailService(authConfig.email);
|
|
11838
11619
|
}
|
|
11839
|
-
const
|
|
11840
|
-
const
|
|
11620
|
+
const tableName = authCollection ? "table" in authCollection && typeof authCollection.table === "string" ? authCollection.table : authCollection.slug : void 0;
|
|
11621
|
+
const usersTable = tableName ? registry.getTable(tableName) : void 0;
|
|
11841
11622
|
let usersSchemaName = "rebase";
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
usersSchemaName = pgCore.getTableConfig(customUsersTable).schema || "public";
|
|
11623
|
+
if (authCollection && "schema" in authCollection && typeof authCollection.schema === "string") {
|
|
11624
|
+
usersSchemaName = authCollection.schema;
|
|
11845
11625
|
}
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
11849
|
-
const authTables = createAuthSchema(rolesSchemaName, usersSchemaName);
|
|
11850
|
-
if (customUsersTable) {
|
|
11851
|
-
authTables.users = customUsersTable;
|
|
11852
|
-
}
|
|
11853
|
-
if (customRolesTable) {
|
|
11854
|
-
authTables.roles = customRolesTable;
|
|
11626
|
+
const authTables = createAuthSchema(usersSchemaName);
|
|
11627
|
+
if (usersTable) {
|
|
11628
|
+
authTables.users = usersTable;
|
|
11855
11629
|
}
|
|
11856
11630
|
const userService = new UserService(db, authTables);
|
|
11857
|
-
const roleService = new RoleService(db, authTables);
|
|
11858
11631
|
const authRepository = new PostgresAuthRepository(db, authTables);
|
|
11859
11632
|
return {
|
|
11860
11633
|
userService,
|
|
11861
|
-
roleService,
|
|
11634
|
+
roleService: userService,
|
|
11862
11635
|
emailService,
|
|
11863
11636
|
authRepository
|
|
11864
11637
|
};
|
|
@@ -11962,17 +11735,12 @@ ${tableRelations.join(",\n")}
|
|
|
11962
11735
|
exports2.mfaFactorsRelations = mfaFactorsRelations;
|
|
11963
11736
|
exports2.passwordResetTokens = passwordResetTokens;
|
|
11964
11737
|
exports2.passwordResetTokensRelations = passwordResetTokensRelations;
|
|
11965
|
-
exports2.rebaseSchema = rebaseSchema;
|
|
11966
11738
|
exports2.recoveryCodes = recoveryCodes;
|
|
11967
11739
|
exports2.recoveryCodesRelations = recoveryCodesRelations;
|
|
11968
11740
|
exports2.refreshTokens = refreshTokens;
|
|
11969
11741
|
exports2.refreshTokensRelations = refreshTokensRelations;
|
|
11970
|
-
exports2.roles = roles;
|
|
11971
|
-
exports2.rolesRelations = rolesRelations;
|
|
11972
11742
|
exports2.userIdentities = userIdentities;
|
|
11973
11743
|
exports2.userIdentitiesRelations = userIdentitiesRelations;
|
|
11974
|
-
exports2.userRoles = userRoles;
|
|
11975
|
-
exports2.userRolesRelations = userRolesRelations;
|
|
11976
11744
|
exports2.users = users;
|
|
11977
11745
|
exports2.usersRelations = usersRelations;
|
|
11978
11746
|
exports2.usersSchema = usersSchema;
|