@rebasepro/server-core 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 +318 -282
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +318 -282
- package/dist/index.umd.js.map +1 -1
- package/dist/server-core/src/api/rest/query-parser.d.ts +2 -0
- package/dist/server-core/src/api/types.d.ts +2 -1
- package/dist/server-core/src/email/types.d.ts +1 -0
- package/dist/server-core/src/init.d.ts +31 -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 +5 -5
- package/src/api/rest/api-generator.ts +11 -9
- package/src/api/rest/query-parser.ts +184 -63
- package/src/api/types.ts +2 -1
- package/src/auth/admin-routes.ts +2 -91
- package/src/auth/builtin-auth-adapter.ts +5 -55
- package/src/auth/custom-auth-adapter.ts +1 -1
- package/src/email/smtp-email-service.ts +31 -0
- package/src/email/types.ts +1 -0
- package/src/init.ts +136 -24
- package/src/storage/image-transform.ts +2 -1
- package/test/admin-routes.test.ts +0 -169
- package/test/backend-hooks-admin.test.ts +0 -25
- package/test/custom-auth-adapter.test.ts +2 -10
- package/test/smtp-email-service.test.ts +169 -0
- package/dist/types/src/users/roles.d.ts +0 -14
package/dist/index.es.js
CHANGED
|
@@ -146,6 +146,9 @@ class Vector {
|
|
|
146
146
|
this.value = value;
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
+
function isPostgresCollection(collection) {
|
|
150
|
+
return !collection.driver || collection.driver === "postgres";
|
|
151
|
+
}
|
|
149
152
|
function isSQLAdmin(admin) {
|
|
150
153
|
return !!admin && typeof admin.executeSql === "function";
|
|
151
154
|
}
|
|
@@ -2547,30 +2550,6 @@ class CollectionRegistry {
|
|
|
2547
2550
|
};
|
|
2548
2551
|
}
|
|
2549
2552
|
}
|
|
2550
|
-
function mapOperator$1(op) {
|
|
2551
|
-
switch (op) {
|
|
2552
|
-
case "==":
|
|
2553
|
-
return "eq";
|
|
2554
|
-
case "!=":
|
|
2555
|
-
return "neq";
|
|
2556
|
-
case ">":
|
|
2557
|
-
return "gt";
|
|
2558
|
-
case ">=":
|
|
2559
|
-
return "gte";
|
|
2560
|
-
case "<":
|
|
2561
|
-
return "lt";
|
|
2562
|
-
case "<=":
|
|
2563
|
-
return "lte";
|
|
2564
|
-
case "array-contains":
|
|
2565
|
-
return "cs";
|
|
2566
|
-
case "array-contains-any":
|
|
2567
|
-
return "csa";
|
|
2568
|
-
case "not-in":
|
|
2569
|
-
return "nin";
|
|
2570
|
-
default:
|
|
2571
|
-
return op;
|
|
2572
|
-
}
|
|
2573
|
-
}
|
|
2574
2553
|
class QueryBuilder {
|
|
2575
2554
|
constructor(collection) {
|
|
2576
2555
|
this.collection = collection;
|
|
@@ -2578,23 +2557,30 @@ class QueryBuilder {
|
|
|
2578
2557
|
params = {
|
|
2579
2558
|
where: {}
|
|
2580
2559
|
};
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
where(column, operator, value) {
|
|
2560
|
+
where(columnOrCondition, operator, value) {
|
|
2561
|
+
if (typeof columnOrCondition === "object" && columnOrCondition !== null && "type" in columnOrCondition) {
|
|
2562
|
+
this.params.logical = columnOrCondition;
|
|
2563
|
+
return this;
|
|
2564
|
+
}
|
|
2587
2565
|
if (!this.params.where) {
|
|
2588
2566
|
this.params.where = {};
|
|
2589
2567
|
}
|
|
2590
|
-
const
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2568
|
+
const column = columnOrCondition;
|
|
2569
|
+
const condition = [operator, value];
|
|
2570
|
+
const existing = this.params.where[column];
|
|
2571
|
+
if (existing === void 0) {
|
|
2572
|
+
this.params.where[column] = condition;
|
|
2573
|
+
} else if (Array.isArray(existing) && existing.length > 0 && Array.isArray(existing[0])) {
|
|
2574
|
+
this.params.where[column].push(condition);
|
|
2575
|
+
} else {
|
|
2576
|
+
let firstCondition;
|
|
2577
|
+
if (Array.isArray(existing) && existing.length === 2 && typeof existing[0] === "string") {
|
|
2578
|
+
firstCondition = existing;
|
|
2579
|
+
} else {
|
|
2580
|
+
firstCondition = ["==", existing];
|
|
2581
|
+
}
|
|
2582
|
+
this.params.where[column] = [firstCondition, condition];
|
|
2596
2583
|
}
|
|
2597
|
-
this.params.where[column] = mappedOp === "eq" ? String(formattedValue) : `${mappedOp}.${formattedValue}`;
|
|
2598
2584
|
return this;
|
|
2599
2585
|
}
|
|
2600
2586
|
/**
|
|
@@ -2917,68 +2903,184 @@ function mapOperator(op) {
|
|
|
2917
2903
|
return null;
|
|
2918
2904
|
}
|
|
2919
2905
|
}
|
|
2906
|
+
function getLastValue(val) {
|
|
2907
|
+
if (Array.isArray(val)) {
|
|
2908
|
+
return val[val.length - 1];
|
|
2909
|
+
}
|
|
2910
|
+
return val;
|
|
2911
|
+
}
|
|
2912
|
+
function parseLogicalString(str) {
|
|
2913
|
+
str = str.trim();
|
|
2914
|
+
if (str.startsWith("or(") && str.endsWith(")")) {
|
|
2915
|
+
const inner = str.slice(3, -1);
|
|
2916
|
+
return {
|
|
2917
|
+
type: "or",
|
|
2918
|
+
conditions: parseLogicalList(inner)
|
|
2919
|
+
};
|
|
2920
|
+
}
|
|
2921
|
+
if (str.startsWith("and(") && str.endsWith(")")) {
|
|
2922
|
+
const inner = str.slice(4, -1);
|
|
2923
|
+
return {
|
|
2924
|
+
type: "and",
|
|
2925
|
+
conditions: parseLogicalList(inner)
|
|
2926
|
+
};
|
|
2927
|
+
}
|
|
2928
|
+
const firstDot = str.indexOf(".");
|
|
2929
|
+
if (firstDot === -1) {
|
|
2930
|
+
return {
|
|
2931
|
+
column: str,
|
|
2932
|
+
operator: "==",
|
|
2933
|
+
value: true
|
|
2934
|
+
};
|
|
2935
|
+
}
|
|
2936
|
+
const field = str.substring(0, firstDot);
|
|
2937
|
+
const rest = str.substring(firstDot + 1);
|
|
2938
|
+
const secondDot = rest.indexOf(".");
|
|
2939
|
+
let op = "eq";
|
|
2940
|
+
let valStr = rest;
|
|
2941
|
+
if (secondDot !== -1) {
|
|
2942
|
+
op = rest.substring(0, secondDot);
|
|
2943
|
+
valStr = rest.substring(secondDot + 1);
|
|
2944
|
+
} else {
|
|
2945
|
+
op = "eq";
|
|
2946
|
+
valStr = rest;
|
|
2947
|
+
}
|
|
2948
|
+
const rebaseOp = mapOperator(op) || "==";
|
|
2949
|
+
let parsedVal = valStr;
|
|
2950
|
+
if (valStr === "true") parsedVal = true;
|
|
2951
|
+
else if (valStr === "false") parsedVal = false;
|
|
2952
|
+
else if (valStr === "null") parsedVal = null;
|
|
2953
|
+
else if (!isNaN(Number(valStr)) && valStr.trim() !== "") parsedVal = Number(valStr);
|
|
2954
|
+
else if (valStr.startsWith("(")) {
|
|
2955
|
+
const arrayContent = valStr.endsWith(")") ? valStr.slice(1, -1) : valStr.slice(1);
|
|
2956
|
+
parsedVal = arrayContent.split(",").map((v) => {
|
|
2957
|
+
const trimmed = v.trim();
|
|
2958
|
+
if (!isNaN(Number(trimmed)) && trimmed !== "") return Number(trimmed);
|
|
2959
|
+
if (trimmed === "true") return true;
|
|
2960
|
+
if (trimmed === "false") return false;
|
|
2961
|
+
if (trimmed === "null") return null;
|
|
2962
|
+
return trimmed;
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
return {
|
|
2966
|
+
column: field,
|
|
2967
|
+
operator: rebaseOp,
|
|
2968
|
+
value: parsedVal
|
|
2969
|
+
};
|
|
2970
|
+
}
|
|
2971
|
+
function parseLogicalList(str) {
|
|
2972
|
+
const list = [];
|
|
2973
|
+
let depth = 0;
|
|
2974
|
+
let current = "";
|
|
2975
|
+
for (let i = 0; i < str.length; i++) {
|
|
2976
|
+
const char = str[i];
|
|
2977
|
+
if (char === "(") depth++;
|
|
2978
|
+
if (char === ")") depth--;
|
|
2979
|
+
if (char === "," && depth === 0) {
|
|
2980
|
+
list.push(parseLogicalString(current));
|
|
2981
|
+
current = "";
|
|
2982
|
+
} else {
|
|
2983
|
+
current += char;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
if (current) {
|
|
2987
|
+
list.push(parseLogicalString(current));
|
|
2988
|
+
}
|
|
2989
|
+
return list;
|
|
2990
|
+
}
|
|
2920
2991
|
function parseQueryOptions(query) {
|
|
2921
2992
|
const options2 = {};
|
|
2922
|
-
|
|
2923
|
-
if (
|
|
2924
|
-
|
|
2925
|
-
|
|
2993
|
+
const limitVal = getLastValue(query.limit);
|
|
2994
|
+
if (limitVal) options2.limit = parseInt(String(limitVal));
|
|
2995
|
+
const offsetVal = getLastValue(query.offset);
|
|
2996
|
+
if (offsetVal) options2.offset = parseInt(String(offsetVal));
|
|
2997
|
+
const pageVal = getLastValue(query.page);
|
|
2998
|
+
if (pageVal) {
|
|
2999
|
+
const page = parseInt(String(pageVal));
|
|
2926
3000
|
const limit = options2.limit || 20;
|
|
2927
3001
|
options2.offset = (page - 1) * limit;
|
|
2928
3002
|
}
|
|
2929
3003
|
options2.where = {};
|
|
2930
|
-
const
|
|
3004
|
+
const orVal = getLastValue(query.or);
|
|
3005
|
+
const andVal = getLastValue(query.and);
|
|
3006
|
+
if (orVal) {
|
|
3007
|
+
let orStr = String(orVal).trim();
|
|
3008
|
+
if (orStr.startsWith("(") && orStr.endsWith(")")) {
|
|
3009
|
+
orStr = orStr.slice(1, -1);
|
|
3010
|
+
}
|
|
3011
|
+
options2.logical = {
|
|
3012
|
+
type: "or",
|
|
3013
|
+
conditions: parseLogicalList(orStr)
|
|
3014
|
+
};
|
|
3015
|
+
} else if (andVal) {
|
|
3016
|
+
let andStr = String(andVal).trim();
|
|
3017
|
+
if (andStr.startsWith("(") && andStr.endsWith(")")) {
|
|
3018
|
+
andStr = andStr.slice(1, -1);
|
|
3019
|
+
}
|
|
3020
|
+
options2.logical = {
|
|
3021
|
+
type: "and",
|
|
3022
|
+
conditions: parseLogicalList(andStr)
|
|
3023
|
+
};
|
|
3024
|
+
}
|
|
3025
|
+
const reservedQueryKeys = ["limit", "offset", "page", "orderBy", "include", "fields", "searchString", "vector_search", "vector", "vector_distance", "vector_threshold", "or", "and"];
|
|
2931
3026
|
for (const [key, rawValue] of Object.entries(query)) {
|
|
2932
3027
|
if (reservedQueryKeys.includes(key)) continue;
|
|
2933
|
-
const
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
if (
|
|
2937
|
-
const
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
if (
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
const
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
3028
|
+
const rawValues = Array.isArray(rawValue) ? rawValue : [rawValue];
|
|
3029
|
+
const conditions = [];
|
|
3030
|
+
for (const value of rawValues) {
|
|
3031
|
+
if (typeof value === "string") {
|
|
3032
|
+
const parts = value.split(".");
|
|
3033
|
+
if (parts.length >= 2) {
|
|
3034
|
+
const op = parts[0];
|
|
3035
|
+
const val = parts.slice(1).join(".");
|
|
3036
|
+
const rebaseOp = mapOperator(op);
|
|
3037
|
+
if (rebaseOp) {
|
|
3038
|
+
let parsedVal = val;
|
|
3039
|
+
if (val === "true") parsedVal = true;
|
|
3040
|
+
else if (val === "false") parsedVal = false;
|
|
3041
|
+
else if (val === "null") parsedVal = null;
|
|
3042
|
+
else if (!isNaN(Number(val)) && val.trim() !== "") parsedVal = Number(val);
|
|
3043
|
+
else if (val.startsWith("(")) {
|
|
3044
|
+
const arrayContent = val.endsWith(")") ? val.slice(1, -1) : val.slice(1);
|
|
3045
|
+
parsedVal = arrayContent.split(",").map((v) => {
|
|
3046
|
+
const trimmed = v.trim();
|
|
3047
|
+
if (!isNaN(Number(trimmed)) && trimmed !== "") return Number(trimmed);
|
|
3048
|
+
if (trimmed === "true") return true;
|
|
3049
|
+
if (trimmed === "false") return false;
|
|
3050
|
+
if (trimmed === "null") return null;
|
|
3051
|
+
return trimmed;
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
conditions.push([rebaseOp, parsedVal]);
|
|
3055
|
+
} else {
|
|
3056
|
+
let parsedVal = value;
|
|
3057
|
+
if (!isNaN(Number(value)) && value.trim() !== "") parsedVal = Number(value);
|
|
3058
|
+
conditions.push(["==", parsedVal]);
|
|
2956
3059
|
}
|
|
2957
|
-
options2.where[key] = [rebaseOp, parsedVal];
|
|
2958
3060
|
} else {
|
|
2959
3061
|
let parsedVal = value;
|
|
2960
|
-
if (
|
|
2961
|
-
|
|
3062
|
+
if (value === "true") parsedVal = true;
|
|
3063
|
+
else if (value === "false") parsedVal = false;
|
|
3064
|
+
else if (value === "null") parsedVal = null;
|
|
3065
|
+
else if (!isNaN(Number(value)) && value.trim() !== "") parsedVal = Number(value);
|
|
3066
|
+
conditions.push(["==", parsedVal]);
|
|
2962
3067
|
}
|
|
2963
|
-
} else {
|
|
2964
|
-
let parsedVal = value;
|
|
2965
|
-
if (value === "true") parsedVal = true;
|
|
2966
|
-
else if (value === "false") parsedVal = false;
|
|
2967
|
-
else if (value === "null") parsedVal = null;
|
|
2968
|
-
else if (!isNaN(Number(value)) && value.trim() !== "") parsedVal = Number(value);
|
|
2969
|
-
options2.where[key] = ["==", parsedVal];
|
|
2970
3068
|
}
|
|
2971
3069
|
}
|
|
3070
|
+
if (conditions.length > 0) {
|
|
3071
|
+
options2.where[key] = conditions.length === 1 ? conditions[0] : conditions;
|
|
3072
|
+
}
|
|
2972
3073
|
}
|
|
2973
3074
|
if (Object.keys(options2.where).length === 0) {
|
|
2974
3075
|
delete options2.where;
|
|
2975
3076
|
}
|
|
2976
|
-
|
|
3077
|
+
const orderByVal = getLastValue(query.orderBy);
|
|
3078
|
+
if (orderByVal) {
|
|
2977
3079
|
try {
|
|
2978
|
-
options2.orderBy = typeof
|
|
3080
|
+
options2.orderBy = typeof orderByVal === "string" ? JSON.parse(orderByVal) : orderByVal;
|
|
2979
3081
|
} catch {
|
|
2980
|
-
if (typeof
|
|
2981
|
-
const [field, direction] =
|
|
3082
|
+
if (typeof orderByVal === "string") {
|
|
3083
|
+
const [field, direction] = orderByVal.split(":");
|
|
2982
3084
|
const dir = direction === "desc" ? "desc" : "asc";
|
|
2983
3085
|
options2.orderBy = [{
|
|
2984
3086
|
field,
|
|
@@ -2987,20 +3089,24 @@ function parseQueryOptions(query) {
|
|
|
2987
3089
|
}
|
|
2988
3090
|
}
|
|
2989
3091
|
}
|
|
2990
|
-
|
|
2991
|
-
|
|
3092
|
+
const includeVal = getLastValue(query.include);
|
|
3093
|
+
if (includeVal) {
|
|
3094
|
+
const includeStr = String(includeVal).trim();
|
|
2992
3095
|
if (includeStr === "*") {
|
|
2993
3096
|
options2.include = ["*"];
|
|
2994
3097
|
} else {
|
|
2995
3098
|
options2.include = includeStr.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
2996
3099
|
}
|
|
2997
3100
|
}
|
|
2998
|
-
|
|
2999
|
-
|
|
3101
|
+
const fieldsVal = getLastValue(query.fields);
|
|
3102
|
+
if (fieldsVal) {
|
|
3103
|
+
const fieldsStr = String(fieldsVal).trim();
|
|
3000
3104
|
options2.fields = fieldsStr.split(",").map((s2) => s2.trim()).filter(Boolean);
|
|
3001
3105
|
}
|
|
3002
|
-
|
|
3003
|
-
|
|
3106
|
+
const vectorSearchVal = getLastValue(query.vector_search);
|
|
3107
|
+
const vectorVal = getLastValue(query.vector);
|
|
3108
|
+
if (vectorSearchVal && vectorVal) {
|
|
3109
|
+
const vectorStr = String(vectorVal);
|
|
3004
3110
|
let queryVector;
|
|
3005
3111
|
try {
|
|
3006
3112
|
queryVector = JSON.parse(vectorStr);
|
|
@@ -3010,17 +3116,19 @@ function parseQueryOptions(query) {
|
|
|
3010
3116
|
} catch {
|
|
3011
3117
|
throw new Error("Invalid vector format. Expected JSON array of numbers, e.g. [0.1,0.2,0.3]");
|
|
3012
3118
|
}
|
|
3013
|
-
const
|
|
3119
|
+
const distanceParamVal = getLastValue(query.vector_distance);
|
|
3120
|
+
const distanceParam = distanceParamVal ? String(distanceParamVal) : "cosine";
|
|
3014
3121
|
if (distanceParam !== "cosine" && distanceParam !== "l2" && distanceParam !== "inner_product") {
|
|
3015
3122
|
throw new Error(`Invalid vector_distance: ${distanceParam}. Expected: cosine, l2, or inner_product`);
|
|
3016
3123
|
}
|
|
3017
3124
|
const vectorSearch = {
|
|
3018
|
-
property: String(
|
|
3125
|
+
property: String(vectorSearchVal),
|
|
3019
3126
|
vector: queryVector,
|
|
3020
3127
|
distance: distanceParam
|
|
3021
3128
|
};
|
|
3022
|
-
|
|
3023
|
-
|
|
3129
|
+
const thresholdVal = getLastValue(query.vector_threshold);
|
|
3130
|
+
if (thresholdVal) {
|
|
3131
|
+
const threshold = parseFloat(String(thresholdVal));
|
|
3024
3132
|
if (isNaN(threshold)) {
|
|
3025
3133
|
throw new Error("Invalid vector_threshold. Expected a number.");
|
|
3026
3134
|
}
|
|
@@ -3115,9 +3223,9 @@ class RestApiGenerator {
|
|
|
3115
3223
|
const resolvedCollection = collection;
|
|
3116
3224
|
this.router.get(`${basePath}/count`, async (c) => {
|
|
3117
3225
|
this.enforceApiKeyPermission(c, collection.slug);
|
|
3118
|
-
const queryDict = c.req.
|
|
3226
|
+
const queryDict = c.req.queries();
|
|
3119
3227
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3120
|
-
const searchString = queryDict.searchString;
|
|
3228
|
+
const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : void 0;
|
|
3121
3229
|
const driver = c.get("driver") || this.driver;
|
|
3122
3230
|
const total = await this.countRawEntities(driver, resolvedCollection, queryOptions, searchString);
|
|
3123
3231
|
return c.json({
|
|
@@ -3126,9 +3234,9 @@ class RestApiGenerator {
|
|
|
3126
3234
|
});
|
|
3127
3235
|
this.router.get(basePath, async (c) => {
|
|
3128
3236
|
this.enforceApiKeyPermission(c, collection.slug);
|
|
3129
|
-
const queryDict = c.req.
|
|
3237
|
+
const queryDict = c.req.queries();
|
|
3130
3238
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3131
|
-
const searchString = queryDict.searchString;
|
|
3239
|
+
const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : void 0;
|
|
3132
3240
|
const driver = c.get("driver") || this.driver;
|
|
3133
3241
|
const fetchService = this.getFetchService(driver);
|
|
3134
3242
|
const hookCtx = this.buildHookContext(c, "GET");
|
|
@@ -3171,7 +3279,7 @@ class RestApiGenerator {
|
|
|
3171
3279
|
this.router.get(`${basePath}/:id`, async (c) => {
|
|
3172
3280
|
this.enforceApiKeyPermission(c, collection.slug);
|
|
3173
3281
|
const id = c.req.param("id");
|
|
3174
|
-
const queryDict = c.req.
|
|
3282
|
+
const queryDict = c.req.queries();
|
|
3175
3283
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3176
3284
|
const driver = c.get("driver") || this.driver;
|
|
3177
3285
|
const fetchService = this.getFetchService(driver);
|
|
@@ -3340,12 +3448,13 @@ class RestApiGenerator {
|
|
|
3340
3448
|
const driver = c.get("driver") || this.driver;
|
|
3341
3449
|
this.enforceApiKeyPermission(c, c.req.param("parent"));
|
|
3342
3450
|
if (parsed.entityId === "count") {
|
|
3343
|
-
const queryDict = c.req.
|
|
3451
|
+
const queryDict = c.req.queries();
|
|
3344
3452
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3453
|
+
const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : void 0;
|
|
3345
3454
|
const total = driver.countEntities ? await driver.countEntities({
|
|
3346
3455
|
path: parsed.collectionPath,
|
|
3347
3456
|
filter: queryOptions.where,
|
|
3348
|
-
searchString
|
|
3457
|
+
searchString
|
|
3349
3458
|
}) : 0;
|
|
3350
3459
|
return c.json({
|
|
3351
3460
|
count: total
|
|
@@ -3358,15 +3467,16 @@ class RestApiGenerator {
|
|
|
3358
3467
|
if (!entity) throw ApiError.notFound("Entity not found");
|
|
3359
3468
|
return c.json(this.flattenEntity(entity));
|
|
3360
3469
|
} else {
|
|
3361
|
-
const queryDict = c.req.
|
|
3470
|
+
const queryDict = c.req.queries();
|
|
3362
3471
|
const queryOptions = parseQueryOptions(queryDict);
|
|
3472
|
+
const searchString = Array.isArray(queryDict.searchString) ? queryDict.searchString[queryDict.searchString.length - 1] : void 0;
|
|
3363
3473
|
const entities = await driver.fetchCollection({
|
|
3364
3474
|
path: parsed.collectionPath,
|
|
3365
3475
|
filter: queryOptions.where,
|
|
3366
3476
|
limit: queryOptions.limit,
|
|
3367
3477
|
orderBy: queryOptions.orderBy?.[0]?.field,
|
|
3368
3478
|
order: queryOptions.orderBy?.[0]?.direction === "desc" ? "desc" : "asc",
|
|
3369
|
-
searchString
|
|
3479
|
+
searchString
|
|
3370
3480
|
});
|
|
3371
3481
|
return c.json({
|
|
3372
3482
|
data: entities.map((e) => this.flattenEntity(e)),
|
|
@@ -13035,11 +13145,6 @@ function createAdminRoutes(config) {
|
|
|
13035
13145
|
const results = await Promise.all(users.map((u) => applyUserAfterRead(u, ctx)));
|
|
13036
13146
|
return results.filter((u) => u !== null);
|
|
13037
13147
|
}
|
|
13038
|
-
async function applyRoleAfterReadBatch(roles, ctx) {
|
|
13039
|
-
if (!hooks?.roles?.afterRead) return roles;
|
|
13040
|
-
const results = await Promise.all(roles.map((r) => hooks.roles.afterRead(r, ctx)));
|
|
13041
|
-
return results.filter((r) => r !== null);
|
|
13042
|
-
}
|
|
13043
13148
|
function toAdminUser(u, roles) {
|
|
13044
13149
|
return {
|
|
13045
13150
|
uid: u.id,
|
|
@@ -13398,90 +13503,6 @@ function createAdminRoutes(config) {
|
|
|
13398
13503
|
success: true
|
|
13399
13504
|
});
|
|
13400
13505
|
});
|
|
13401
|
-
router.get("/roles", requireAdmin, async (c) => {
|
|
13402
|
-
const roles = await authRepo.listRoles();
|
|
13403
|
-
const hookCtx = buildHookContext(c, "GET");
|
|
13404
|
-
let adminRoles = roles.map((r) => ({
|
|
13405
|
-
id: r.id,
|
|
13406
|
-
name: r.name,
|
|
13407
|
-
isAdmin: r.isAdmin,
|
|
13408
|
-
defaultPermissions: r.defaultPermissions
|
|
13409
|
-
}));
|
|
13410
|
-
adminRoles = await applyRoleAfterReadBatch(adminRoles, hookCtx);
|
|
13411
|
-
return c.json({
|
|
13412
|
-
roles: adminRoles
|
|
13413
|
-
});
|
|
13414
|
-
});
|
|
13415
|
-
router.get("/roles/:roleId", requireAdmin, async (c) => {
|
|
13416
|
-
const roleId = c.req.param("roleId");
|
|
13417
|
-
const role = await authRepo.getRoleById(roleId);
|
|
13418
|
-
if (!role) {
|
|
13419
|
-
throw ApiError.notFound("Role not found");
|
|
13420
|
-
}
|
|
13421
|
-
return c.json({
|
|
13422
|
-
role
|
|
13423
|
-
});
|
|
13424
|
-
});
|
|
13425
|
-
router.post("/roles", requireAdmin, async (c) => {
|
|
13426
|
-
const body = await c.req.json();
|
|
13427
|
-
const {
|
|
13428
|
-
id,
|
|
13429
|
-
name: name2,
|
|
13430
|
-
isAdmin,
|
|
13431
|
-
defaultPermissions
|
|
13432
|
-
} = body;
|
|
13433
|
-
if (!id || !name2) {
|
|
13434
|
-
throw ApiError.badRequest("Role ID and name are required", "INVALID_INPUT");
|
|
13435
|
-
}
|
|
13436
|
-
const existing = await authRepo.getRoleById(id);
|
|
13437
|
-
if (existing) {
|
|
13438
|
-
throw ApiError.conflict("Role already exists", "ROLE_EXISTS");
|
|
13439
|
-
}
|
|
13440
|
-
const role = await authRepo.createRole({
|
|
13441
|
-
id,
|
|
13442
|
-
name: name2,
|
|
13443
|
-
isAdmin: isAdmin ?? false,
|
|
13444
|
-
defaultPermissions: defaultPermissions ?? null
|
|
13445
|
-
});
|
|
13446
|
-
return c.json({
|
|
13447
|
-
role
|
|
13448
|
-
}, 201);
|
|
13449
|
-
});
|
|
13450
|
-
router.put("/roles/:roleId", requireAdmin, async (c) => {
|
|
13451
|
-
const roleId = c.req.param("roleId");
|
|
13452
|
-
const body = await c.req.json();
|
|
13453
|
-
const {
|
|
13454
|
-
name: name2,
|
|
13455
|
-
isAdmin,
|
|
13456
|
-
defaultPermissions
|
|
13457
|
-
} = body;
|
|
13458
|
-
const existing = await authRepo.getRoleById(roleId);
|
|
13459
|
-
if (!existing) {
|
|
13460
|
-
throw ApiError.notFound("Role not found");
|
|
13461
|
-
}
|
|
13462
|
-
const role = await authRepo.updateRole(roleId, {
|
|
13463
|
-
name: name2,
|
|
13464
|
-
isAdmin,
|
|
13465
|
-
defaultPermissions
|
|
13466
|
-
});
|
|
13467
|
-
return c.json({
|
|
13468
|
-
role
|
|
13469
|
-
});
|
|
13470
|
-
});
|
|
13471
|
-
router.delete("/roles/:roleId", requireAdmin, async (c) => {
|
|
13472
|
-
const roleId = c.req.param("roleId");
|
|
13473
|
-
if (["admin", "editor", "viewer"].includes(roleId)) {
|
|
13474
|
-
throw ApiError.badRequest("Cannot delete built-in roles", "BUILTIN_ROLE");
|
|
13475
|
-
}
|
|
13476
|
-
const existing = await authRepo.getRoleById(roleId);
|
|
13477
|
-
if (!existing) {
|
|
13478
|
-
throw ApiError.notFound("Role not found");
|
|
13479
|
-
}
|
|
13480
|
-
await authRepo.deleteRole(roleId);
|
|
13481
|
-
return c.json({
|
|
13482
|
-
success: true
|
|
13483
|
-
});
|
|
13484
|
-
});
|
|
13485
13506
|
return router;
|
|
13486
13507
|
}
|
|
13487
13508
|
function createBuiltinAuthAdapter(config) {
|
|
@@ -13525,8 +13546,7 @@ function createBuiltinAuthAdapter(config) {
|
|
|
13525
13546
|
const extendedPayload = payload;
|
|
13526
13547
|
let roles = payload.roles || [];
|
|
13527
13548
|
try {
|
|
13528
|
-
|
|
13529
|
-
roles = userRoles.map((r) => r.id);
|
|
13549
|
+
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
13530
13550
|
} catch {
|
|
13531
13551
|
}
|
|
13532
13552
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
@@ -13556,8 +13576,7 @@ function createBuiltinAuthAdapter(config) {
|
|
|
13556
13576
|
const extendedPayload = payload;
|
|
13557
13577
|
let roles = payload.roles || [];
|
|
13558
13578
|
try {
|
|
13559
|
-
|
|
13560
|
-
roles = userRoles.map((r) => r.id);
|
|
13579
|
+
roles = await authRepository.getUserRoleIds(payload.userId);
|
|
13561
13580
|
} catch {
|
|
13562
13581
|
}
|
|
13563
13582
|
const isAdmin = roles.some((r) => r === "admin" || r === "schema-admin");
|
|
@@ -13571,7 +13590,6 @@ function createBuiltinAuthAdapter(config) {
|
|
|
13571
13590
|
};
|
|
13572
13591
|
},
|
|
13573
13592
|
userManagement: createUserManagementFromRepo(authRepository, resolvedOps, authHooks),
|
|
13574
|
-
roleManagement: createRoleManagementFromRepo(authRepository),
|
|
13575
13593
|
createAuthRoutes() {
|
|
13576
13594
|
return createAuthRoutes({
|
|
13577
13595
|
authRepo: authRepository,
|
|
@@ -13687,43 +13705,13 @@ function createUserManagementFromRepo(repo, resolvedOps, authHooks) {
|
|
|
13687
13705
|
}
|
|
13688
13706
|
},
|
|
13689
13707
|
async getUserRoles(userId) {
|
|
13690
|
-
|
|
13691
|
-
return roles.map(toAuthRoleData);
|
|
13708
|
+
return repo.getUserRoleIds(userId);
|
|
13692
13709
|
},
|
|
13693
13710
|
async setUserRoles(userId, roleIds) {
|
|
13694
13711
|
await repo.setUserRoles(userId, roleIds);
|
|
13695
13712
|
}
|
|
13696
13713
|
};
|
|
13697
13714
|
}
|
|
13698
|
-
function createRoleManagementFromRepo(repo) {
|
|
13699
|
-
return {
|
|
13700
|
-
async listRoles() {
|
|
13701
|
-
const roles = await repo.listRoles();
|
|
13702
|
-
return roles.map(toAuthRoleData);
|
|
13703
|
-
},
|
|
13704
|
-
async getRoleById(id) {
|
|
13705
|
-
const role = await repo.getRoleById(id);
|
|
13706
|
-
return role ? toAuthRoleData(role) : null;
|
|
13707
|
-
},
|
|
13708
|
-
async createRole(data) {
|
|
13709
|
-
const role = await repo.createRole({
|
|
13710
|
-
id: data.id,
|
|
13711
|
-
name: data.name,
|
|
13712
|
-
isAdmin: data.isAdmin,
|
|
13713
|
-
defaultPermissions: data.defaultPermissions,
|
|
13714
|
-
collectionPermissions: data.collectionPermissions
|
|
13715
|
-
});
|
|
13716
|
-
return toAuthRoleData(role);
|
|
13717
|
-
},
|
|
13718
|
-
async updateRole(id, data) {
|
|
13719
|
-
const role = await repo.updateRole(id, data);
|
|
13720
|
-
return role ? toAuthRoleData(role) : null;
|
|
13721
|
-
},
|
|
13722
|
-
async deleteRole(id) {
|
|
13723
|
-
await repo.deleteRole(id);
|
|
13724
|
-
}
|
|
13725
|
-
};
|
|
13726
|
-
}
|
|
13727
13715
|
function toAuthUserData(user) {
|
|
13728
13716
|
return {
|
|
13729
13717
|
id: user.id,
|
|
@@ -13736,15 +13724,6 @@ function toAuthUserData(user) {
|
|
|
13736
13724
|
updatedAt: user.updatedAt
|
|
13737
13725
|
};
|
|
13738
13726
|
}
|
|
13739
|
-
function toAuthRoleData(role) {
|
|
13740
|
-
return {
|
|
13741
|
-
id: role.id,
|
|
13742
|
-
name: role.name,
|
|
13743
|
-
isAdmin: role.isAdmin,
|
|
13744
|
-
defaultPermissions: role.defaultPermissions,
|
|
13745
|
-
collectionPermissions: role.collectionPermissions
|
|
13746
|
-
};
|
|
13747
|
-
}
|
|
13748
13727
|
function configureLogLevel(logLevel) {
|
|
13749
13728
|
const LOG_LEVEL = logLevel || process.env.LOG_LEVEL || "info";
|
|
13750
13729
|
const logLevels = {
|
|
@@ -24927,7 +24906,6 @@ function createCustomAuthAdapter(options2) {
|
|
|
24927
24906
|
verifyRequest: options2.verifyRequest,
|
|
24928
24907
|
verifyToken: resolvedVerifyToken,
|
|
24929
24908
|
userManagement: options2.userManagement,
|
|
24930
|
-
roleManagement: options2.roleManagement,
|
|
24931
24909
|
getCapabilities() {
|
|
24932
24910
|
return defaultCapabilities;
|
|
24933
24911
|
}
|
|
@@ -26225,15 +26203,33 @@ function normalizeWhereValue(value) {
|
|
|
26225
26203
|
if (value === null) return "eq.null";
|
|
26226
26204
|
if (typeof value === "boolean") return `eq.${value}`;
|
|
26227
26205
|
if (typeof value === "number") return String(value);
|
|
26228
|
-
if (Array.isArray(value)
|
|
26229
|
-
const [
|
|
26230
|
-
const
|
|
26231
|
-
if (
|
|
26232
|
-
|
|
26233
|
-
|
|
26206
|
+
if (Array.isArray(value)) {
|
|
26207
|
+
const conditions = Array.isArray(value[0]) ? value : [value];
|
|
26208
|
+
const [rawOp, val] = conditions[0] || [];
|
|
26209
|
+
if (rawOp) {
|
|
26210
|
+
const op = OP_MAP[rawOp] ?? rawOp;
|
|
26211
|
+
if (val === null) return `${op}.null`;
|
|
26212
|
+
if (Array.isArray(val)) return `${op}.(${val.join(",")})`;
|
|
26213
|
+
return `${op}.${val}`;
|
|
26214
|
+
}
|
|
26234
26215
|
}
|
|
26235
26216
|
return String(value);
|
|
26236
26217
|
}
|
|
26218
|
+
function serializeLogicalCondition(cond2) {
|
|
26219
|
+
if ("type" in cond2) {
|
|
26220
|
+
const sub = cond2.conditions.map(serializeLogicalCondition).join(",");
|
|
26221
|
+
return `${cond2.type}(${sub})`;
|
|
26222
|
+
} else {
|
|
26223
|
+
const op = OP_MAP[cond2.operator] ?? cond2.operator;
|
|
26224
|
+
let formattedValue = cond2.value;
|
|
26225
|
+
if (Array.isArray(cond2.value)) {
|
|
26226
|
+
formattedValue = `(${cond2.value.join(",")})`;
|
|
26227
|
+
} else if (cond2.value === null) {
|
|
26228
|
+
formattedValue = "null";
|
|
26229
|
+
}
|
|
26230
|
+
return `${cond2.column}.${op}.${formattedValue}`;
|
|
26231
|
+
}
|
|
26232
|
+
}
|
|
26237
26233
|
function buildQueryString(params) {
|
|
26238
26234
|
if (!params) return "";
|
|
26239
26235
|
const parts = [];
|
|
@@ -26249,10 +26245,22 @@ function buildQueryString(params) {
|
|
|
26249
26245
|
if (params.include && params.include.length > 0) {
|
|
26250
26246
|
parts.push(`include=${encodeURIComponent(params.include.join(","))}`);
|
|
26251
26247
|
}
|
|
26248
|
+
if (params.logical) {
|
|
26249
|
+
const root = params.logical;
|
|
26250
|
+
const serialized = root.conditions.map(serializeLogicalCondition).join(",");
|
|
26251
|
+
parts.push(`${root.type}=${encodeURIComponent(`(${serialized})`)}`);
|
|
26252
|
+
}
|
|
26252
26253
|
if (params.where) {
|
|
26253
26254
|
for (const [field, value] of Object.entries(params.where)) {
|
|
26254
|
-
|
|
26255
|
-
|
|
26255
|
+
if (Array.isArray(value) && value.length > 0 && Array.isArray(value[0])) {
|
|
26256
|
+
for (const subVal of value) {
|
|
26257
|
+
const normalized2 = normalizeWhereValue(subVal);
|
|
26258
|
+
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(normalized2)}`);
|
|
26259
|
+
}
|
|
26260
|
+
} else {
|
|
26261
|
+
const normalized2 = normalizeWhereValue(value);
|
|
26262
|
+
parts.push(`${encodeURIComponent(field)}=${encodeURIComponent(normalized2)}`);
|
|
26263
|
+
}
|
|
26256
26264
|
}
|
|
26257
26265
|
}
|
|
26258
26266
|
return parts.length > 0 ? "?" + parts.join("&") : "";
|
|
@@ -26927,33 +26935,6 @@ function createAdmin(transport, options2) {
|
|
|
26927
26935
|
method: "DELETE"
|
|
26928
26936
|
});
|
|
26929
26937
|
}
|
|
26930
|
-
async function listRoles() {
|
|
26931
|
-
return transport.request(adminPath + "/roles", {
|
|
26932
|
-
method: "GET"
|
|
26933
|
-
});
|
|
26934
|
-
}
|
|
26935
|
-
async function getRole(roleId) {
|
|
26936
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
26937
|
-
method: "GET"
|
|
26938
|
-
});
|
|
26939
|
-
}
|
|
26940
|
-
async function createRole(data) {
|
|
26941
|
-
return transport.request(adminPath + "/roles", {
|
|
26942
|
-
method: "POST",
|
|
26943
|
-
body: JSON.stringify(data)
|
|
26944
|
-
});
|
|
26945
|
-
}
|
|
26946
|
-
async function updateRole(roleId, data) {
|
|
26947
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
26948
|
-
method: "PUT",
|
|
26949
|
-
body: JSON.stringify(data)
|
|
26950
|
-
});
|
|
26951
|
-
}
|
|
26952
|
-
async function deleteRole(roleId) {
|
|
26953
|
-
return transport.request(adminPath + "/roles/" + encodeURIComponent(roleId), {
|
|
26954
|
-
method: "DELETE"
|
|
26955
|
-
});
|
|
26956
|
-
}
|
|
26957
26938
|
async function bootstrap() {
|
|
26958
26939
|
return transport.request(adminPath + "/bootstrap", {
|
|
26959
26940
|
method: "POST"
|
|
@@ -26966,11 +26947,6 @@ function createAdmin(transport, options2) {
|
|
|
26966
26947
|
createUser,
|
|
26967
26948
|
updateUser,
|
|
26968
26949
|
deleteUser,
|
|
26969
|
-
listRoles,
|
|
26970
|
-
getRole,
|
|
26971
|
-
createRole,
|
|
26972
|
-
updateRole,
|
|
26973
|
-
deleteRole,
|
|
26974
26950
|
bootstrap
|
|
26975
26951
|
};
|
|
26976
26952
|
}
|
|
@@ -27036,11 +27012,18 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
27036
27012
|
};
|
|
27037
27013
|
},
|
|
27038
27014
|
async findById(id) {
|
|
27039
|
-
|
|
27040
|
-
|
|
27041
|
-
|
|
27042
|
-
|
|
27043
|
-
|
|
27015
|
+
try {
|
|
27016
|
+
const raw = await transport.request(`${basePath}/${encodeURIComponent(String(id))}`, {
|
|
27017
|
+
method: "GET"
|
|
27018
|
+
});
|
|
27019
|
+
if (!raw) return void 0;
|
|
27020
|
+
return rowToEntity(raw, slug);
|
|
27021
|
+
} catch (err) {
|
|
27022
|
+
if (err instanceof RebaseApiError && err.status === 404) {
|
|
27023
|
+
return void 0;
|
|
27024
|
+
}
|
|
27025
|
+
throw err;
|
|
27026
|
+
}
|
|
27044
27027
|
},
|
|
27045
27028
|
async create(data, id) {
|
|
27046
27029
|
const body = {
|
|
@@ -27080,8 +27063,12 @@ function createCollectionClient(transport, slug, ws) {
|
|
|
27080
27063
|
return raw.count ?? 0;
|
|
27081
27064
|
},
|
|
27082
27065
|
// Fluent builder instantiation
|
|
27083
|
-
where(
|
|
27084
|
-
|
|
27066
|
+
where(columnOrCondition, operator, value) {
|
|
27067
|
+
const builder = new QueryBuilder(client);
|
|
27068
|
+
if (typeof columnOrCondition === "object") {
|
|
27069
|
+
return builder.where(columnOrCondition);
|
|
27070
|
+
}
|
|
27071
|
+
return builder.where(columnOrCondition, operator, value);
|
|
27085
27072
|
},
|
|
27086
27073
|
orderBy(column, ascending) {
|
|
27087
27074
|
return new QueryBuilder(client).orderBy(column, ascending);
|
|
@@ -38359,13 +38346,35 @@ var createTransport = function(transporter, defaults) {
|
|
|
38359
38346
|
mailer2 = new Mailer(transporter, options2, defaults);
|
|
38360
38347
|
return mailer2;
|
|
38361
38348
|
};
|
|
38349
|
+
function getHostname(urlStr) {
|
|
38350
|
+
try {
|
|
38351
|
+
const url = new URL(urlStr.includes("://") ? urlStr : `https://${urlStr}`);
|
|
38352
|
+
return url.hostname;
|
|
38353
|
+
} catch {
|
|
38354
|
+
return void 0;
|
|
38355
|
+
}
|
|
38356
|
+
}
|
|
38362
38357
|
class SMTPEmailService {
|
|
38363
38358
|
transporter = null;
|
|
38364
38359
|
config;
|
|
38365
38360
|
constructor(config) {
|
|
38366
38361
|
this.config = config;
|
|
38367
38362
|
if (config.smtp) {
|
|
38363
|
+
let smtpName = config.smtp.name;
|
|
38364
|
+
if (!smtpName) {
|
|
38365
|
+
const urlsToTry = [process.env.FRONTEND_URL, config.resetPasswordUrl, config.verifyEmailUrl];
|
|
38366
|
+
for (const urlStr of urlsToTry) {
|
|
38367
|
+
if (urlStr) {
|
|
38368
|
+
const hostname = getHostname(urlStr);
|
|
38369
|
+
if (hostname) {
|
|
38370
|
+
smtpName = hostname;
|
|
38371
|
+
break;
|
|
38372
|
+
}
|
|
38373
|
+
}
|
|
38374
|
+
}
|
|
38375
|
+
}
|
|
38368
38376
|
this.transporter = createTransport({
|
|
38377
|
+
name: smtpName,
|
|
38369
38378
|
host: config.smtp.host,
|
|
38370
38379
|
port: config.smtp.port,
|
|
38371
38380
|
secure: config.smtp.secure ?? config.smtp.port === 465,
|
|
@@ -38515,6 +38524,14 @@ async function _initializeRebaseBackend(config) {
|
|
|
38515
38524
|
dir: config.collectionsDir
|
|
38516
38525
|
});
|
|
38517
38526
|
}
|
|
38527
|
+
if (config.defaultSecurityRules?.length) {
|
|
38528
|
+
for (const collection of activeCollections) {
|
|
38529
|
+
if (isPostgresCollection(collection) && (!collection.securityRules || collection.securityRules.length === 0)) {
|
|
38530
|
+
collection.securityRules = config.defaultSecurityRules;
|
|
38531
|
+
}
|
|
38532
|
+
}
|
|
38533
|
+
logger.info("Default security rules applied to collections without explicit rules");
|
|
38534
|
+
}
|
|
38518
38535
|
const realtimeServices = {};
|
|
38519
38536
|
const delegates = {};
|
|
38520
38537
|
let bootstrappers = config.bootstrappers || [];
|
|
@@ -38583,8 +38600,7 @@ async function _initializeRebaseBackend(config) {
|
|
|
38583
38600
|
id: authAdapter.id
|
|
38584
38601
|
});
|
|
38585
38602
|
authConfigResult = {
|
|
38586
|
-
userService: authAdapter.userManagement ?? {}
|
|
38587
|
-
roleService: authAdapter.roleManagement ?? {}
|
|
38603
|
+
userService: authAdapter.userManagement ?? {}
|
|
38588
38604
|
};
|
|
38589
38605
|
} else {
|
|
38590
38606
|
const safeAuthConfig = config.auth;
|
|
@@ -38929,6 +38945,26 @@ async function _initializeRebaseBackend(config) {
|
|
|
38929
38945
|
logger.info("Email service attached to singleton", {
|
|
38930
38946
|
configured: emailService.isConfigured()
|
|
38931
38947
|
});
|
|
38948
|
+
if (emailService.isConfigured() && typeof emailService.verifyConnection === "function") {
|
|
38949
|
+
emailService.verifyConnection().then((success) => {
|
|
38950
|
+
if (!success) {
|
|
38951
|
+
logger.warn("Warning: SMTP connection verification failed. Email delivery may fail.");
|
|
38952
|
+
} else {
|
|
38953
|
+
logger.info("SMTP connection verified successfully.");
|
|
38954
|
+
}
|
|
38955
|
+
}).catch((err) => {
|
|
38956
|
+
logger.warn("Warning: SMTP connection verification failed. Email delivery may fail.", {
|
|
38957
|
+
error: err
|
|
38958
|
+
});
|
|
38959
|
+
});
|
|
38960
|
+
}
|
|
38961
|
+
}
|
|
38962
|
+
const driverAdmin = defaultBootstrapper.getAdmin?.(defaultDriverResult);
|
|
38963
|
+
if (isSQLAdmin(driverAdmin)) {
|
|
38964
|
+
Object.assign(serverClient, {
|
|
38965
|
+
sql: (query, options2) => driverAdmin.executeSql(query, options2)
|
|
38966
|
+
});
|
|
38967
|
+
logger.info("SQL capability attached to singleton");
|
|
38932
38968
|
}
|
|
38933
38969
|
_initRebase(serverClient);
|
|
38934
38970
|
logger.info("Rebase singleton initialized");
|