@simple-auth-kit/cli 1.4.3 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/registry/combos/express-drizzle/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-drizzle/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +23 -7
- package/registry/combos/{express-prisma/shared/src → express-drizzle/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-drizzle/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +32 -9
- package/registry/combos/express-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/express-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/express-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +20 -6
- package/registry/combos/express-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/express-drizzle/shared/src/routers/auth.router.ts +395 -0
- package/registry/combos/express-drizzle/variants/base/src/create-auth-app.ts +52 -19
- package/registry/combos/express-drizzle/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +28 -10
- package/registry/combos/express-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +239 -48
- package/registry/combos/express-drizzle/variants/base/src/routers/admin.router.ts +497 -0
- package/registry/combos/express-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +480 -109
- package/registry/combos/express-drizzle/variants/workspaces/src/create-auth-app.ts +69 -23
- package/registry/combos/express-drizzle/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-drizzle/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +30 -11
- package/registry/combos/express-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +319 -70
- package/registry/combos/express-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +127 -30
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/admin.router.ts +534 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-drizzle/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +544 -122
- package/registry/combos/express-prisma/shared/src/{auth-core-error.middleware.ts → middleware/auth-core-error.middleware.ts} +17 -4
- package/registry/combos/express-prisma/shared/src/{auth.middleware.ts → middleware/auth.middleware.ts} +19 -6
- package/registry/combos/{express-drizzle/shared/src → express-prisma/shared/src/middleware}/response-envelope.middleware.ts +15 -3
- package/registry/combos/express-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +27 -9
- package/registry/combos/express-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/express-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/express-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/express-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/express-prisma/shared/src/routers/auth.router.ts +398 -0
- package/registry/combos/express-prisma/variants/base/src/create-auth-app.ts +57 -22
- package/registry/combos/express-prisma/variants/base/src/{authz.middleware.ts → middleware/authz.middleware.ts} +15 -7
- package/registry/combos/express-prisma/variants/base/src/rbac.defaults.ts +67 -19
- package/registry/combos/express-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +199 -48
- package/registry/combos/express-prisma/variants/base/src/routers/admin.router.ts +508 -0
- package/registry/combos/express-prisma/variants/base/src/seed.ts +78 -20
- package/registry/combos/express-prisma/variants/base/src/{auth.service.ts → services/auth.service.ts} +454 -105
- package/registry/combos/express-prisma/variants/workspaces/src/create-auth-app.ts +81 -26
- package/registry/combos/express-prisma/variants/workspaces/src/{authz.middleware.ts → middleware/authz.middleware.ts} +32 -12
- package/registry/combos/express-prisma/variants/workspaces/src/openapi-workspace.ts +147 -28
- package/registry/combos/express-prisma/variants/workspaces/src/rbac.defaults.ts +73 -21
- package/registry/combos/express-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/express-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +272 -64
- package/registry/combos/express-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +86 -23
- package/registry/combos/express-prisma/variants/workspaces/src/routers/admin.router.ts +545 -0
- package/registry/combos/express-prisma/variants/workspaces/src/routers/workspace.router.ts +194 -0
- package/registry/combos/express-prisma/variants/workspaces/src/seed.ts +109 -29
- package/registry/combos/express-prisma/variants/workspaces/src/{auth.service.ts → services/auth.service.ts} +518 -119
- package/registry/combos/nestjs-drizzle/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-drizzle/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-drizzle/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-drizzle/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-prisma/shared/src → nestjs-drizzle/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-drizzle/shared/src/repositories/oauth.repository.ts +63 -0
- package/registry/combos/nestjs-drizzle/shared/src/repositories/password-reset.repository.ts +59 -0
- package/registry/combos/nestjs-drizzle/shared/src/{session.repository.ts → repositories/session.repository.ts} +52 -14
- package/registry/combos/nestjs-drizzle/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +21 -7
- package/registry/combos/nestjs-drizzle/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-drizzle/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-drizzle/variants/base/src/auth.module.ts +44 -20
- package/registry/combos/nestjs-drizzle/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +228 -62
- package/registry/combos/nestjs-drizzle/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-drizzle/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +27 -10
- package/registry/combos/nestjs-drizzle/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +249 -51
- package/registry/combos/nestjs-drizzle/variants/base/src/{auth.service.ts → services/auth.service.ts} +499 -116
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.module.ts +48 -22
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +269 -70
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +29 -11
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +323 -71
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +131 -30
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/services/auth.service.ts +988 -0
- package/registry/combos/nestjs-prisma/shared/src/{auth.controller.ts → controllers/auth.controller.ts} +6 -6
- package/registry/combos/nestjs-prisma/shared/src/{auth-core-error.filter.ts → filters/auth-core-error.filter.ts} +29 -5
- package/registry/combos/nestjs-prisma/shared/src/{ability.guard.ts → guards/ability.guard.ts} +20 -7
- package/registry/combos/nestjs-prisma/shared/src/{auth.guard.ts → guards/auth.guard.ts} +19 -7
- package/registry/combos/{nestjs-drizzle/shared/src → nestjs-prisma/shared/src/interceptors}/response.interceptor.ts +10 -2
- package/registry/combos/nestjs-prisma/shared/src/{oauth.repository.ts → repositories/oauth.repository.ts} +25 -8
- package/registry/combos/nestjs-prisma/shared/src/repositories/password-reset.repository.ts +55 -0
- package/registry/combos/nestjs-prisma/shared/src/{session.repository.ts → repositories/session.repository.ts} +30 -9
- package/registry/combos/nestjs-prisma/shared/src/{two-factor.repository.ts → repositories/two-factor.repository.ts} +11 -4
- package/registry/combos/nestjs-prisma/shared/src/request-context.ts +1 -1
- package/registry/combos/nestjs-prisma/test/prove-cycle.ts +1097 -307
- package/registry/combos/nestjs-prisma/variants/base/src/auth.module.ts +59 -28
- package/registry/combos/nestjs-prisma/variants/base/src/{admin.controller.ts → controllers/admin.controller.ts} +469 -110
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.gateway.ts → gateways/audit-log.gateway.ts} +22 -6
- package/registry/combos/nestjs-prisma/variants/base/src/{authz.guard.ts → guards/authz.guard.ts} +12 -5
- package/registry/combos/nestjs-prisma/variants/base/src/rbac.defaults.ts +98 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/base/src/{country.repository.ts → repositories/country.repository.ts} +98 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{customer.repository.ts → repositories/customer.repository.ts} +106 -27
- package/registry/combos/nestjs-prisma/variants/base/src/{language.repository.ts → repositories/language.repository.ts} +93 -25
- package/registry/combos/nestjs-prisma/variants/base/src/{rbac.repository.ts → repositories/rbac.repository.ts} +229 -55
- package/registry/combos/nestjs-prisma/variants/base/src/seed.ts +78 -20
- package/registry/combos/nestjs-prisma/variants/base/src/services/auth.service.ts +1064 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.module.ts +55 -27
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{admin.controller.ts → controllers/admin.controller.ts} +544 -128
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.controller.ts → controllers/workspace.controller.ts} +93 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{authz.guard.ts → guards/authz.guard.ts} +26 -8
- package/registry/combos/nestjs-prisma/variants/workspaces/src/rbac.defaults.ts +104 -29
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{audit-log.repository.ts → repositories/audit-log.repository.ts} +16 -5
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{country.repository.ts → repositories/country.repository.ts} +127 -26
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{customer.repository.ts → repositories/customer.repository.ts} +137 -28
- package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts +243 -0
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{rbac.repository.ts → repositories/rbac.repository.ts} +314 -74
- package/registry/combos/nestjs-prisma/variants/workspaces/src/{workspace.repository.ts → repositories/workspace.repository.ts} +94 -24
- package/registry/combos/nestjs-prisma/variants/workspaces/src/seed.ts +109 -29
- package/registry/combos/nestjs-prisma/variants/workspaces/src/services/auth.service.ts +961 -0
- package/registry.json +4 -4
- package/simple-auth-kit.ts +29 -2
- package/registry/combos/express-drizzle/shared/src/auth.router.ts +0 -266
- package/registry/combos/express-drizzle/shared/src/password-reset.repository.ts +0 -35
- package/registry/combos/express-drizzle/variants/base/src/admin.router.ts +0 -307
- package/registry/combos/express-drizzle/variants/workspaces/src/admin.router.ts +0 -323
- package/registry/combos/express-drizzle/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/express-prisma/shared/src/auth.router.ts +0 -269
- package/registry/combos/express-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/express-prisma/variants/base/src/admin.router.ts +0 -314
- package/registry/combos/express-prisma/variants/workspaces/src/admin.router.ts +0 -330
- package/registry/combos/express-prisma/variants/workspaces/src/workspace.router.ts +0 -123
- package/registry/combos/nestjs-drizzle/shared/src/oauth.repository.ts +0 -36
- package/registry/combos/nestjs-drizzle/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-drizzle/variants/workspaces/src/auth.service.ts +0 -553
- package/registry/combos/nestjs-prisma/shared/src/password-reset.repository.ts +0 -29
- package/registry/combos/nestjs-prisma/variants/base/src/auth.service.ts +0 -618
- package/registry/combos/nestjs-prisma/variants/workspaces/src/auth.service.ts +0 -509
- package/registry/combos/nestjs-prisma/variants/workspaces/src/language.repository.ts +0 -143
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
ConflictException,
|
|
3
|
+
Inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
NotFoundException,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Prisma, PrismaClient } from "../../generated/prisma/client.js";
|
|
8
|
+
import { toId, toIdOrNull } from "../id.helper.js";
|
|
9
|
+
import {
|
|
10
|
+
buildPageMeta,
|
|
11
|
+
normalizeLimit,
|
|
12
|
+
normalizePage,
|
|
13
|
+
type Paginated,
|
|
14
|
+
} from "../pagination.js";
|
|
5
15
|
|
|
6
16
|
/** A `Country` row as `findUnique`/`findMany` return it. */
|
|
7
17
|
export type CountryRow = Prisma.CountryGetPayload<object>;
|
|
@@ -78,7 +88,10 @@ export class CountryRepository {
|
|
|
78
88
|
constructor(@Inject(PrismaClient) private readonly prisma: PrismaClient) {}
|
|
79
89
|
|
|
80
90
|
/** Newest-first, page-paginated; `search` matches name/code/isoCode. Returns raw rows — shaping is the caller's job. */
|
|
81
|
-
async list(
|
|
91
|
+
async list(
|
|
92
|
+
workspaceId: string,
|
|
93
|
+
filter: CountryListFilter = {},
|
|
94
|
+
): Promise<CountryListResult> {
|
|
82
95
|
const page = normalizePage(filter.page);
|
|
83
96
|
const limit = normalizeLimit(filter.limit);
|
|
84
97
|
const where: Prisma.CountryWhereInput = {
|
|
@@ -95,64 +108,152 @@ export class CountryRepository {
|
|
|
95
108
|
};
|
|
96
109
|
|
|
97
110
|
const [rows, total] = await this.prisma.$transaction([
|
|
98
|
-
this.prisma.country.findMany({
|
|
111
|
+
this.prisma.country.findMany({
|
|
112
|
+
where,
|
|
113
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
|
114
|
+
take: limit,
|
|
115
|
+
skip: (page - 1) * limit,
|
|
116
|
+
}),
|
|
99
117
|
this.prisma.country.count({ where }),
|
|
100
118
|
]);
|
|
101
119
|
|
|
102
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
items: rows.map(toCountrySummary),
|
|
122
|
+
meta: buildPageMeta(page, limit, total),
|
|
123
|
+
};
|
|
103
124
|
}
|
|
104
125
|
|
|
105
126
|
async get(workspaceId: string, countryId: string): Promise<CountrySummary> {
|
|
106
|
-
const row = await this.prisma.country.findUnique({
|
|
127
|
+
const row = await this.prisma.country.findUnique({
|
|
128
|
+
where: {
|
|
129
|
+
id: toId(countryId),
|
|
130
|
+
workspaceId: toId(workspaceId),
|
|
131
|
+
isDeleted: false,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
107
134
|
if (!row) throw new NotFoundException(`country "${countryId}" not found`);
|
|
108
135
|
return toCountrySummary(row);
|
|
109
136
|
}
|
|
110
137
|
|
|
111
|
-
async create(
|
|
138
|
+
async create(
|
|
139
|
+
workspaceId: string,
|
|
140
|
+
input: CountryInput,
|
|
141
|
+
actorUserId: string | null,
|
|
142
|
+
): Promise<CountrySummary> {
|
|
112
143
|
const workspaceIdBig = toId(workspaceId);
|
|
113
|
-
const existing = await this.prisma.country.findFirst({
|
|
114
|
-
|
|
144
|
+
const existing = await this.prisma.country.findFirst({
|
|
145
|
+
where: {
|
|
146
|
+
workspaceId: workspaceIdBig,
|
|
147
|
+
OR: [{ code: input.code }, { isoCode: input.isoCode }],
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
if (existing)
|
|
151
|
+
throw new ConflictException(
|
|
152
|
+
"a country with this code or ISO code already exists in this workspace",
|
|
153
|
+
);
|
|
115
154
|
|
|
116
155
|
const row = await this.prisma.country.create({
|
|
117
|
-
data: {
|
|
156
|
+
data: {
|
|
157
|
+
...input,
|
|
158
|
+
workspaceId: workspaceIdBig,
|
|
159
|
+
createdBy: toIdOrNull(actorUserId),
|
|
160
|
+
updatedBy: toIdOrNull(actorUserId),
|
|
161
|
+
},
|
|
118
162
|
});
|
|
119
163
|
return toCountrySummary(row);
|
|
120
164
|
}
|
|
121
165
|
|
|
122
|
-
async update(
|
|
166
|
+
async update(
|
|
167
|
+
workspaceId: string,
|
|
168
|
+
countryId: string,
|
|
169
|
+
input: Partial<CountryInput>,
|
|
170
|
+
actorUserId: string | null,
|
|
171
|
+
): Promise<CountrySummary> {
|
|
123
172
|
const workspaceIdBig = toId(workspaceId);
|
|
124
173
|
const countryIdBig = toId(countryId);
|
|
125
|
-
const existing = await this.prisma.country.findUnique({
|
|
126
|
-
|
|
174
|
+
const existing = await this.prisma.country.findUnique({
|
|
175
|
+
where: {
|
|
176
|
+
id: countryIdBig,
|
|
177
|
+
workspaceId: workspaceIdBig,
|
|
178
|
+
isDeleted: false,
|
|
179
|
+
},
|
|
180
|
+
select: { id: true },
|
|
181
|
+
});
|
|
182
|
+
if (!existing)
|
|
183
|
+
throw new NotFoundException(`country "${countryId}" not found`);
|
|
127
184
|
|
|
128
185
|
if (input.code !== undefined || input.isoCode !== undefined) {
|
|
129
186
|
const clash = await this.prisma.country.findFirst({
|
|
130
|
-
where: {
|
|
187
|
+
where: {
|
|
188
|
+
workspaceId: workspaceIdBig,
|
|
189
|
+
NOT: { id: countryIdBig },
|
|
190
|
+
OR: [{ code: input.code }, { isoCode: input.isoCode }],
|
|
191
|
+
},
|
|
131
192
|
});
|
|
132
|
-
if (clash)
|
|
193
|
+
if (clash)
|
|
194
|
+
throw new ConflictException(
|
|
195
|
+
"a country with this code or ISO code already exists in this workspace",
|
|
196
|
+
);
|
|
133
197
|
}
|
|
134
198
|
|
|
135
|
-
const row = await this.prisma.country.update({
|
|
199
|
+
const row = await this.prisma.country.update({
|
|
200
|
+
where: { id: countryIdBig },
|
|
201
|
+
data: { ...input, updatedBy: toIdOrNull(actorUserId) },
|
|
202
|
+
});
|
|
136
203
|
return toCountrySummary(row);
|
|
137
204
|
}
|
|
138
205
|
|
|
139
206
|
// Soft-delete, matching every other table's isDeleted/deletedAt/deletedBy/deletedReason pattern.
|
|
140
|
-
async delete(
|
|
207
|
+
async delete(
|
|
208
|
+
workspaceId: string,
|
|
209
|
+
countryId: string,
|
|
210
|
+
actorUserId: string | null,
|
|
211
|
+
reason?: string,
|
|
212
|
+
): Promise<void> {
|
|
141
213
|
const workspaceIdBig = toId(workspaceId);
|
|
142
214
|
const countryIdBig = toId(countryId);
|
|
143
|
-
const existing = await this.prisma.country.findUnique({
|
|
144
|
-
|
|
215
|
+
const existing = await this.prisma.country.findUnique({
|
|
216
|
+
where: {
|
|
217
|
+
id: countryIdBig,
|
|
218
|
+
workspaceId: workspaceIdBig,
|
|
219
|
+
isDeleted: false,
|
|
220
|
+
},
|
|
221
|
+
select: { id: true },
|
|
222
|
+
});
|
|
223
|
+
if (!existing)
|
|
224
|
+
throw new NotFoundException(`country "${countryId}" not found`);
|
|
145
225
|
await this.prisma.country.update({
|
|
146
226
|
where: { id: countryIdBig },
|
|
147
|
-
data: {
|
|
227
|
+
data: {
|
|
228
|
+
isDeleted: true,
|
|
229
|
+
deletedAt: new Date(),
|
|
230
|
+
deletedBy: toIdOrNull(actorUserId),
|
|
231
|
+
deletedReason: reason ?? null,
|
|
232
|
+
},
|
|
148
233
|
});
|
|
149
234
|
}
|
|
150
235
|
|
|
151
|
-
async setActive(
|
|
236
|
+
async setActive(
|
|
237
|
+
workspaceId: string,
|
|
238
|
+
countryId: string,
|
|
239
|
+
isActive: boolean,
|
|
240
|
+
actorUserId: string | null,
|
|
241
|
+
): Promise<void> {
|
|
152
242
|
const workspaceIdBig = toId(workspaceId);
|
|
153
243
|
const countryIdBig = toId(countryId);
|
|
154
|
-
const existing = await this.prisma.country.findUnique({
|
|
155
|
-
|
|
156
|
-
|
|
244
|
+
const existing = await this.prisma.country.findUnique({
|
|
245
|
+
where: {
|
|
246
|
+
id: countryIdBig,
|
|
247
|
+
workspaceId: workspaceIdBig,
|
|
248
|
+
isDeleted: false,
|
|
249
|
+
},
|
|
250
|
+
select: { id: true },
|
|
251
|
+
});
|
|
252
|
+
if (!existing)
|
|
253
|
+
throw new NotFoundException(`country "${countryId}" not found`);
|
|
254
|
+
await this.prisma.country.update({
|
|
255
|
+
where: { id: countryIdBig },
|
|
256
|
+
data: { isActive, updatedBy: toIdOrNull(actorUserId) },
|
|
257
|
+
});
|
|
157
258
|
}
|
|
158
259
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
ConflictException,
|
|
3
|
+
Inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
NotFoundException,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Prisma, PrismaClient } from "../../generated/prisma/client.js";
|
|
8
|
+
import { toId, toIdOrNull } from "../id.helper.js";
|
|
9
|
+
import {
|
|
10
|
+
buildPageMeta,
|
|
11
|
+
normalizeLimit,
|
|
12
|
+
normalizePage,
|
|
13
|
+
type Paginated,
|
|
14
|
+
} from "../pagination.js";
|
|
5
15
|
|
|
6
16
|
/** A `Customer` row as `findUnique`/`findMany` return it. */
|
|
7
17
|
export type CustomerRow = Prisma.CustomerGetPayload<object>;
|
|
@@ -78,11 +88,22 @@ export interface CustomerInput {
|
|
|
78
88
|
isActive?: boolean;
|
|
79
89
|
}
|
|
80
90
|
|
|
81
|
-
function toPrismaInput(
|
|
91
|
+
function toPrismaInput(
|
|
92
|
+
input: Partial<CustomerInput>,
|
|
93
|
+
): Omit<Partial<CustomerInput>, "dob" | "joinedDate"> & {
|
|
94
|
+
dob?: Date | null;
|
|
95
|
+
joinedDate?: Date;
|
|
96
|
+
} {
|
|
82
97
|
return {
|
|
83
98
|
...input,
|
|
84
|
-
dob:
|
|
85
|
-
|
|
99
|
+
dob:
|
|
100
|
+
input.dob === undefined
|
|
101
|
+
? undefined
|
|
102
|
+
: input.dob === null
|
|
103
|
+
? null
|
|
104
|
+
: new Date(input.dob),
|
|
105
|
+
joinedDate:
|
|
106
|
+
input.joinedDate === undefined ? undefined : new Date(input.joinedDate),
|
|
86
107
|
};
|
|
87
108
|
}
|
|
88
109
|
|
|
@@ -96,7 +117,10 @@ export class CustomerRepository {
|
|
|
96
117
|
constructor(@Inject(PrismaClient) private readonly prisma: PrismaClient) {}
|
|
97
118
|
|
|
98
119
|
/** Newest-first, page-paginated; `search` matches name/email/username/phone. Returns raw rows — shaping is the caller's job. */
|
|
99
|
-
async list(
|
|
120
|
+
async list(
|
|
121
|
+
workspaceId: string,
|
|
122
|
+
filter: CustomerListFilter = {},
|
|
123
|
+
): Promise<CustomerListResult> {
|
|
100
124
|
const page = normalizePage(filter.page);
|
|
101
125
|
const limit = normalizeLimit(filter.limit);
|
|
102
126
|
const where: Prisma.CustomerWhereInput = {
|
|
@@ -115,39 +139,90 @@ export class CustomerRepository {
|
|
|
115
139
|
};
|
|
116
140
|
|
|
117
141
|
const [rows, total] = await this.prisma.$transaction([
|
|
118
|
-
this.prisma.customer.findMany({
|
|
142
|
+
this.prisma.customer.findMany({
|
|
143
|
+
where,
|
|
144
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
|
145
|
+
take: limit,
|
|
146
|
+
skip: (page - 1) * limit,
|
|
147
|
+
}),
|
|
119
148
|
this.prisma.customer.count({ where }),
|
|
120
149
|
]);
|
|
121
150
|
|
|
122
|
-
return {
|
|
151
|
+
return {
|
|
152
|
+
items: rows.map(toCustomerSummary),
|
|
153
|
+
meta: buildPageMeta(page, limit, total),
|
|
154
|
+
};
|
|
123
155
|
}
|
|
124
156
|
|
|
125
157
|
async get(workspaceId: string, customerId: string): Promise<CustomerSummary> {
|
|
126
|
-
const row = await this.prisma.customer.findUnique({
|
|
158
|
+
const row = await this.prisma.customer.findUnique({
|
|
159
|
+
where: {
|
|
160
|
+
id: toId(customerId),
|
|
161
|
+
workspaceId: toId(workspaceId),
|
|
162
|
+
isDeleted: false,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
127
165
|
if (!row) throw new NotFoundException(`customer "${customerId}" not found`);
|
|
128
166
|
return toCustomerSummary(row);
|
|
129
167
|
}
|
|
130
168
|
|
|
131
|
-
async create(
|
|
169
|
+
async create(
|
|
170
|
+
workspaceId: string,
|
|
171
|
+
input: CustomerInput,
|
|
172
|
+
actorUserId: string | null,
|
|
173
|
+
): Promise<CustomerSummary> {
|
|
132
174
|
const workspaceIdBig = toId(workspaceId);
|
|
133
|
-
const existing = await this.prisma.customer.findFirst({
|
|
134
|
-
|
|
175
|
+
const existing = await this.prisma.customer.findFirst({
|
|
176
|
+
where: { workspaceId: workspaceIdBig, email: input.email },
|
|
177
|
+
});
|
|
178
|
+
if (existing)
|
|
179
|
+
throw new ConflictException(
|
|
180
|
+
"a customer with this email already exists in this workspace",
|
|
181
|
+
);
|
|
135
182
|
|
|
136
183
|
const row = await this.prisma.customer.create({
|
|
137
|
-
data: {
|
|
184
|
+
data: {
|
|
185
|
+
...toPrismaInput(input),
|
|
186
|
+
email: input.email,
|
|
187
|
+
workspaceId: workspaceIdBig,
|
|
188
|
+
createdBy: toIdOrNull(actorUserId),
|
|
189
|
+
updatedBy: toIdOrNull(actorUserId),
|
|
190
|
+
},
|
|
138
191
|
});
|
|
139
192
|
return toCustomerSummary(row);
|
|
140
193
|
}
|
|
141
194
|
|
|
142
|
-
async update(
|
|
195
|
+
async update(
|
|
196
|
+
workspaceId: string,
|
|
197
|
+
customerId: string,
|
|
198
|
+
input: Partial<CustomerInput>,
|
|
199
|
+
actorUserId: string | null,
|
|
200
|
+
): Promise<CustomerSummary> {
|
|
143
201
|
const workspaceIdBig = toId(workspaceId);
|
|
144
202
|
const customerIdBig = toId(customerId);
|
|
145
|
-
const existing = await this.prisma.customer.findUnique({
|
|
146
|
-
|
|
203
|
+
const existing = await this.prisma.customer.findUnique({
|
|
204
|
+
where: {
|
|
205
|
+
id: customerIdBig,
|
|
206
|
+
workspaceId: workspaceIdBig,
|
|
207
|
+
isDeleted: false,
|
|
208
|
+
},
|
|
209
|
+
select: { id: true },
|
|
210
|
+
});
|
|
211
|
+
if (!existing)
|
|
212
|
+
throw new NotFoundException(`customer "${customerId}" not found`);
|
|
147
213
|
|
|
148
214
|
if (input.email !== undefined) {
|
|
149
|
-
const clash = await this.prisma.customer.findFirst({
|
|
150
|
-
|
|
215
|
+
const clash = await this.prisma.customer.findFirst({
|
|
216
|
+
where: {
|
|
217
|
+
workspaceId: workspaceIdBig,
|
|
218
|
+
NOT: { id: customerIdBig },
|
|
219
|
+
email: input.email,
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
if (clash)
|
|
223
|
+
throw new ConflictException(
|
|
224
|
+
"a customer with this email already exists in this workspace",
|
|
225
|
+
);
|
|
151
226
|
}
|
|
152
227
|
|
|
153
228
|
const row = await this.prisma.customer.update({
|
|
@@ -158,22 +233,56 @@ export class CustomerRepository {
|
|
|
158
233
|
}
|
|
159
234
|
|
|
160
235
|
// Soft-delete, matching every other table's isDeleted/deletedAt/deletedBy/deletedReason pattern.
|
|
161
|
-
async delete(
|
|
236
|
+
async delete(
|
|
237
|
+
workspaceId: string,
|
|
238
|
+
customerId: string,
|
|
239
|
+
actorUserId: string | null,
|
|
240
|
+
reason?: string,
|
|
241
|
+
): Promise<void> {
|
|
162
242
|
const workspaceIdBig = toId(workspaceId);
|
|
163
243
|
const customerIdBig = toId(customerId);
|
|
164
|
-
const existing = await this.prisma.customer.findUnique({
|
|
165
|
-
|
|
244
|
+
const existing = await this.prisma.customer.findUnique({
|
|
245
|
+
where: {
|
|
246
|
+
id: customerIdBig,
|
|
247
|
+
workspaceId: workspaceIdBig,
|
|
248
|
+
isDeleted: false,
|
|
249
|
+
},
|
|
250
|
+
select: { id: true },
|
|
251
|
+
});
|
|
252
|
+
if (!existing)
|
|
253
|
+
throw new NotFoundException(`customer "${customerId}" not found`);
|
|
166
254
|
await this.prisma.customer.update({
|
|
167
255
|
where: { id: customerIdBig },
|
|
168
|
-
data: {
|
|
256
|
+
data: {
|
|
257
|
+
isDeleted: true,
|
|
258
|
+
deletedAt: new Date(),
|
|
259
|
+
deletedBy: toIdOrNull(actorUserId),
|
|
260
|
+
deletedReason: reason ?? null,
|
|
261
|
+
},
|
|
169
262
|
});
|
|
170
263
|
}
|
|
171
264
|
|
|
172
|
-
async setActive(
|
|
265
|
+
async setActive(
|
|
266
|
+
workspaceId: string,
|
|
267
|
+
customerId: string,
|
|
268
|
+
isActive: boolean,
|
|
269
|
+
actorUserId: string | null,
|
|
270
|
+
): Promise<void> {
|
|
173
271
|
const workspaceIdBig = toId(workspaceId);
|
|
174
272
|
const customerIdBig = toId(customerId);
|
|
175
|
-
const existing = await this.prisma.customer.findUnique({
|
|
176
|
-
|
|
177
|
-
|
|
273
|
+
const existing = await this.prisma.customer.findUnique({
|
|
274
|
+
where: {
|
|
275
|
+
id: customerIdBig,
|
|
276
|
+
workspaceId: workspaceIdBig,
|
|
277
|
+
isDeleted: false,
|
|
278
|
+
},
|
|
279
|
+
select: { id: true },
|
|
280
|
+
});
|
|
281
|
+
if (!existing)
|
|
282
|
+
throw new NotFoundException(`customer "${customerId}" not found`);
|
|
283
|
+
await this.prisma.customer.update({
|
|
284
|
+
where: { id: customerIdBig },
|
|
285
|
+
data: { isActive, updatedBy: toIdOrNull(actorUserId) },
|
|
286
|
+
});
|
|
178
287
|
}
|
|
179
288
|
}
|
package/registry/combos/nestjs-prisma/variants/workspaces/src/repositories/language.repository.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConflictException,
|
|
3
|
+
Inject,
|
|
4
|
+
Injectable,
|
|
5
|
+
NotFoundException,
|
|
6
|
+
} from "@nestjs/common";
|
|
7
|
+
import { Prisma, PrismaClient } from "../../generated/prisma/client.js";
|
|
8
|
+
import { toId, toIdOrNull } from "../id.helper.js";
|
|
9
|
+
import {
|
|
10
|
+
buildPageMeta,
|
|
11
|
+
normalizeLimit,
|
|
12
|
+
normalizePage,
|
|
13
|
+
type Paginated,
|
|
14
|
+
} from "../pagination.js";
|
|
15
|
+
|
|
16
|
+
/** A `Language` row as `findUnique`/`findMany` return it. */
|
|
17
|
+
export type LanguageRow = Prisma.LanguageGetPayload<object>;
|
|
18
|
+
|
|
19
|
+
/** Every column the `Language` table has — no secret to withhold, same reasoning as `toUserSummary`. */
|
|
20
|
+
export interface LanguageSummary {
|
|
21
|
+
id: string;
|
|
22
|
+
uuid: string;
|
|
23
|
+
code: string;
|
|
24
|
+
name: string;
|
|
25
|
+
nativeName: string;
|
|
26
|
+
direction: string;
|
|
27
|
+
isDefault: boolean;
|
|
28
|
+
isActive: boolean;
|
|
29
|
+
createdBy: string | null;
|
|
30
|
+
updatedBy: string | null;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
updatedAt: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function toLanguageSummary(row: LanguageRow): LanguageSummary {
|
|
36
|
+
return {
|
|
37
|
+
id: row.id.toString(),
|
|
38
|
+
uuid: row.uuid,
|
|
39
|
+
code: row.code,
|
|
40
|
+
name: row.name,
|
|
41
|
+
nativeName: row.nativeName,
|
|
42
|
+
direction: row.direction,
|
|
43
|
+
isDefault: row.isDefault,
|
|
44
|
+
isActive: row.isActive,
|
|
45
|
+
createdBy: row.createdBy?.toString() ?? null,
|
|
46
|
+
updatedBy: row.updatedBy?.toString() ?? null,
|
|
47
|
+
createdAt: row.createdAt.toISOString(),
|
|
48
|
+
updatedAt: row.updatedAt.toISOString(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface LanguageListFilter {
|
|
53
|
+
search?: string;
|
|
54
|
+
page?: number;
|
|
55
|
+
limit?: number;
|
|
56
|
+
activeOnly?: boolean;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export type LanguageListResult = Paginated<LanguageSummary>;
|
|
60
|
+
|
|
61
|
+
export interface LanguageInput {
|
|
62
|
+
code: string;
|
|
63
|
+
name: string;
|
|
64
|
+
nativeName: string;
|
|
65
|
+
direction?: string;
|
|
66
|
+
isDefault?: boolean;
|
|
67
|
+
isActive?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Every method takes `workspaceId` first, matching `WorkspaceRepository`'s signature style, and
|
|
71
|
+
// every query's `where` includes it — a language lives in exactly one workspace, and `code`
|
|
72
|
+
// uniqueness is checked within that workspace only (see `schema.prisma`'s composite unique constraint).
|
|
73
|
+
@Injectable()
|
|
74
|
+
export class LanguageRepository {
|
|
75
|
+
constructor(@Inject(PrismaClient) private readonly prisma: PrismaClient) {}
|
|
76
|
+
|
|
77
|
+
/** Newest-first, page-paginated; `search` matches name/code/nativeName. Returns raw rows — shaping is the caller's job. */
|
|
78
|
+
async list(
|
|
79
|
+
workspaceId: string,
|
|
80
|
+
filter: LanguageListFilter = {},
|
|
81
|
+
): Promise<LanguageListResult> {
|
|
82
|
+
const page = normalizePage(filter.page);
|
|
83
|
+
const limit = normalizeLimit(filter.limit);
|
|
84
|
+
const where: Prisma.LanguageWhereInput = {
|
|
85
|
+
workspaceId: toId(workspaceId),
|
|
86
|
+
isDeleted: false,
|
|
87
|
+
isActive: filter.activeOnly ? true : undefined,
|
|
88
|
+
OR: filter.search
|
|
89
|
+
? [
|
|
90
|
+
{ name: { contains: filter.search, mode: "insensitive" } },
|
|
91
|
+
{ code: { contains: filter.search, mode: "insensitive" } },
|
|
92
|
+
{ nativeName: { contains: filter.search, mode: "insensitive" } },
|
|
93
|
+
]
|
|
94
|
+
: undefined,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const [rows, total] = await this.prisma.$transaction([
|
|
98
|
+
this.prisma.language.findMany({
|
|
99
|
+
where,
|
|
100
|
+
orderBy: [{ createdAt: "desc" }, { id: "desc" }],
|
|
101
|
+
take: limit,
|
|
102
|
+
skip: (page - 1) * limit,
|
|
103
|
+
}),
|
|
104
|
+
this.prisma.language.count({ where }),
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
items: rows.map(toLanguageSummary),
|
|
109
|
+
meta: buildPageMeta(page, limit, total),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async get(workspaceId: string, languageId: string): Promise<LanguageSummary> {
|
|
114
|
+
const row = await this.prisma.language.findUnique({
|
|
115
|
+
where: {
|
|
116
|
+
id: toId(languageId),
|
|
117
|
+
workspaceId: toId(workspaceId),
|
|
118
|
+
isDeleted: false,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
if (!row) throw new NotFoundException(`language "${languageId}" not found`);
|
|
122
|
+
return toLanguageSummary(row);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async create(
|
|
126
|
+
workspaceId: string,
|
|
127
|
+
input: LanguageInput,
|
|
128
|
+
actorUserId: string | null,
|
|
129
|
+
): Promise<LanguageSummary> {
|
|
130
|
+
const workspaceIdBig = toId(workspaceId);
|
|
131
|
+
const existing = await this.prisma.language.findFirst({
|
|
132
|
+
where: { workspaceId: workspaceIdBig, code: input.code },
|
|
133
|
+
});
|
|
134
|
+
if (existing)
|
|
135
|
+
throw new ConflictException(
|
|
136
|
+
"a language with this code already exists in this workspace",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const row = await this.prisma.language.create({
|
|
140
|
+
data: {
|
|
141
|
+
...input,
|
|
142
|
+
workspaceId: workspaceIdBig,
|
|
143
|
+
createdBy: toIdOrNull(actorUserId),
|
|
144
|
+
updatedBy: toIdOrNull(actorUserId),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
return toLanguageSummary(row);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async update(
|
|
151
|
+
workspaceId: string,
|
|
152
|
+
languageId: string,
|
|
153
|
+
input: Partial<LanguageInput>,
|
|
154
|
+
actorUserId: string | null,
|
|
155
|
+
): Promise<LanguageSummary> {
|
|
156
|
+
const workspaceIdBig = toId(workspaceId);
|
|
157
|
+
const languageIdBig = toId(languageId);
|
|
158
|
+
const existing = await this.prisma.language.findUnique({
|
|
159
|
+
where: {
|
|
160
|
+
id: languageIdBig,
|
|
161
|
+
workspaceId: workspaceIdBig,
|
|
162
|
+
isDeleted: false,
|
|
163
|
+
},
|
|
164
|
+
select: { id: true },
|
|
165
|
+
});
|
|
166
|
+
if (!existing)
|
|
167
|
+
throw new NotFoundException(`language "${languageId}" not found`);
|
|
168
|
+
|
|
169
|
+
if (input.code !== undefined) {
|
|
170
|
+
const clash = await this.prisma.language.findFirst({
|
|
171
|
+
where: {
|
|
172
|
+
workspaceId: workspaceIdBig,
|
|
173
|
+
NOT: { id: languageIdBig },
|
|
174
|
+
code: input.code,
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
if (clash)
|
|
178
|
+
throw new ConflictException(
|
|
179
|
+
"a language with this code already exists in this workspace",
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const row = await this.prisma.language.update({
|
|
184
|
+
where: { id: languageIdBig },
|
|
185
|
+
data: { ...input, updatedBy: toIdOrNull(actorUserId) },
|
|
186
|
+
});
|
|
187
|
+
return toLanguageSummary(row);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Soft-delete, matching every other table's isDeleted/deletedAt/deletedBy/deletedReason pattern.
|
|
191
|
+
async delete(
|
|
192
|
+
workspaceId: string,
|
|
193
|
+
languageId: string,
|
|
194
|
+
actorUserId: string | null,
|
|
195
|
+
reason?: string,
|
|
196
|
+
): Promise<void> {
|
|
197
|
+
const workspaceIdBig = toId(workspaceId);
|
|
198
|
+
const languageIdBig = toId(languageId);
|
|
199
|
+
const existing = await this.prisma.language.findUnique({
|
|
200
|
+
where: {
|
|
201
|
+
id: languageIdBig,
|
|
202
|
+
workspaceId: workspaceIdBig,
|
|
203
|
+
isDeleted: false,
|
|
204
|
+
},
|
|
205
|
+
select: { id: true },
|
|
206
|
+
});
|
|
207
|
+
if (!existing)
|
|
208
|
+
throw new NotFoundException(`language "${languageId}" not found`);
|
|
209
|
+
await this.prisma.language.update({
|
|
210
|
+
where: { id: languageIdBig },
|
|
211
|
+
data: {
|
|
212
|
+
isDeleted: true,
|
|
213
|
+
deletedAt: new Date(),
|
|
214
|
+
deletedBy: toIdOrNull(actorUserId),
|
|
215
|
+
deletedReason: reason ?? null,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async setActive(
|
|
221
|
+
workspaceId: string,
|
|
222
|
+
languageId: string,
|
|
223
|
+
isActive: boolean,
|
|
224
|
+
actorUserId: string | null,
|
|
225
|
+
): Promise<void> {
|
|
226
|
+
const workspaceIdBig = toId(workspaceId);
|
|
227
|
+
const languageIdBig = toId(languageId);
|
|
228
|
+
const existing = await this.prisma.language.findUnique({
|
|
229
|
+
where: {
|
|
230
|
+
id: languageIdBig,
|
|
231
|
+
workspaceId: workspaceIdBig,
|
|
232
|
+
isDeleted: false,
|
|
233
|
+
},
|
|
234
|
+
select: { id: true },
|
|
235
|
+
});
|
|
236
|
+
if (!existing)
|
|
237
|
+
throw new NotFoundException(`language "${languageId}" not found`);
|
|
238
|
+
await this.prisma.language.update({
|
|
239
|
+
where: { id: languageIdBig },
|
|
240
|
+
data: { isActive, updatedBy: toIdOrNull(actorUserId) },
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
}
|