@m5kdev/backend 0.5.0 → 0.7.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/src/modules/ai/ai.service.d.ts +11 -13
- package/dist/src/modules/ai/ai.service.js +6 -6
- package/dist/src/modules/ai/ai.trpc.d.ts +1 -1
- package/dist/src/modules/auth/auth.lib.d.ts +8 -12
- package/dist/src/modules/auth/auth.lib.js +2 -2
- package/dist/src/modules/auth/auth.service.d.ts +17 -47
- package/dist/src/modules/auth/auth.service.js +79 -66
- package/dist/src/modules/auth/auth.trpc.d.ts +16 -16
- package/dist/src/modules/base/base.abstract.d.ts +3 -2
- package/dist/src/modules/base/base.abstract.js +10 -1
- package/dist/src/modules/base/base.actor.d.ts +68 -0
- package/dist/src/modules/base/base.actor.js +99 -0
- package/dist/src/modules/base/base.actor.test.d.ts +1 -0
- package/dist/src/modules/base/base.actor.test.js +58 -0
- package/dist/src/modules/base/base.grants.d.ts +3 -7
- package/dist/src/modules/base/base.grants.js +22 -10
- package/dist/src/modules/base/base.grants.test.js +16 -45
- package/dist/src/modules/base/base.procedure.d.ts +109 -0
- package/dist/src/modules/base/base.procedure.js +301 -0
- package/dist/src/modules/base/base.repository.d.ts +1 -0
- package/dist/src/modules/base/base.repository.js +12 -2
- package/dist/src/modules/base/base.service.d.ts +23 -23
- package/dist/src/modules/base/base.service.js +26 -12
- package/dist/src/modules/base/base.service.test.d.ts +1 -0
- package/dist/src/modules/base/base.service.test.js +443 -0
- package/dist/src/modules/billing/billing.service.d.ts +4 -25
- package/dist/src/modules/billing/billing.service.js +6 -6
- package/dist/src/modules/billing/billing.trpc.d.ts +2 -2
- package/dist/src/modules/billing/billing.trpc.js +4 -6
- package/dist/src/modules/connect/connect.repository.d.ts +3 -3
- package/dist/src/modules/connect/connect.service.d.ts +21 -13
- package/dist/src/modules/connect/connect.service.js +10 -8
- package/dist/src/modules/connect/connect.trpc.d.ts +2 -2
- package/dist/src/modules/recurrence/recurrence.service.d.ts +59 -8
- package/dist/src/modules/recurrence/recurrence.service.js +16 -14
- package/dist/src/modules/recurrence/recurrence.trpc.d.ts +3 -3
- package/dist/src/modules/recurrence/recurrence.trpc.js +1 -1
- package/dist/src/modules/social/social.service.d.ts +3 -4
- package/dist/src/modules/social/social.service.js +3 -3
- package/dist/src/modules/tag/tag.repository.js +27 -26
- package/dist/src/modules/tag/tag.service.d.ts +90 -15
- package/dist/src/modules/tag/tag.service.js +20 -12
- package/dist/src/modules/tag/tag.trpc.d.ts +3 -3
- package/dist/src/modules/workflow/workflow.service.d.ts +48 -8
- package/dist/src/modules/workflow/workflow.service.js +6 -6
- package/dist/src/modules/workflow/workflow.trpc.d.ts +2 -2
- package/dist/src/types.d.ts +19 -19
- package/dist/src/utils/trpc.d.ts +31 -41
- package/dist/src/utils/trpc.js +95 -0
- package/dist/src/utils/trpc.test.d.ts +1 -0
- package/dist/src/utils/trpc.test.js +154 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createServiceProcedureBuilder = createServiceProcedureBuilder;
|
|
4
|
+
exports.createPermissionServiceProcedureBuilder = createPermissionServiceProcedureBuilder;
|
|
5
|
+
const neverthrow_1 = require("neverthrow");
|
|
6
|
+
const base_actor_1 = require("./base.actor");
|
|
7
|
+
const DEFAULT_CONTEXT_FILTER_INCLUDE = [
|
|
8
|
+
"user",
|
|
9
|
+
];
|
|
10
|
+
function isServerResult(value) {
|
|
11
|
+
return (typeof value === "object" &&
|
|
12
|
+
value !== null &&
|
|
13
|
+
"isErr" in value &&
|
|
14
|
+
typeof value.isErr === "function" &&
|
|
15
|
+
"isOk" in value &&
|
|
16
|
+
typeof value.isOk === "function");
|
|
17
|
+
}
|
|
18
|
+
async function normalizeProcedureResult(result) {
|
|
19
|
+
const resolved = await result;
|
|
20
|
+
return isServerResult(resolved) ? resolved : (0, neverthrow_1.ok)(resolved);
|
|
21
|
+
}
|
|
22
|
+
function assertUniqueStepName(steps, stepName) {
|
|
23
|
+
if (steps.some((step) => step.stepName === stepName)) {
|
|
24
|
+
throw new Error(`Duplicate service procedure step name: ${stepName}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function hasStepName(steps, stepName) {
|
|
28
|
+
return steps.some((step) => step.stepName === stepName);
|
|
29
|
+
}
|
|
30
|
+
function getContextFilterInclude(include = DEFAULT_CONTEXT_FILTER_INCLUDE) {
|
|
31
|
+
return {
|
|
32
|
+
user: include.includes("user"),
|
|
33
|
+
organization: include.includes("organization"),
|
|
34
|
+
team: include.includes("team"),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function getFailureStage(code) {
|
|
38
|
+
return code === "FORBIDDEN" || code === "UNAUTHORIZED" ? "forbidden" : "error";
|
|
39
|
+
}
|
|
40
|
+
function logProcedureStage(host, procedureName, ctx, stage, { stepName, durationMs, errorCode, } = {}) {
|
|
41
|
+
host.logger.debug({
|
|
42
|
+
procedureName,
|
|
43
|
+
stage,
|
|
44
|
+
stepName,
|
|
45
|
+
durationMs,
|
|
46
|
+
errorCode,
|
|
47
|
+
hasActor: Boolean(ctx.actor),
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function requireProcedureActor(host, ctx, scope) {
|
|
51
|
+
if (!ctx.actor) {
|
|
52
|
+
return host.error("UNAUTHORIZED", "Unauthorized");
|
|
53
|
+
}
|
|
54
|
+
if (!(0, base_actor_1.validateActor)(ctx.actor, scope)) {
|
|
55
|
+
return host.error("FORBIDDEN", "Forbidden");
|
|
56
|
+
}
|
|
57
|
+
return (0, neverthrow_1.ok)(ctx.actor);
|
|
58
|
+
}
|
|
59
|
+
function createRequireAuthStep(host, scope = "user") {
|
|
60
|
+
return {
|
|
61
|
+
stage: "auth",
|
|
62
|
+
stepName: "auth",
|
|
63
|
+
run: async ({ ctx }) => {
|
|
64
|
+
return requireProcedureActor(host, ctx, scope);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function createUseStep(stepName, step) {
|
|
69
|
+
return {
|
|
70
|
+
stage: "use",
|
|
71
|
+
stepName,
|
|
72
|
+
run: async (args) => normalizeProcedureResult(step(args)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function createInputStep(stepName, step) {
|
|
76
|
+
return {
|
|
77
|
+
stage: "input",
|
|
78
|
+
stepName,
|
|
79
|
+
run: async (args) => normalizeProcedureResult(step(args)),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function createContextFilterStep(host, include) {
|
|
83
|
+
const contextInclude = getContextFilterInclude(include);
|
|
84
|
+
const requiredScope = contextInclude.team
|
|
85
|
+
? "team"
|
|
86
|
+
: contextInclude.organization
|
|
87
|
+
? "organization"
|
|
88
|
+
: "user";
|
|
89
|
+
return {
|
|
90
|
+
stage: "input",
|
|
91
|
+
stepName: "contextFilter",
|
|
92
|
+
run: async ({ input, ctx }) => {
|
|
93
|
+
const actor = requireProcedureActor(host, ctx, requiredScope);
|
|
94
|
+
if (actor.isErr())
|
|
95
|
+
return actor;
|
|
96
|
+
return (0, neverthrow_1.ok)(host.addContextFilter(actor.value, contextInclude, input));
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function createAccessStep(host, config) {
|
|
101
|
+
return {
|
|
102
|
+
stage: "access",
|
|
103
|
+
stepName: "access",
|
|
104
|
+
run: async (args) => {
|
|
105
|
+
const typedArgs = args;
|
|
106
|
+
const actor = requireProcedureActor(host, typedArgs.ctx, "user");
|
|
107
|
+
if (actor.isErr())
|
|
108
|
+
return actor;
|
|
109
|
+
if ("entityStep" in config && typeof config.entityStep === "string") {
|
|
110
|
+
const entities = typedArgs.state[config.entityStep];
|
|
111
|
+
const hasPermission = host.checkPermission(actor.value, config.action, entities, config.grants);
|
|
112
|
+
if (!hasPermission) {
|
|
113
|
+
return host.error("FORBIDDEN");
|
|
114
|
+
}
|
|
115
|
+
return (0, neverthrow_1.ok)(entities);
|
|
116
|
+
}
|
|
117
|
+
if (typeof config.entities === "function") {
|
|
118
|
+
const resolveEntities = config.entities;
|
|
119
|
+
let loadedEntities;
|
|
120
|
+
const permission = await host.checkPermissionAsync(actor.value, config.action, async () => {
|
|
121
|
+
const entityResult = await normalizeProcedureResult(resolveEntities(typedArgs));
|
|
122
|
+
if (entityResult.isOk()) {
|
|
123
|
+
loadedEntities = entityResult.value;
|
|
124
|
+
}
|
|
125
|
+
return entityResult;
|
|
126
|
+
}, config.grants);
|
|
127
|
+
if (permission.isErr()) {
|
|
128
|
+
return permission;
|
|
129
|
+
}
|
|
130
|
+
if (!permission.value) {
|
|
131
|
+
return host.error("FORBIDDEN");
|
|
132
|
+
}
|
|
133
|
+
return (0, neverthrow_1.ok)(loadedEntities);
|
|
134
|
+
}
|
|
135
|
+
const entities = config.entities;
|
|
136
|
+
const hasPermission = host.checkPermission(actor.value, config.action, entities, config.grants);
|
|
137
|
+
if (!hasPermission) {
|
|
138
|
+
return host.error("FORBIDDEN");
|
|
139
|
+
}
|
|
140
|
+
return (0, neverthrow_1.ok)(entities);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function createProcedureHandler(host, config, handler) {
|
|
145
|
+
return async (input, ctx) => host.throwableAsync(async () => {
|
|
146
|
+
const state = {};
|
|
147
|
+
const startTime = Date.now();
|
|
148
|
+
const typedCtx = ctx;
|
|
149
|
+
let currentInput = input;
|
|
150
|
+
logProcedureStage(host, config.name, typedCtx, "start");
|
|
151
|
+
try {
|
|
152
|
+
for (const step of config.steps) {
|
|
153
|
+
const stepResult = await step.run({
|
|
154
|
+
input: currentInput,
|
|
155
|
+
ctx: typedCtx,
|
|
156
|
+
state,
|
|
157
|
+
repository: host.repository,
|
|
158
|
+
service: host.service,
|
|
159
|
+
logger: host.logger,
|
|
160
|
+
});
|
|
161
|
+
if (stepResult.isErr()) {
|
|
162
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(stepResult.error.code), {
|
|
163
|
+
stepName: step.stepName,
|
|
164
|
+
durationMs: Date.now() - startTime,
|
|
165
|
+
errorCode: stepResult.error.code,
|
|
166
|
+
});
|
|
167
|
+
return stepResult;
|
|
168
|
+
}
|
|
169
|
+
state[step.stepName] = stepResult.value;
|
|
170
|
+
if (step.stage === "input") {
|
|
171
|
+
currentInput = stepResult.value;
|
|
172
|
+
}
|
|
173
|
+
if (step.stage === "auth") {
|
|
174
|
+
logProcedureStage(host, config.name, typedCtx, "auth_passed", {
|
|
175
|
+
stepName: step.stepName,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (step.stage === "access") {
|
|
179
|
+
logProcedureStage(host, config.name, typedCtx, "access_passed", {
|
|
180
|
+
stepName: step.stepName,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
const handlerResult = await normalizeProcedureResult(handler({
|
|
185
|
+
input: currentInput,
|
|
186
|
+
ctx: ctx,
|
|
187
|
+
state: state,
|
|
188
|
+
repository: host.repository,
|
|
189
|
+
service: host.service,
|
|
190
|
+
logger: host.logger,
|
|
191
|
+
}));
|
|
192
|
+
if (handlerResult.isErr()) {
|
|
193
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(handlerResult.error.code), {
|
|
194
|
+
durationMs: Date.now() - startTime,
|
|
195
|
+
errorCode: handlerResult.error.code,
|
|
196
|
+
});
|
|
197
|
+
return handlerResult;
|
|
198
|
+
}
|
|
199
|
+
logProcedureStage(host, config.name, typedCtx, "success", {
|
|
200
|
+
durationMs: Date.now() - startTime,
|
|
201
|
+
});
|
|
202
|
+
return handlerResult;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
const serverError = host.handleUnknownError(error);
|
|
206
|
+
logProcedureStage(host, config.name, typedCtx, getFailureStage(serverError.code), {
|
|
207
|
+
durationMs: Date.now() - startTime,
|
|
208
|
+
errorCode: serverError.code,
|
|
209
|
+
});
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function createServiceProcedureBuilder(host, config) {
|
|
215
|
+
function addContextFilter(include) {
|
|
216
|
+
const steps = hasStepName(config.steps, "auth")
|
|
217
|
+
? config.steps
|
|
218
|
+
: [...config.steps, createRequireAuthStep(host, "user")];
|
|
219
|
+
assertUniqueStepName(steps, "contextFilter");
|
|
220
|
+
return createServiceProcedureBuilder(host, {
|
|
221
|
+
...config,
|
|
222
|
+
steps: [...steps, createContextFilterStep(host, include)],
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
const builder = {
|
|
226
|
+
use(stepName, step) {
|
|
227
|
+
assertUniqueStepName(config.steps, stepName);
|
|
228
|
+
return createServiceProcedureBuilder(host, {
|
|
229
|
+
...config,
|
|
230
|
+
steps: [...config.steps, createUseStep(stepName, step)],
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
mapInput(stepName, step) {
|
|
234
|
+
assertUniqueStepName(config.steps, stepName);
|
|
235
|
+
return createServiceProcedureBuilder(host, {
|
|
236
|
+
...config,
|
|
237
|
+
steps: [...config.steps, createInputStep(stepName, step)],
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
addContextFilter,
|
|
241
|
+
requireAuth(scope) {
|
|
242
|
+
assertUniqueStepName(config.steps, "auth");
|
|
243
|
+
return createServiceProcedureBuilder(host, {
|
|
244
|
+
...config,
|
|
245
|
+
steps: [...config.steps, createRequireAuthStep(host, scope ?? "user")],
|
|
246
|
+
});
|
|
247
|
+
},
|
|
248
|
+
handle(handler) {
|
|
249
|
+
return createProcedureHandler(host, config, handler);
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
return builder;
|
|
253
|
+
}
|
|
254
|
+
function createPermissionServiceProcedureBuilder(host, config) {
|
|
255
|
+
function addContextFilter(include) {
|
|
256
|
+
const steps = hasStepName(config.steps, "auth")
|
|
257
|
+
? config.steps
|
|
258
|
+
: [...config.steps, createRequireAuthStep(host, "user")];
|
|
259
|
+
assertUniqueStepName(steps, "contextFilter");
|
|
260
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
261
|
+
...config,
|
|
262
|
+
steps: [...steps, createContextFilterStep(host, include)],
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function access(accessConfig) {
|
|
266
|
+
assertUniqueStepName(config.steps, "access");
|
|
267
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
268
|
+
...config,
|
|
269
|
+
steps: [...config.steps, createAccessStep(host, accessConfig)],
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
const builder = {
|
|
273
|
+
use(stepName, step) {
|
|
274
|
+
assertUniqueStepName(config.steps, stepName);
|
|
275
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
276
|
+
...config,
|
|
277
|
+
steps: [...config.steps, createUseStep(stepName, step)],
|
|
278
|
+
});
|
|
279
|
+
},
|
|
280
|
+
mapInput(stepName, step) {
|
|
281
|
+
assertUniqueStepName(config.steps, stepName);
|
|
282
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
283
|
+
...config,
|
|
284
|
+
steps: [...config.steps, createInputStep(stepName, step)],
|
|
285
|
+
});
|
|
286
|
+
},
|
|
287
|
+
addContextFilter,
|
|
288
|
+
requireAuth(scope) {
|
|
289
|
+
assertUniqueStepName(config.steps, "auth");
|
|
290
|
+
return createPermissionServiceProcedureBuilder(host, {
|
|
291
|
+
...config,
|
|
292
|
+
steps: [...config.steps, createRequireAuthStep(host, scope ?? "user")],
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
access,
|
|
296
|
+
handle(handler) {
|
|
297
|
+
return createProcedureHandler(host, config, handler);
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
return builder;
|
|
301
|
+
}
|
|
@@ -32,6 +32,7 @@ export declare class BaseRepository<O extends LibSQLDatabase<any>, S extends Rec
|
|
|
32
32
|
getConditionBuilder(): ConditionBuilder;
|
|
33
33
|
getConditionBuilder(table: undefined): ConditionBuilder;
|
|
34
34
|
getConditionBuilder<TTable extends SQLiteTableWithColumns<any>>(table: TTable): TableConditionBuilder<TTable>;
|
|
35
|
+
throwableQuery<T>(fn: () => Promise<T>): ServerResultAsync<T>;
|
|
35
36
|
withPagination<TQuery>(query: TQuery, { page, limit }: Pick<QueryInput, "page" | "limit">): TQuery;
|
|
36
37
|
withSorting<TTable extends SQLiteTableWithColumns<any>, TQuery>(query: TQuery, { sort, order }: Pick<QueryInput, "sort" | "order">, table?: TTable): TQuery;
|
|
37
38
|
withSortingAndPagination<TTable extends SQLiteTableWithColumns<any>, TQuery>(query: TQuery, { sort, order, page, limit }: Pick<QueryInput, "sort" | "order" | "page" | "limit">, table?: TTable): TQuery;
|
|
@@ -3,11 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BaseExternaRepository = exports.BaseTableRepository = exports.BaseRepository = exports.arrayContains = exports.TableConditionBuilder = exports.ConditionBuilder = void 0;
|
|
4
4
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
5
5
|
const neverthrow_1 = require("neverthrow");
|
|
6
|
-
const
|
|
7
|
-
const base_dto_1 = require("./base.dto");
|
|
6
|
+
const errors_1 = require("../../utils/errors");
|
|
8
7
|
const applyPagination_1 = require("../utils/applyPagination");
|
|
9
8
|
const applySorting_1 = require("../utils/applySorting");
|
|
10
9
|
const getConditionsFromFilters_1 = require("../utils/getConditionsFromFilters");
|
|
10
|
+
const base_abstract_1 = require("./base.abstract");
|
|
11
|
+
const base_dto_1 = require("./base.dto");
|
|
11
12
|
class ConditionBuilder {
|
|
12
13
|
conditions;
|
|
13
14
|
constructor(conditions = []) {
|
|
@@ -66,6 +67,15 @@ class BaseRepository extends base_abstract_1.Base {
|
|
|
66
67
|
}
|
|
67
68
|
return new TableConditionBuilder(table);
|
|
68
69
|
}
|
|
70
|
+
throwableQuery(fn) {
|
|
71
|
+
return this.throwablePromise(() => fn(), (error) => new errors_1.ServerError({
|
|
72
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
73
|
+
layer: "repository",
|
|
74
|
+
layerName: this.constructor.name,
|
|
75
|
+
message: "Database query failed",
|
|
76
|
+
cause: error,
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
69
79
|
withPagination(query, { page, limit }) {
|
|
70
80
|
return (0, applyPagination_1.applyPagination)(query, limit, page);
|
|
71
81
|
}
|
|
@@ -1,40 +1,40 @@
|
|
|
1
1
|
import type { QueryFilter, QueryInput } from "@m5kdev/commons/modules/schemas/query.schema";
|
|
2
|
-
import type { Context, Session, User } from "../auth/auth.lib";
|
|
3
2
|
import { Base } from "./base.abstract";
|
|
3
|
+
import { type AuthenticatedActor } from "./base.actor";
|
|
4
4
|
import type { ServerResult, ServerResultAsync } from "./base.dto";
|
|
5
5
|
import { type Entity, type ResourceActionGrant, type ResourceGrant } from "./base.grants";
|
|
6
|
-
import type
|
|
7
|
-
export
|
|
6
|
+
import { type PermissionServiceProcedureBuilder, type ServiceProcedureBuilder, type ServiceProcedureContext } from "./base.procedure";
|
|
7
|
+
export type { PermissionServiceProcedureBuilder, ServiceProcedure, ServiceProcedureAccessConfig, ServiceProcedureAccessEntitiesConfig, ServiceProcedureAccessStateConfig, ServiceProcedureArgs, ServiceProcedureBuilder, ServiceProcedureContext, ServiceProcedureContextFilteredInput, ServiceProcedureContextFilterScope, ServiceProcedureEntityStepName, ServiceProcedureInputMapper, } from "./base.procedure";
|
|
8
|
+
export declare class BaseService<Repositories extends Record<string, Base>, Services extends Record<string, Base>, DefaultContext extends ServiceProcedureContext = ServiceProcedureContext> extends Base {
|
|
8
9
|
repository: Repositories;
|
|
9
10
|
service: Services;
|
|
10
11
|
constructor(repository?: Repositories, service?: Services);
|
|
11
|
-
addUserFilter(value: string, query?:
|
|
12
|
-
|
|
12
|
+
addUserFilter(value: string, query?: undefined, columnId?: string, method?: QueryFilter["method"]): QueryInput;
|
|
13
|
+
addUserFilter<TQuery extends QueryInput>(value: string, query: TQuery, columnId?: string, method?: QueryFilter["method"]): TQuery;
|
|
14
|
+
protected procedure<TInput, TCtx extends ServiceProcedureContext = DefaultContext>(name: string): ServiceProcedureBuilder<TInput, TCtx, Repositories, Services>;
|
|
15
|
+
addContextFilter(actor: AuthenticatedActor, include?: {
|
|
13
16
|
user?: boolean;
|
|
14
17
|
organization?: boolean;
|
|
15
18
|
team?: boolean;
|
|
16
|
-
}, query?:
|
|
19
|
+
}, query?: undefined, map?: Record<string, {
|
|
17
20
|
columnId: string;
|
|
18
21
|
method: QueryFilter["method"];
|
|
19
22
|
}>): QueryInput;
|
|
23
|
+
addContextFilter<TQuery extends QueryInput>(actor: AuthenticatedActor, include: {
|
|
24
|
+
user?: boolean;
|
|
25
|
+
organization?: boolean;
|
|
26
|
+
team?: boolean;
|
|
27
|
+
} | undefined, query: TQuery, map?: Record<string, {
|
|
28
|
+
columnId: string;
|
|
29
|
+
method: QueryFilter["method"];
|
|
30
|
+
}>): TQuery;
|
|
20
31
|
}
|
|
21
|
-
export declare class BasePermissionService<Repositories extends Record<string,
|
|
32
|
+
export declare class BasePermissionService<Repositories extends Record<string, Base>, Services extends Record<string, Base>, DefaultContext extends ServiceProcedureContext = ServiceProcedureContext> extends BaseService<Repositories, Services, DefaultContext> {
|
|
22
33
|
grants: ResourceGrant[];
|
|
23
34
|
constructor(repository: Repositories, service: Services, grants?: ResourceGrant[]);
|
|
24
|
-
accessGuard<T extends Entity>(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
session: Session;
|
|
30
|
-
user: User;
|
|
31
|
-
}, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<true>;
|
|
32
|
-
checkPermission<T extends Entity>(ctx: {
|
|
33
|
-
session: Session;
|
|
34
|
-
user: User;
|
|
35
|
-
}, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): boolean;
|
|
36
|
-
checkPermissionAsync<T extends Entity>(ctx: {
|
|
37
|
-
session: Session;
|
|
38
|
-
user: User;
|
|
39
|
-
}, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<boolean>;
|
|
35
|
+
accessGuard<T extends Entity>(actor: AuthenticatedActor, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): ServerResult<true>;
|
|
36
|
+
accessGuardAsync<T extends Entity>(actor: AuthenticatedActor, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<true>;
|
|
37
|
+
protected procedure<TInput, TCtx extends ServiceProcedureContext = DefaultContext>(name: string): PermissionServiceProcedureBuilder<TInput, TCtx, Repositories, Services>;
|
|
38
|
+
checkPermission<T extends Entity>(actor: AuthenticatedActor, action: string, entities?: T | T[], grants?: ResourceActionGrant[]): boolean;
|
|
39
|
+
checkPermissionAsync<T extends Entity>(actor: AuthenticatedActor, action: string, getEntities: () => ServerResultAsync<T | T[] | undefined>, grants?: ResourceActionGrant[]): ServerResultAsync<boolean>;
|
|
40
40
|
}
|
|
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BasePermissionService = exports.BaseService = void 0;
|
|
4
4
|
const neverthrow_1 = require("neverthrow");
|
|
5
5
|
const base_abstract_1 = require("./base.abstract");
|
|
6
|
+
const base_actor_1 = require("./base.actor");
|
|
6
7
|
const base_grants_1 = require("./base.grants");
|
|
8
|
+
const base_procedure_1 = require("./base.procedure");
|
|
7
9
|
class BaseService extends base_abstract_1.Base {
|
|
8
10
|
repository;
|
|
9
11
|
service;
|
|
@@ -25,7 +27,10 @@ class BaseService extends base_abstract_1.Base {
|
|
|
25
27
|
? { ...query, filters: [...(query?.filters ?? []), userFilter] }
|
|
26
28
|
: { filters: [userFilter] };
|
|
27
29
|
}
|
|
28
|
-
|
|
30
|
+
procedure(name) {
|
|
31
|
+
return (0, base_procedure_1.createServiceProcedureBuilder)(this, { name, steps: [] });
|
|
32
|
+
}
|
|
33
|
+
addContextFilter(actor, include = {
|
|
29
34
|
user: true,
|
|
30
35
|
organization: false,
|
|
31
36
|
team: false,
|
|
@@ -49,23 +54,29 @@ class BaseService extends base_abstract_1.Base {
|
|
|
49
54
|
columnId: map.userId.columnId,
|
|
50
55
|
type: "string",
|
|
51
56
|
method: map.userId.method,
|
|
52
|
-
value:
|
|
57
|
+
value: actor.userId,
|
|
53
58
|
});
|
|
54
59
|
}
|
|
55
60
|
if (include.organization) {
|
|
61
|
+
if (!(0, base_actor_1.validateActor)(actor, "organization")) {
|
|
62
|
+
throw new Error("Organization-scoped context filter requires an organization actor");
|
|
63
|
+
}
|
|
56
64
|
filters.push({
|
|
57
65
|
columnId: map.organizationId.columnId,
|
|
58
66
|
type: "string",
|
|
59
67
|
method: map.organizationId.method,
|
|
60
|
-
value:
|
|
68
|
+
value: actor.organizationId,
|
|
61
69
|
});
|
|
62
70
|
}
|
|
63
71
|
if (include.team) {
|
|
72
|
+
if (!(0, base_actor_1.validateActor)(actor, "team")) {
|
|
73
|
+
throw new Error("Team-scoped context filter requires a team actor");
|
|
74
|
+
}
|
|
64
75
|
filters.push({
|
|
65
76
|
columnId: map.teamId.columnId,
|
|
66
77
|
type: "string",
|
|
67
78
|
method: map.teamId.method,
|
|
68
|
-
value:
|
|
79
|
+
value: actor.teamId,
|
|
69
80
|
});
|
|
70
81
|
}
|
|
71
82
|
return query ? { ...query, filters: [...(query?.filters ?? []), ...filters] } : { filters };
|
|
@@ -78,27 +89,30 @@ class BasePermissionService extends BaseService {
|
|
|
78
89
|
super(repository, service);
|
|
79
90
|
this.grants = grants;
|
|
80
91
|
}
|
|
81
|
-
accessGuard(
|
|
82
|
-
const hasPermission = this.checkPermission(
|
|
92
|
+
accessGuard(actor, action, entities, grants) {
|
|
93
|
+
const hasPermission = this.checkPermission(actor, action, entities, grants);
|
|
83
94
|
if (!hasPermission)
|
|
84
95
|
return this.error("FORBIDDEN");
|
|
85
96
|
return (0, neverthrow_1.ok)(true);
|
|
86
97
|
}
|
|
87
|
-
async accessGuardAsync(
|
|
88
|
-
const hasPermission = await this.checkPermissionAsync(
|
|
98
|
+
async accessGuardAsync(actor, action, getEntities, grants) {
|
|
99
|
+
const hasPermission = await this.checkPermissionAsync(actor, action, getEntities, grants);
|
|
89
100
|
if (hasPermission.isErr())
|
|
90
101
|
return (0, neverthrow_1.err)(hasPermission.error);
|
|
91
102
|
if (!hasPermission.value)
|
|
92
103
|
return this.error("FORBIDDEN");
|
|
93
104
|
return (0, neverthrow_1.ok)(true);
|
|
94
105
|
}
|
|
95
|
-
|
|
106
|
+
procedure(name) {
|
|
107
|
+
return (0, base_procedure_1.createPermissionServiceProcedureBuilder)(this, { name, steps: [] });
|
|
108
|
+
}
|
|
109
|
+
checkPermission(actor, action, entities, grants) {
|
|
96
110
|
const actionGrants = grants ?? this.grants.filter((grant) => grant.action === action);
|
|
97
|
-
return (0, base_grants_1.checkPermissionSync)(
|
|
111
|
+
return (0, base_grants_1.checkPermissionSync)(actor, actionGrants, entities);
|
|
98
112
|
}
|
|
99
|
-
async checkPermissionAsync(
|
|
113
|
+
async checkPermissionAsync(actor, action, getEntities, grants) {
|
|
100
114
|
const actionGrants = grants ?? this.grants.filter((grant) => grant.action === action);
|
|
101
|
-
const permission = await (0, base_grants_1.checkPermissionAsync)(
|
|
115
|
+
const permission = await (0, base_grants_1.checkPermissionAsync)(actor, actionGrants, getEntities);
|
|
102
116
|
if (permission.isErr())
|
|
103
117
|
return this.error("INTERNAL_SERVER_ERROR", "Failed to check permission", {
|
|
104
118
|
cause: permission.error,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|