@rhino-dev/rhino-nestjs 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +427 -0
- package/dist/blueprint/blueprint-parser.d.ts +130 -0
- package/dist/blueprint/blueprint-parser.js +276 -0
- package/dist/blueprint/blueprint-parser.js.map +1 -0
- package/dist/blueprint/blueprint-runner.d.ts +51 -0
- package/dist/blueprint/blueprint-runner.js +238 -0
- package/dist/blueprint/blueprint-runner.js.map +1 -0
- package/dist/blueprint/blueprint-validator.d.ts +19 -0
- package/dist/blueprint/blueprint-validator.js +163 -0
- package/dist/blueprint/blueprint-validator.js.map +1 -0
- package/dist/blueprint/generators/policy-generator.d.ts +40 -0
- package/dist/blueprint/generators/policy-generator.js +157 -0
- package/dist/blueprint/generators/policy-generator.js.map +1 -0
- package/dist/blueprint/generators/prisma-schema-generator.d.ts +43 -0
- package/dist/blueprint/generators/prisma-schema-generator.js +197 -0
- package/dist/blueprint/generators/prisma-schema-generator.js.map +1 -0
- package/dist/blueprint/generators/resource-definition-generator.d.ts +52 -0
- package/dist/blueprint/generators/resource-definition-generator.js +229 -0
- package/dist/blueprint/generators/resource-definition-generator.js.map +1 -0
- package/dist/blueprint/generators/seeder-generator.d.ts +21 -0
- package/dist/blueprint/generators/seeder-generator.js +135 -0
- package/dist/blueprint/generators/seeder-generator.js.map +1 -0
- package/dist/blueprint/generators/test-generator.d.ts +28 -0
- package/dist/blueprint/generators/test-generator.js +275 -0
- package/dist/blueprint/generators/test-generator.js.map +1 -0
- package/dist/blueprint/manifest-manager.d.ts +58 -0
- package/dist/blueprint/manifest-manager.js +137 -0
- package/dist/blueprint/manifest-manager.js.map +1 -0
- package/dist/cli/commands/blueprint.command.d.ts +16 -0
- package/dist/cli/commands/blueprint.command.js +35 -0
- package/dist/cli/commands/blueprint.command.js.map +1 -0
- package/dist/cli/commands/export-postman.command.d.ts +5 -0
- package/dist/cli/commands/export-postman.command.js +39 -0
- package/dist/cli/commands/export-postman.command.js.map +1 -0
- package/dist/cli/commands/export-types.command.d.ts +4 -0
- package/dist/cli/commands/export-types.command.js +36 -0
- package/dist/cli/commands/export-types.command.js.map +1 -0
- package/dist/cli/commands/generate.command.d.ts +6 -0
- package/dist/cli/commands/generate.command.js +212 -0
- package/dist/cli/commands/generate.command.js.map +1 -0
- package/dist/cli/commands/install.command.d.ts +8 -0
- package/dist/cli/commands/install.command.js +205 -0
- package/dist/cli/commands/install.command.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.js +123 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/io.d.ts +12 -0
- package/dist/cli/utils/io.js +74 -0
- package/dist/cli/utils/io.js.map +1 -0
- package/dist/cli/utils/prompt.d.ts +12 -0
- package/dist/cli/utils/prompt.js +78 -0
- package/dist/cli/utils/prompt.js.map +1 -0
- package/dist/constants/tokens.d.ts +3 -0
- package/dist/constants/tokens.js +7 -0
- package/dist/constants/tokens.js.map +1 -0
- package/dist/controllers/auth.controller.d.ts +59 -0
- package/dist/controllers/auth.controller.js +121 -0
- package/dist/controllers/auth.controller.js.map +1 -0
- package/dist/controllers/global.controller.d.ts +44 -0
- package/dist/controllers/global.controller.js +357 -0
- package/dist/controllers/global.controller.js.map +1 -0
- package/dist/controllers/invitation.controller.d.ts +30 -0
- package/dist/controllers/invitation.controller.js +111 -0
- package/dist/controllers/invitation.controller.js.map +1 -0
- package/dist/controllers/nested.controller.d.ts +12 -0
- package/dist/controllers/nested.controller.js +69 -0
- package/dist/controllers/nested.controller.js.map +1 -0
- package/dist/decorators/index.d.ts +33 -0
- package/dist/decorators/index.js +79 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/errors/rhino-exception.d.ts +24 -0
- package/dist/errors/rhino-exception.js +47 -0
- package/dist/errors/rhino-exception.js.map +1 -0
- package/dist/exporters/postman-exporter.d.ts +21 -0
- package/dist/exporters/postman-exporter.js +396 -0
- package/dist/exporters/postman-exporter.js.map +1 -0
- package/dist/exporters/typescript-exporter.d.ts +23 -0
- package/dist/exporters/typescript-exporter.js +129 -0
- package/dist/exporters/typescript-exporter.js.map +1 -0
- package/dist/guards/jwt-auth.guard.d.ts +16 -0
- package/dist/guards/jwt-auth.guard.js +61 -0
- package/dist/guards/jwt-auth.guard.js.map +1 -0
- package/dist/guards/resource-policy.guard.d.ts +12 -0
- package/dist/guards/resource-policy.guard.js +111 -0
- package/dist/guards/resource-policy.guard.js.map +1 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +171 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors/hidden-columns.interceptor.d.ts +18 -0
- package/dist/interceptors/hidden-columns.interceptor.js +66 -0
- package/dist/interceptors/hidden-columns.interceptor.js.map +1 -0
- package/dist/interceptors/response.interceptor.d.ts +19 -0
- package/dist/interceptors/response.interceptor.js +47 -0
- package/dist/interceptors/response.interceptor.js.map +1 -0
- package/dist/interfaces/rhino-config.interface.d.ts +103 -0
- package/dist/interfaces/rhino-config.interface.js +3 -0
- package/dist/interfaces/rhino-config.interface.js.map +1 -0
- package/dist/interfaces/rhino-request.interface.d.ts +12 -0
- package/dist/interfaces/rhino-request.interface.js +3 -0
- package/dist/interfaces/rhino-request.interface.js.map +1 -0
- package/dist/middleware/resolve-organization.middleware.d.ts +15 -0
- package/dist/middleware/resolve-organization.middleware.js +38 -0
- package/dist/middleware/resolve-organization.middleware.js.map +1 -0
- package/dist/middleware/route-group.middleware.d.ts +17 -0
- package/dist/middleware/route-group.middleware.js +60 -0
- package/dist/middleware/route-group.middleware.js.map +1 -0
- package/dist/middleware/tenant-route-rewrite.d.ts +76 -0
- package/dist/middleware/tenant-route-rewrite.js +124 -0
- package/dist/middleware/tenant-route-rewrite.js.map +1 -0
- package/dist/policies/resource-policy.d.ts +25 -0
- package/dist/policies/resource-policy.js +77 -0
- package/dist/policies/resource-policy.js.map +1 -0
- package/dist/prisma/prisma-soft-delete.extension.d.ts +16 -0
- package/dist/prisma/prisma-soft-delete.extension.js +57 -0
- package/dist/prisma/prisma-soft-delete.extension.js.map +1 -0
- package/dist/prisma/prisma-uuid.extension.d.ts +9 -0
- package/dist/prisma/prisma-uuid.extension.js +28 -0
- package/dist/prisma/prisma-uuid.extension.js.map +1 -0
- package/dist/prisma/prisma.service.d.ts +38 -0
- package/dist/prisma/prisma.service.js +100 -0
- package/dist/prisma/prisma.service.js.map +1 -0
- package/dist/rhino.config.d.ts +43 -0
- package/dist/rhino.config.js +130 -0
- package/dist/rhino.config.js.map +1 -0
- package/dist/rhino.module.d.ts +61 -0
- package/dist/rhino.module.js +268 -0
- package/dist/rhino.module.js.map +1 -0
- package/dist/services/audit.service.d.ts +35 -0
- package/dist/services/audit.service.js +92 -0
- package/dist/services/audit.service.js.map +1 -0
- package/dist/services/auth.service.d.ts +33 -0
- package/dist/services/auth.service.js +157 -0
- package/dist/services/auth.service.js.map +1 -0
- package/dist/services/invitation.service.d.ts +37 -0
- package/dist/services/invitation.service.js +143 -0
- package/dist/services/invitation.service.js.map +1 -0
- package/dist/services/nested.service.d.ts +34 -0
- package/dist/services/nested.service.js +163 -0
- package/dist/services/nested.service.js.map +1 -0
- package/dist/services/organization.service.d.ts +13 -0
- package/dist/services/organization.service.js +60 -0
- package/dist/services/organization.service.js.map +1 -0
- package/dist/services/query-builder.service.d.ts +31 -0
- package/dist/services/query-builder.service.js +151 -0
- package/dist/services/query-builder.service.js.map +1 -0
- package/dist/services/resource.service.d.ts +38 -0
- package/dist/services/resource.service.js +230 -0
- package/dist/services/resource.service.js.map +1 -0
- package/dist/services/route-registration.service.d.ts +40 -0
- package/dist/services/route-registration.service.js +68 -0
- package/dist/services/route-registration.service.js.map +1 -0
- package/dist/services/scope.service.d.ts +16 -0
- package/dist/services/scope.service.js +32 -0
- package/dist/services/scope.service.js.map +1 -0
- package/dist/services/serializer.service.d.ts +36 -0
- package/dist/services/serializer.service.js +90 -0
- package/dist/services/serializer.service.js.map +1 -0
- package/dist/services/validation.service.d.ts +50 -0
- package/dist/services/validation.service.js +203 -0
- package/dist/services/validation.service.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/utils/fk-chain-walker.d.ts +30 -0
- package/dist/utils/fk-chain-walker.js +23 -0
- package/dist/utils/fk-chain-walker.js.map +1 -0
- package/dist/utils/format.d.ts +7 -0
- package/dist/utils/format.js +28 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/model-builder.d.ts +64 -0
- package/dist/utils/model-builder.js +67 -0
- package/dist/utils/model-builder.js.map +1 -0
- package/dist/utils/permission-matcher.d.ts +46 -0
- package/dist/utils/permission-matcher.js +120 -0
- package/dist/utils/permission-matcher.js.map +1 -0
- package/dist/utils/scope-discovery.d.ts +34 -0
- package/dist/utils/scope-discovery.js +102 -0
- package/dist/utils/scope-discovery.js.map +1 -0
- package/package.json +64 -0
- package/stubs/skills/rhino-audit.md +184 -0
- package/stubs/skills/rhino-bugfix.md +117 -0
- package/stubs/skills/rhino-deploy.md +5 -0
- package/stubs/skills/rhino-docs.md +147 -0
- package/stubs/skills/rhino-feature.md +146 -0
- package/stubs/skills/rhino-migrate.md +181 -0
- package/stubs/skills/rhino-model.md +180 -0
- package/stubs/skills/rhino-plan.md +5 -0
- package/stubs/skills/rhino-policy.md +183 -0
- package/stubs/skills/rhino-refactor.md +147 -0
- package/stubs/skills/rhino-review.md +113 -0
- package/stubs/skills/rhino-scope.md +140 -0
- package/stubs/skills/rhino-test.md +233 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PrismaService } from '../prisma/prisma.service';
|
|
2
|
+
import { RhinoConfigService } from '../rhino.config';
|
|
3
|
+
import { QueryBuilderService } from './query-builder.service';
|
|
4
|
+
import { ScopeService } from './scope.service';
|
|
5
|
+
export interface FindAllResult {
|
|
6
|
+
items: any[];
|
|
7
|
+
total?: number;
|
|
8
|
+
page?: number;
|
|
9
|
+
perPage?: number;
|
|
10
|
+
lastPage?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface ResourceContext {
|
|
13
|
+
user?: any;
|
|
14
|
+
organization?: any;
|
|
15
|
+
orgIdentifierColumn?: string;
|
|
16
|
+
includeTrashed?: boolean;
|
|
17
|
+
onlyTrashed?: boolean;
|
|
18
|
+
}
|
|
19
|
+
export declare class ResourceService {
|
|
20
|
+
private readonly prisma;
|
|
21
|
+
private readonly config;
|
|
22
|
+
private readonly queryBuilder;
|
|
23
|
+
private readonly scopes?;
|
|
24
|
+
constructor(prisma: PrismaService, config: RhinoConfigService, queryBuilder: QueryBuilderService, scopes?: ScopeService);
|
|
25
|
+
private applyScopes;
|
|
26
|
+
private delegate;
|
|
27
|
+
/** Resolve Prisma where filter for the current org context. */
|
|
28
|
+
protected orgFilter(modelSlug: string, org?: any): Record<string, any> | null;
|
|
29
|
+
findAll(modelSlug: string, rawQuery: Record<string, any>, ctx?: ResourceContext): Promise<FindAllResult>;
|
|
30
|
+
findOne(modelSlug: string, id: string | number, rawQuery: Record<string, any>, ctx?: ResourceContext): Promise<any>;
|
|
31
|
+
create(modelSlug: string, data: Record<string, any>, ctx?: ResourceContext): Promise<any>;
|
|
32
|
+
update(modelSlug: string, id: string | number, data: Record<string, any>, ctx?: ResourceContext): Promise<any>;
|
|
33
|
+
delete(modelSlug: string, id: string | number, ctx?: ResourceContext): Promise<boolean>;
|
|
34
|
+
restore(modelSlug: string, id: string | number, ctx?: ResourceContext): Promise<boolean>;
|
|
35
|
+
forceDelete(modelSlug: string, id: string | number, ctx?: ResourceContext): Promise<boolean>;
|
|
36
|
+
protected castId(id: string | number, hasUuid?: boolean): string | number;
|
|
37
|
+
protected mergeWhere(...parts: (Record<string, any> | null | undefined)[]): Record<string, any>;
|
|
38
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.ResourceService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const prisma_service_1 = require("../prisma/prisma.service");
|
|
18
|
+
const rhino_config_1 = require("../rhino.config");
|
|
19
|
+
const query_builder_service_1 = require("./query-builder.service");
|
|
20
|
+
const scope_service_1 = require("./scope.service");
|
|
21
|
+
const permission_matcher_1 = require("../utils/permission-matcher");
|
|
22
|
+
let ResourceService = class ResourceService {
|
|
23
|
+
prisma;
|
|
24
|
+
config;
|
|
25
|
+
queryBuilder;
|
|
26
|
+
scopes;
|
|
27
|
+
constructor(prisma, config, queryBuilder, scopes) {
|
|
28
|
+
this.prisma = prisma;
|
|
29
|
+
this.config = config;
|
|
30
|
+
this.queryBuilder = queryBuilder;
|
|
31
|
+
this.scopes = scopes;
|
|
32
|
+
}
|
|
33
|
+
applyScopes(where, modelSlug, ctx) {
|
|
34
|
+
if (!this.scopes)
|
|
35
|
+
return where;
|
|
36
|
+
const reg = this.config.model(modelSlug);
|
|
37
|
+
if (!reg)
|
|
38
|
+
return where;
|
|
39
|
+
return this.scopes.apply(where, reg, {
|
|
40
|
+
user: ctx.user,
|
|
41
|
+
organization: ctx.organization,
|
|
42
|
+
userRole: (0, permission_matcher_1.resolveUserRoleSlug)(ctx.user, ctx.organization?.id),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
delegate(modelSlug) {
|
|
46
|
+
const reg = this.config.model(modelSlug);
|
|
47
|
+
if (!reg)
|
|
48
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
49
|
+
return this.prisma.model(reg.model);
|
|
50
|
+
}
|
|
51
|
+
/** Resolve Prisma where filter for the current org context. */
|
|
52
|
+
orgFilter(modelSlug, org) {
|
|
53
|
+
if (!org)
|
|
54
|
+
return null;
|
|
55
|
+
const reg = this.config.model(modelSlug);
|
|
56
|
+
if (!reg?.belongsToOrganization)
|
|
57
|
+
return null;
|
|
58
|
+
return { organizationId: org.id };
|
|
59
|
+
}
|
|
60
|
+
async findAll(modelSlug, rawQuery, ctx = {}) {
|
|
61
|
+
const reg = this.config.model(modelSlug);
|
|
62
|
+
if (!reg)
|
|
63
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
64
|
+
const delegate = this.delegate(modelSlug);
|
|
65
|
+
const parsed = this.queryBuilder.build(rawQuery, reg);
|
|
66
|
+
let where = this.mergeWhere(parsed.where, this.orgFilter(modelSlug, ctx.organization));
|
|
67
|
+
where = this.applyScopes(where, modelSlug, ctx);
|
|
68
|
+
// soft delete visibility
|
|
69
|
+
if (reg.softDeletes) {
|
|
70
|
+
if (ctx.onlyTrashed) {
|
|
71
|
+
where.deletedAt = { not: null };
|
|
72
|
+
}
|
|
73
|
+
else if (!ctx.includeTrashed) {
|
|
74
|
+
where.deletedAt = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const paginate = reg.paginationEnabled !== false;
|
|
78
|
+
if (paginate) {
|
|
79
|
+
const perPage = parsed.perPage ?? reg.perPage ?? 25;
|
|
80
|
+
const page = parsed.page ?? 1;
|
|
81
|
+
const skip = (page - 1) * perPage;
|
|
82
|
+
const [items, total] = await Promise.all([
|
|
83
|
+
delegate.findMany({
|
|
84
|
+
where,
|
|
85
|
+
orderBy: parsed.orderBy,
|
|
86
|
+
include: parsed.include,
|
|
87
|
+
select: parsed.select,
|
|
88
|
+
skip,
|
|
89
|
+
take: perPage,
|
|
90
|
+
}),
|
|
91
|
+
delegate.count({ where }),
|
|
92
|
+
]);
|
|
93
|
+
return {
|
|
94
|
+
items,
|
|
95
|
+
total,
|
|
96
|
+
page,
|
|
97
|
+
perPage,
|
|
98
|
+
lastPage: Math.max(1, Math.ceil(total / perPage)),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
const items = await delegate.findMany({
|
|
102
|
+
where,
|
|
103
|
+
orderBy: parsed.orderBy,
|
|
104
|
+
include: parsed.include,
|
|
105
|
+
select: parsed.select,
|
|
106
|
+
});
|
|
107
|
+
return { items };
|
|
108
|
+
}
|
|
109
|
+
async findOne(modelSlug, id, rawQuery, ctx = {}) {
|
|
110
|
+
const reg = this.config.model(modelSlug);
|
|
111
|
+
if (!reg)
|
|
112
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
113
|
+
const delegate = this.delegate(modelSlug);
|
|
114
|
+
const parsed = this.queryBuilder.build(rawQuery, reg);
|
|
115
|
+
let where = { id: this.castId(id, reg.hasUuid) };
|
|
116
|
+
const orgScope = this.orgFilter(modelSlug, ctx.organization);
|
|
117
|
+
if (orgScope)
|
|
118
|
+
Object.assign(where, orgScope);
|
|
119
|
+
where = this.applyScopes(where, modelSlug, ctx);
|
|
120
|
+
if (reg.softDeletes && !ctx.includeTrashed)
|
|
121
|
+
where.deletedAt = null;
|
|
122
|
+
return delegate.findFirst({ where, include: parsed.include, select: parsed.select });
|
|
123
|
+
}
|
|
124
|
+
async create(modelSlug, data, ctx = {}) {
|
|
125
|
+
const reg = this.config.model(modelSlug);
|
|
126
|
+
if (!reg)
|
|
127
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
128
|
+
const delegate = this.delegate(modelSlug);
|
|
129
|
+
const payload = { ...data };
|
|
130
|
+
if (reg.belongsToOrganization && ctx.organization) {
|
|
131
|
+
payload.organizationId = ctx.organization.id;
|
|
132
|
+
}
|
|
133
|
+
return delegate.create({ data: payload });
|
|
134
|
+
}
|
|
135
|
+
async update(modelSlug, id, data, ctx = {}) {
|
|
136
|
+
const reg = this.config.model(modelSlug);
|
|
137
|
+
if (!reg)
|
|
138
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
139
|
+
const delegate = this.delegate(modelSlug);
|
|
140
|
+
const where = { id: this.castId(id, reg.hasUuid) };
|
|
141
|
+
const orgScope = this.orgFilter(modelSlug, ctx.organization);
|
|
142
|
+
if (orgScope)
|
|
143
|
+
Object.assign(where, orgScope);
|
|
144
|
+
// Reject attempts to change organizationId silently
|
|
145
|
+
const payload = { ...data };
|
|
146
|
+
delete payload.organizationId;
|
|
147
|
+
delete payload.organization_id;
|
|
148
|
+
// Prisma "update" doesn't allow extra where props; use updateMany + findFirst for org scope
|
|
149
|
+
if (orgScope) {
|
|
150
|
+
const res = await delegate.updateMany({ where, data: payload });
|
|
151
|
+
if (res.count === 0)
|
|
152
|
+
return null;
|
|
153
|
+
return delegate.findFirst({ where });
|
|
154
|
+
}
|
|
155
|
+
return delegate.update({ where: { id: this.castId(id, reg.hasUuid) }, data: payload });
|
|
156
|
+
}
|
|
157
|
+
async delete(modelSlug, id, ctx = {}) {
|
|
158
|
+
const reg = this.config.model(modelSlug);
|
|
159
|
+
if (!reg)
|
|
160
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
161
|
+
const delegate = this.delegate(modelSlug);
|
|
162
|
+
const where = { id: this.castId(id, reg.hasUuid) };
|
|
163
|
+
const orgScope = this.orgFilter(modelSlug, ctx.organization);
|
|
164
|
+
if (orgScope)
|
|
165
|
+
Object.assign(where, orgScope);
|
|
166
|
+
if (reg.softDeletes) {
|
|
167
|
+
const res = await delegate.updateMany({ where, data: { deletedAt: new Date() } });
|
|
168
|
+
return res.count > 0;
|
|
169
|
+
}
|
|
170
|
+
if (orgScope) {
|
|
171
|
+
const res = await delegate.deleteMany({ where });
|
|
172
|
+
return res.count > 0;
|
|
173
|
+
}
|
|
174
|
+
await delegate.delete({ where: { id: this.castId(id, reg.hasUuid) } });
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
async restore(modelSlug, id, ctx = {}) {
|
|
178
|
+
const reg = this.config.model(modelSlug);
|
|
179
|
+
if (!reg?.softDeletes)
|
|
180
|
+
throw new Error(`Model ${modelSlug} does not support soft deletes`);
|
|
181
|
+
const delegate = this.delegate(modelSlug);
|
|
182
|
+
const where = { id: this.castId(id, reg.hasUuid) };
|
|
183
|
+
const orgScope = this.orgFilter(modelSlug, ctx.organization);
|
|
184
|
+
if (orgScope)
|
|
185
|
+
Object.assign(where, orgScope);
|
|
186
|
+
const res = await delegate.updateMany({ where, data: { deletedAt: null } });
|
|
187
|
+
return res.count > 0;
|
|
188
|
+
}
|
|
189
|
+
async forceDelete(modelSlug, id, ctx = {}) {
|
|
190
|
+
const reg = this.config.model(modelSlug);
|
|
191
|
+
if (!reg)
|
|
192
|
+
throw new Error(`Unknown model: ${modelSlug}`);
|
|
193
|
+
const delegate = this.delegate(modelSlug);
|
|
194
|
+
const where = { id: this.castId(id, reg.hasUuid) };
|
|
195
|
+
const orgScope = this.orgFilter(modelSlug, ctx.organization);
|
|
196
|
+
if (orgScope)
|
|
197
|
+
Object.assign(where, orgScope);
|
|
198
|
+
if (orgScope) {
|
|
199
|
+
const res = await delegate.deleteMany({ where });
|
|
200
|
+
return res.count > 0;
|
|
201
|
+
}
|
|
202
|
+
await delegate.delete({ where: { id: this.castId(id, reg.hasUuid) } });
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
castId(id, hasUuid) {
|
|
206
|
+
if (hasUuid)
|
|
207
|
+
return String(id);
|
|
208
|
+
if (typeof id === 'string' && /^\d+$/.test(id))
|
|
209
|
+
return Number(id);
|
|
210
|
+
return id;
|
|
211
|
+
}
|
|
212
|
+
mergeWhere(...parts) {
|
|
213
|
+
const out = {};
|
|
214
|
+
for (const p of parts) {
|
|
215
|
+
if (p)
|
|
216
|
+
Object.assign(out, p);
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
exports.ResourceService = ResourceService;
|
|
222
|
+
exports.ResourceService = ResourceService = __decorate([
|
|
223
|
+
(0, common_1.Injectable)(),
|
|
224
|
+
__param(3, (0, common_1.Optional)()),
|
|
225
|
+
__metadata("design:paramtypes", [prisma_service_1.PrismaService,
|
|
226
|
+
rhino_config_1.RhinoConfigService,
|
|
227
|
+
query_builder_service_1.QueryBuilderService,
|
|
228
|
+
scope_service_1.ScopeService])
|
|
229
|
+
], ResourceService);
|
|
230
|
+
//# sourceMappingURL=resource.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.service.js","sourceRoot":"","sources":["../../src/services/resource.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { INestApplication } from '@nestjs/common';
|
|
2
|
+
import { RhinoConfigService } from '../rhino.config';
|
|
3
|
+
export interface RegisterRoutesOptions {
|
|
4
|
+
/** Global prefix for all routes (default: "api"). */
|
|
5
|
+
prefix?: string;
|
|
6
|
+
/** Apply the authentication guard globally. Set `false` to skip. */
|
|
7
|
+
enableAuth?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Utility helper invoked during `main.ts` bootstrap to wire up Rhino
|
|
11
|
+
* routing conventions that can't be expressed purely via @Controller decorators
|
|
12
|
+
* (route group prefixes, tenant resolution middleware, skip-auth for public groups).
|
|
13
|
+
*
|
|
14
|
+
* NestJS has no built-in "register the same controller at multiple prefixes" API,
|
|
15
|
+
* so the pattern here is:
|
|
16
|
+
*
|
|
17
|
+
* 1. Users register RhinoModule with their model + routeGroup config.
|
|
18
|
+
* 2. Users expose a single Global/Auth/Invitation/Nested controller set
|
|
19
|
+
* (either by re-exporting from this library or by subclassing).
|
|
20
|
+
* 3. The helper returns the metadata needed to install middleware chains.
|
|
21
|
+
*
|
|
22
|
+
* Route group expansion is done at request time by the `ResolveOrganization`
|
|
23
|
+
* middleware — the tenant group's `:organization` param is the discriminator.
|
|
24
|
+
*/
|
|
25
|
+
export declare function applyRhinoRouting(app: INestApplication, options?: RegisterRoutesOptions): {
|
|
26
|
+
prefix: string;
|
|
27
|
+
enableAuth: boolean;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Describe the resolved route table for documentation / postman export.
|
|
31
|
+
*/
|
|
32
|
+
export declare function describeRoutes(config: RhinoConfigService): Array<{
|
|
33
|
+
group: string;
|
|
34
|
+
slug: string;
|
|
35
|
+
prefix: string;
|
|
36
|
+
model: string;
|
|
37
|
+
softDeletes: boolean;
|
|
38
|
+
hasAuditTrail: boolean;
|
|
39
|
+
exceptActions: string[];
|
|
40
|
+
}>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyRhinoRouting = applyRhinoRouting;
|
|
4
|
+
exports.describeRoutes = describeRoutes;
|
|
5
|
+
/**
|
|
6
|
+
* Utility helper invoked during `main.ts` bootstrap to wire up Rhino
|
|
7
|
+
* routing conventions that can't be expressed purely via @Controller decorators
|
|
8
|
+
* (route group prefixes, tenant resolution middleware, skip-auth for public groups).
|
|
9
|
+
*
|
|
10
|
+
* NestJS has no built-in "register the same controller at multiple prefixes" API,
|
|
11
|
+
* so the pattern here is:
|
|
12
|
+
*
|
|
13
|
+
* 1. Users register RhinoModule with their model + routeGroup config.
|
|
14
|
+
* 2. Users expose a single Global/Auth/Invitation/Nested controller set
|
|
15
|
+
* (either by re-exporting from this library or by subclassing).
|
|
16
|
+
* 3. The helper returns the metadata needed to install middleware chains.
|
|
17
|
+
*
|
|
18
|
+
* Route group expansion is done at request time by the `ResolveOrganization`
|
|
19
|
+
* middleware — the tenant group's `:organization` param is the discriminator.
|
|
20
|
+
*/
|
|
21
|
+
function applyRhinoRouting(app, options = {}) {
|
|
22
|
+
const prefix = options.prefix ?? 'api';
|
|
23
|
+
app.setGlobalPrefix(prefix);
|
|
24
|
+
return { prefix, enableAuth: options.enableAuth ?? true };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Describe the resolved route table for documentation / postman export.
|
|
28
|
+
*/
|
|
29
|
+
function describeRoutes(config) {
|
|
30
|
+
const routes = [];
|
|
31
|
+
const models = config.models();
|
|
32
|
+
const groups = config.routeGroups();
|
|
33
|
+
const groupNames = Object.keys(groups);
|
|
34
|
+
const fallback = groupNames.length === 0 ? [[undefined, undefined]] : [];
|
|
35
|
+
for (const name of groupNames) {
|
|
36
|
+
const group = groups[name];
|
|
37
|
+
const slugList = group.models === '*' ? Object.keys(models) : group.models;
|
|
38
|
+
for (const slug of slugList) {
|
|
39
|
+
const reg = models[slug];
|
|
40
|
+
if (!reg)
|
|
41
|
+
continue;
|
|
42
|
+
routes.push({
|
|
43
|
+
group: name,
|
|
44
|
+
slug,
|
|
45
|
+
prefix: group.prefix ?? '',
|
|
46
|
+
model: reg.model,
|
|
47
|
+
softDeletes: !!reg.softDeletes,
|
|
48
|
+
hasAuditTrail: !!reg.hasAuditTrail,
|
|
49
|
+
exceptActions: reg.exceptActions ?? [],
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (fallback.length > 0) {
|
|
54
|
+
for (const [slug, reg] of Object.entries(models)) {
|
|
55
|
+
routes.push({
|
|
56
|
+
group: '(default)',
|
|
57
|
+
slug,
|
|
58
|
+
prefix: '',
|
|
59
|
+
model: reg.model,
|
|
60
|
+
softDeletes: !!reg.softDeletes,
|
|
61
|
+
hasAuditTrail: !!reg.hasAuditTrail,
|
|
62
|
+
exceptActions: reg.exceptActions ?? [],
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return routes;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=route-registration.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-registration.service.js","sourceRoot":"","sources":["../../src/services/route-registration.service.ts"],"names":[],"mappings":";;AA0BA,8CAOC;AAKD,wCA6CC;AAzED;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,iBAAiB,CAC/B,GAAqB,EACrB,UAAiC,EAAE;IAEnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;IACvC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAgB,cAAc,CAAC,MAA0B;IASvD,MAAM,MAAM,GAAU,EAAE,CAAC;IACzB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3E,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;YACzB,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,IAAI;gBACX,IAAI;gBACJ,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,EAAE;gBAC1B,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW;gBAC9B,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa;gBAClC,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE;aACvC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC;gBACV,KAAK,EAAE,WAAW;gBAClB,IAAI;gBACJ,MAAM,EAAE,EAAE;gBACV,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,WAAW;gBAC9B,aAAa,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa;gBAClC,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,EAAE;aACvC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ModelRegistration } from '../interfaces/rhino-config.interface';
|
|
2
|
+
export interface ScopeContext {
|
|
3
|
+
user?: any;
|
|
4
|
+
organization?: any;
|
|
5
|
+
userRole?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface RhinoScope {
|
|
8
|
+
apply(where: Record<string, any>, context: ScopeContext): Record<string, any>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Applies all registered scopes for a model in order.
|
|
12
|
+
* Scopes are classes implementing `RhinoScope`.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ScopeService {
|
|
15
|
+
apply(where: Record<string, any>, reg: ModelRegistration, ctx: ScopeContext): Record<string, any>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ScopeService = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
/**
|
|
12
|
+
* Applies all registered scopes for a model in order.
|
|
13
|
+
* Scopes are classes implementing `RhinoScope`.
|
|
14
|
+
*/
|
|
15
|
+
let ScopeService = class ScopeService {
|
|
16
|
+
apply(where, reg, ctx) {
|
|
17
|
+
const scopes = reg.scopes ?? [];
|
|
18
|
+
let merged = { ...where };
|
|
19
|
+
for (const ScopeClass of scopes) {
|
|
20
|
+
const scope = new ScopeClass();
|
|
21
|
+
if (typeof scope.apply === 'function') {
|
|
22
|
+
merged = scope.apply(merged, ctx) ?? merged;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return merged;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
exports.ScopeService = ScopeService;
|
|
29
|
+
exports.ScopeService = ScopeService = __decorate([
|
|
30
|
+
(0, common_1.Injectable)()
|
|
31
|
+
], ScopeService);
|
|
32
|
+
//# sourceMappingURL=scope.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.service.js","sourceRoot":"","sources":["../../src/services/scope.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAa5C;;;GAGG;AAEI,IAAM,YAAY,GAAlB,MAAM,YAAY;IACvB,KAAK,CAAC,KAA0B,EAAE,GAAsB,EAAE,GAAiB;QACzE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC;QAChC,IAAI,MAAM,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC;QAC1B,KAAK,MAAM,UAAU,IAAI,MAAM,EAAE,CAAC;YAChC,MAAM,KAAK,GAAG,IAAK,UAAkB,EAAE,CAAC;YACxC,IAAI,OAAO,KAAK,CAAC,KAAK,KAAK,UAAU,EAAE,CAAC;gBACtC,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC;YAC9C,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAA;AAZY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;GACA,YAAY,CAYxB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ModelRegistration } from '../interfaces/rhino-config.interface';
|
|
2
|
+
export declare const BASE_HIDDEN_COLUMNS: string[];
|
|
3
|
+
/**
|
|
4
|
+
* Context object for serialization. `organization` is required for role-based
|
|
5
|
+
* policy attribute filtering — without it, `hasRole(user, role, org)` in
|
|
6
|
+
* consumer policies can never resolve the active role and every request
|
|
7
|
+
* would collapse to the "no role" fallback (BP-007).
|
|
8
|
+
*
|
|
9
|
+
* Legacy signature: `serializeOne(record, reg, user)` is still accepted for
|
|
10
|
+
* backwards compatibility. New callers should pass the object form.
|
|
11
|
+
*/
|
|
12
|
+
export interface SerializeContext {
|
|
13
|
+
user?: any;
|
|
14
|
+
organization?: any;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Serializes a record according to the Laravel `asRhinoJson` contract:
|
|
18
|
+
*
|
|
19
|
+
* 1. Merge computed attributes
|
|
20
|
+
* 2. Remove base-hidden columns
|
|
21
|
+
* 3. Remove model-level `additionalHiddenColumns`
|
|
22
|
+
* 4. Apply policy blacklist (`hiddenAttributesForShow`)
|
|
23
|
+
* 5. Apply policy whitelist (`permittedAttributesForShow`) — `id` always kept
|
|
24
|
+
*/
|
|
25
|
+
export declare class SerializerService {
|
|
26
|
+
/**
|
|
27
|
+
* @param record The raw record to serialize (from Prisma).
|
|
28
|
+
* @param reg The model registration driving policy + computed attrs.
|
|
29
|
+
* @param ctx Either a `SerializeContext` object (preferred) or a
|
|
30
|
+
* legacy `user` value (backwards-compat shim — org will
|
|
31
|
+
* be undefined and role-keyed policies will collapse).
|
|
32
|
+
*/
|
|
33
|
+
serializeOne(record: Record<string, any> | null | undefined, reg: ModelRegistration, ctx?: SerializeContext | any): Record<string, any> | null;
|
|
34
|
+
serializeMany(records: any[], reg: ModelRegistration, ctx?: SerializeContext | any): any[];
|
|
35
|
+
private normalizeCtx;
|
|
36
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.SerializerService = exports.BASE_HIDDEN_COLUMNS = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
exports.BASE_HIDDEN_COLUMNS = [
|
|
12
|
+
'password',
|
|
13
|
+
'rememberToken',
|
|
14
|
+
'remember_token',
|
|
15
|
+
'hasTemporaryPassword',
|
|
16
|
+
'has_temporary_password',
|
|
17
|
+
'updatedAt',
|
|
18
|
+
'updated_at',
|
|
19
|
+
'createdAt',
|
|
20
|
+
'created_at',
|
|
21
|
+
'deletedAt',
|
|
22
|
+
'deleted_at',
|
|
23
|
+
'emailVerifiedAt',
|
|
24
|
+
'email_verified_at',
|
|
25
|
+
];
|
|
26
|
+
/**
|
|
27
|
+
* Serializes a record according to the Laravel `asRhinoJson` contract:
|
|
28
|
+
*
|
|
29
|
+
* 1. Merge computed attributes
|
|
30
|
+
* 2. Remove base-hidden columns
|
|
31
|
+
* 3. Remove model-level `additionalHiddenColumns`
|
|
32
|
+
* 4. Apply policy blacklist (`hiddenAttributesForShow`)
|
|
33
|
+
* 5. Apply policy whitelist (`permittedAttributesForShow`) — `id` always kept
|
|
34
|
+
*/
|
|
35
|
+
let SerializerService = class SerializerService {
|
|
36
|
+
/**
|
|
37
|
+
* @param record The raw record to serialize (from Prisma).
|
|
38
|
+
* @param reg The model registration driving policy + computed attrs.
|
|
39
|
+
* @param ctx Either a `SerializeContext` object (preferred) or a
|
|
40
|
+
* legacy `user` value (backwards-compat shim — org will
|
|
41
|
+
* be undefined and role-keyed policies will collapse).
|
|
42
|
+
*/
|
|
43
|
+
serializeOne(record, reg, ctx) {
|
|
44
|
+
if (!record)
|
|
45
|
+
return record;
|
|
46
|
+
const { user, organization } = this.normalizeCtx(ctx);
|
|
47
|
+
let result = { ...record };
|
|
48
|
+
if (reg.computedAttributes) {
|
|
49
|
+
Object.assign(result, reg.computedAttributes(record, user));
|
|
50
|
+
}
|
|
51
|
+
for (const col of exports.BASE_HIDDEN_COLUMNS) {
|
|
52
|
+
delete result[col];
|
|
53
|
+
}
|
|
54
|
+
if (reg.additionalHiddenColumns?.length) {
|
|
55
|
+
for (const col of reg.additionalHiddenColumns)
|
|
56
|
+
delete result[col];
|
|
57
|
+
}
|
|
58
|
+
if (reg.policy) {
|
|
59
|
+
const policy = new reg.policy();
|
|
60
|
+
// BP-007: pass organization as the second argument so policy methods
|
|
61
|
+
// that call `this.hasRole(user, 'admin', org)` can resolve the active
|
|
62
|
+
// role. Base ResourcePolicy signatures accept an optional org.
|
|
63
|
+
const hidden = policy.hiddenAttributesForShow(user, organization) ?? [];
|
|
64
|
+
for (const col of hidden)
|
|
65
|
+
delete result[col];
|
|
66
|
+
const permitted = policy.permittedAttributesForShow(user, organization) ?? ['*'];
|
|
67
|
+
if (!(permitted.length === 1 && permitted[0] === '*')) {
|
|
68
|
+
const keep = new Set([...permitted, 'id']);
|
|
69
|
+
result = Object.fromEntries(Object.entries(result).filter(([k]) => keep.has(k)));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
serializeMany(records, reg, ctx) {
|
|
75
|
+
return records.map((r) => this.serializeOne(r, reg, ctx));
|
|
76
|
+
}
|
|
77
|
+
normalizeCtx(ctx) {
|
|
78
|
+
// New call shape: { user, organization }
|
|
79
|
+
if (ctx && typeof ctx === 'object' && ('user' in ctx || 'organization' in ctx)) {
|
|
80
|
+
return { user: ctx.user, organization: ctx.organization };
|
|
81
|
+
}
|
|
82
|
+
// Legacy call shape: a bare user (or null/undefined)
|
|
83
|
+
return { user: ctx, organization: undefined };
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
exports.SerializerService = SerializerService;
|
|
87
|
+
exports.SerializerService = SerializerService = __decorate([
|
|
88
|
+
(0, common_1.Injectable)()
|
|
89
|
+
], SerializerService);
|
|
90
|
+
//# sourceMappingURL=serializer.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serializer.service.js","sourceRoot":"","sources":["../../src/services/serializer.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { ModelRegistration } from '../interfaces/rhino-config.interface';
|
|
2
|
+
import { PrismaService } from '../prisma/prisma.service';
|
|
3
|
+
import { RhinoConfigService } from '../rhino.config';
|
|
4
|
+
export interface ValidationResult<T = Record<string, any>> {
|
|
5
|
+
valid: boolean;
|
|
6
|
+
data?: T;
|
|
7
|
+
errors?: Record<string, string[]>;
|
|
8
|
+
forbiddenFields?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface ValidationContext {
|
|
11
|
+
user?: any;
|
|
12
|
+
organization?: any;
|
|
13
|
+
action: 'store' | 'update';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Validates request data against a registered model's Zod schema.
|
|
17
|
+
*
|
|
18
|
+
* Responsibilities mirror Laravel's HasValidation trait:
|
|
19
|
+
* - Pick schema based on action (store/update), with role-keyed overrides
|
|
20
|
+
* - Intersect fields with policy's `permittedAttributesFor{Create,Update}`
|
|
21
|
+
* - Reject forbidden fields (HTTP 403 semantics; returned in errors)
|
|
22
|
+
* - Strip the `organizationId` field from user-supplied input when in tenant context
|
|
23
|
+
*/
|
|
24
|
+
export interface FkConstraint {
|
|
25
|
+
field: string;
|
|
26
|
+
model: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class ValidationService {
|
|
29
|
+
private readonly prisma?;
|
|
30
|
+
private readonly config?;
|
|
31
|
+
constructor(prisma?: PrismaService, config?: RhinoConfigService);
|
|
32
|
+
/**
|
|
33
|
+
* Verify that every foreign-key in `data` references a record that lives
|
|
34
|
+
* inside the current organization (directly or through an FK chain). This
|
|
35
|
+
* implements Laravel's `scopeExistsRulesToOrganization` semantics for Prisma.
|
|
36
|
+
*
|
|
37
|
+
* Constraints are declared per model via:
|
|
38
|
+
* ModelRegistration.fkConstraints?: Array<{ field: 'postId', model: 'post' }>
|
|
39
|
+
*/
|
|
40
|
+
verifyTenantFks(data: Record<string, any>, reg: ModelRegistration, organization?: any): Promise<{
|
|
41
|
+
valid: boolean;
|
|
42
|
+
errors?: Record<string, string[]>;
|
|
43
|
+
}>;
|
|
44
|
+
private isFkInOrg;
|
|
45
|
+
private walkFkChainForRecord;
|
|
46
|
+
validateForAction<T = Record<string, any>>(data: Record<string, any>, reg: ModelRegistration, ctx: ValidationContext): ValidationResult<T>;
|
|
47
|
+
private pickSchema;
|
|
48
|
+
private resolvePermittedFields;
|
|
49
|
+
private formatErrors;
|
|
50
|
+
}
|