@open-mercato/core 0.6.3-develop.3525.3.cc18b8a43f → 0.6.3-develop.3574.1.f3c5dcf345
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/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/queue-runner.js +15 -3
- package/dist/helpers/integration/queue-runner.js.map +2 -2
- package/dist/helpers/integration/queue.js +1 -1
- package/dist/helpers/integration/queue.js.map +2 -2
- package/dist/modules/auth/api/roles/acl/route.js +37 -1
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/roles/route.js +36 -2
- package/dist/modules/auth/api/roles/route.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +36 -1
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +89 -11
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +32 -14
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +1 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +1 -1
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/backend/users/roleOptions.js +1 -1
- package/dist/modules/auth/backend/users/roleOptions.js.map +2 -2
- package/dist/modules/auth/commands/roles.js +8 -0
- package/dist/modules/auth/commands/roles.js.map +2 -2
- package/dist/modules/auth/commands/users.js +3 -0
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/components/AclEditor.js +53 -26
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js +97 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -7
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +41 -12
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +104 -0
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/data/validators.js +20 -1
- package/dist/modules/sales/data/validators.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +9 -4
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +23 -11
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/jest.config.cjs +1 -0
- package/package.json +7 -7
- package/src/helpers/integration/queue-runner.ts +21 -2
- package/src/helpers/integration/queue.ts +1 -1
- package/src/modules/auth/api/roles/acl/route.ts +39 -1
- package/src/modules/auth/api/roles/route.ts +38 -2
- package/src/modules/auth/api/users/acl/route.ts +41 -1
- package/src/modules/auth/api/users/route.ts +92 -11
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +35 -13
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +1 -1
- package/src/modules/auth/backend/users/create/page.tsx +1 -1
- package/src/modules/auth/backend/users/roleOptions.ts +2 -1
- package/src/modules/auth/commands/roles.ts +8 -0
- package/src/modules/auth/commands/users.ts +3 -0
- package/src/modules/auth/components/AclEditor.tsx +50 -24
- package/src/modules/auth/lib/grantChecks.ts +116 -2
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -13
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +38 -12
- package/src/modules/sales/commands/documents.ts +116 -0
- package/src/modules/sales/data/validators.ts +38 -0
- package/src/modules/sales/i18n/de.json +2 -0
- package/src/modules/sales/i18n/en.json +2 -0
- package/src/modules/sales/i18n/es.json +2 -0
- package/src/modules/sales/i18n/pl.json +2 -0
- package/src/modules/translations/components/TranslationManager.tsx +9 -4
- package/src/modules/workflows/lib/event-trigger-service.ts +24 -11
|
@@ -9,7 +9,11 @@ import { Organization, Tenant } from "@open-mercato/core/modules/directory/data/
|
|
|
9
9
|
import { E } from "../../../../generated/entities.ids.generated.js";
|
|
10
10
|
import { loadCustomFieldValues } from "@open-mercato/shared/lib/crud/custom-fields";
|
|
11
11
|
import { userCrudEvents, userCrudIndexer } from "@open-mercato/core/modules/auth/commands/users";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
assertActorCanGrantRoleTokens,
|
|
14
|
+
assertActorCanModifySuperAdminUserTarget,
|
|
15
|
+
listSuperAdminUserIds
|
|
16
|
+
} from "@open-mercato/core/modules/auth/lib/grantChecks";
|
|
13
17
|
import { findOneWithDecryption, findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
14
18
|
import { buildPasswordSchema } from "@open-mercato/shared/lib/auth/passwordPolicy";
|
|
15
19
|
import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
|
|
@@ -17,6 +21,10 @@ import { resolveSearchConfig } from "@open-mercato/shared/lib/search/config";
|
|
|
17
21
|
import { tokenizeText } from "@open-mercato/shared/lib/search/tokenize";
|
|
18
22
|
import { sql } from "kysely";
|
|
19
23
|
import { normalizeDisplayNameInput } from "@open-mercato/core/modules/auth/lib/displayName";
|
|
24
|
+
import {
|
|
25
|
+
getSelectedTenantFromRequest,
|
|
26
|
+
resolveOrganizationScopeForRequest
|
|
27
|
+
} from "@open-mercato/core/modules/directory/utils/organizationScope";
|
|
20
28
|
const querySchema = z.object({
|
|
21
29
|
id: z.string().uuid().optional(),
|
|
22
30
|
page: z.coerce.number().min(1).default(1),
|
|
@@ -109,6 +117,9 @@ const crud = makeCrudRoute({
|
|
|
109
117
|
schema: rawBodySchema,
|
|
110
118
|
mapInput: async ({ parsed, ctx }) => {
|
|
111
119
|
if (ctx.request) {
|
|
120
|
+
if (typeof parsed.id === "string" && parsed.id.length) {
|
|
121
|
+
await assertCanModifySuperAdminTarget(ctx.request, parsed.id);
|
|
122
|
+
}
|
|
112
123
|
await assertCanAssignRoles(ctx.request, parsed.roles, parsed);
|
|
113
124
|
}
|
|
114
125
|
return parsed;
|
|
@@ -138,12 +149,12 @@ async function GET(req) {
|
|
|
138
149
|
if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 });
|
|
139
150
|
const container = await createRequestContainer();
|
|
140
151
|
const em = container.resolve("em");
|
|
141
|
-
let isSuperAdmin =
|
|
152
|
+
let isSuperAdmin = auth.isSuperAdmin === true;
|
|
142
153
|
try {
|
|
143
154
|
if (auth.sub) {
|
|
144
155
|
const rbacService = container.resolve("rbacService");
|
|
145
156
|
const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null });
|
|
146
|
-
isSuperAdmin = !!acl?.isSuperAdmin;
|
|
157
|
+
isSuperAdmin = isSuperAdmin || !!acl?.isSuperAdmin;
|
|
147
158
|
}
|
|
148
159
|
} catch (err) {
|
|
149
160
|
console.error("users: failed to resolve rbac", err);
|
|
@@ -151,12 +162,49 @@ async function GET(req) {
|
|
|
151
162
|
const { id, page, pageSize, search, name, organizationId, roleIds } = parsed.data;
|
|
152
163
|
const filters = [{ deletedAt: null }];
|
|
153
164
|
const actorTenantId = auth.tenantId ? String(auth.tenantId) : null;
|
|
165
|
+
let effectiveTenantId = null;
|
|
166
|
+
let effectiveOrganizationIds = null;
|
|
167
|
+
let effectiveSelectedOrganizationId = null;
|
|
168
|
+
let usesSelectedTenantScope = false;
|
|
154
169
|
if (!isSuperAdmin) {
|
|
155
170
|
if (!actorTenantId) {
|
|
156
171
|
return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
|
|
157
172
|
}
|
|
158
|
-
|
|
173
|
+
effectiveTenantId = actorTenantId;
|
|
174
|
+
const superAdminUserIds = await listSuperAdminUserIds(em, actorTenantId);
|
|
175
|
+
if (superAdminUserIds.size) {
|
|
176
|
+
filters.push({ id: { $nin: Array.from(superAdminUserIds) } });
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
const selectedTenantId = getSelectedTenantFromRequest(req);
|
|
180
|
+
if (typeof selectedTenantId === "string" && selectedTenantId.trim().length > 0) {
|
|
181
|
+
const scope = await resolveOrganizationScopeForRequest({
|
|
182
|
+
container,
|
|
183
|
+
auth,
|
|
184
|
+
request: req,
|
|
185
|
+
tenantId: selectedTenantId.trim()
|
|
186
|
+
});
|
|
187
|
+
if (!scope.tenantId) {
|
|
188
|
+
return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
|
|
189
|
+
}
|
|
190
|
+
effectiveTenantId = scope.tenantId;
|
|
191
|
+
effectiveSelectedOrganizationId = scope.selectedId;
|
|
192
|
+
usesSelectedTenantScope = true;
|
|
193
|
+
if (Array.isArray(scope.filterIds)) {
|
|
194
|
+
if (scope.filterIds.length === 0) {
|
|
195
|
+
return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin });
|
|
196
|
+
}
|
|
197
|
+
effectiveOrganizationIds = scope.filterIds;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (effectiveTenantId) {
|
|
202
|
+
filters.push({ tenantId: effectiveTenantId });
|
|
159
203
|
}
|
|
204
|
+
if (effectiveOrganizationIds) {
|
|
205
|
+
filters.push({ organizationId: { $in: effectiveOrganizationIds } });
|
|
206
|
+
}
|
|
207
|
+
const scopeOrganizationId = usesSelectedTenantScope ? effectiveSelectedOrganizationId : auth.orgId ?? null;
|
|
160
208
|
if (organizationId) filters.push({ organizationId });
|
|
161
209
|
const trimmedName = typeof name === "string" ? name.trim() : "";
|
|
162
210
|
if (trimmedName) {
|
|
@@ -183,7 +231,7 @@ async function GET(req) {
|
|
|
183
231
|
}
|
|
184
232
|
const trimmedSearch = typeof search === "string" ? search.trim() : "";
|
|
185
233
|
if (trimmedSearch) {
|
|
186
|
-
const tenantScope = isSuperAdmin ? void 0 : auth.tenantId ?? null;
|
|
234
|
+
const tenantScope = isSuperAdmin ? effectiveTenantId ?? void 0 : auth.tenantId ?? null;
|
|
187
235
|
const searchFilters = [];
|
|
188
236
|
const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope);
|
|
189
237
|
if (matchedIds && matchedIds.length) {
|
|
@@ -251,7 +299,10 @@ async function GET(req) {
|
|
|
251
299
|
UserRole,
|
|
252
300
|
{ user: { $in: userIds } },
|
|
253
301
|
{ populate: ["role"] },
|
|
254
|
-
{
|
|
302
|
+
{
|
|
303
|
+
tenantId: effectiveTenantId ?? auth.tenantId ?? null,
|
|
304
|
+
organizationId: scopeOrganizationId
|
|
305
|
+
}
|
|
255
306
|
) : [];
|
|
256
307
|
const roleMap = {};
|
|
257
308
|
const roleIdMap = {};
|
|
@@ -311,7 +362,7 @@ async function GET(req) {
|
|
|
311
362
|
recordIds: userIds.map(String),
|
|
312
363
|
tenantIdByRecord: tenantByUser,
|
|
313
364
|
organizationIdByRecord: organizationByUser,
|
|
314
|
-
tenantFallbacks: auth.tenantId ? [auth.tenantId] : []
|
|
365
|
+
tenantFallbacks: effectiveTenantId ? [effectiveTenantId] : auth.tenantId ? [auth.tenantId] : []
|
|
315
366
|
}) : {};
|
|
316
367
|
const items = rows.map((u) => {
|
|
317
368
|
const uid = String(u.id);
|
|
@@ -338,8 +389,8 @@ async function GET(req) {
|
|
|
338
389
|
items,
|
|
339
390
|
idField: "id",
|
|
340
391
|
resourceKind: "auth.user",
|
|
341
|
-
organizationId:
|
|
342
|
-
tenantId: auth.tenantId ?? null,
|
|
392
|
+
organizationId: effectiveSelectedOrganizationId,
|
|
393
|
+
tenantId: effectiveTenantId ?? auth.tenantId ?? null,
|
|
343
394
|
query: parsed.data,
|
|
344
395
|
accessType: id ? "read:item" : void 0
|
|
345
396
|
});
|
|
@@ -351,7 +402,20 @@ const POST = async (req) => {
|
|
|
351
402
|
const PUT = async (req) => {
|
|
352
403
|
return crud.PUT(req);
|
|
353
404
|
};
|
|
354
|
-
const DELETE =
|
|
405
|
+
const DELETE = async (req) => {
|
|
406
|
+
const targetId = new URL(req.url).searchParams.get("id");
|
|
407
|
+
if (targetId) {
|
|
408
|
+
try {
|
|
409
|
+
await assertCanModifySuperAdminTarget(req, targetId);
|
|
410
|
+
} catch (err) {
|
|
411
|
+
if (err instanceof CrudHttpError) {
|
|
412
|
+
return NextResponse.json(err.body, { status: err.status });
|
|
413
|
+
}
|
|
414
|
+
throw err;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
return crud.DELETE(req);
|
|
418
|
+
};
|
|
355
419
|
async function findUserIdsBySearchTokens(em, entityType, search, tenantScope) {
|
|
356
420
|
const trimmed = search.trim();
|
|
357
421
|
if (!trimmed) return null;
|
|
@@ -367,6 +431,20 @@ async function findUserIdsBySearchTokens(em, entityType, search, tenantScope) {
|
|
|
367
431
|
const rows = await query.execute();
|
|
368
432
|
return rows.map((row) => typeof row.entity_id === "string" ? row.entity_id : null).filter((id) => typeof id === "string" && id.length > 0);
|
|
369
433
|
}
|
|
434
|
+
async function assertCanModifySuperAdminTarget(req, targetUserId) {
|
|
435
|
+
const auth = await getAuthFromRequest(req);
|
|
436
|
+
if (!auth?.sub) throw new CrudHttpError(401, { error: "Unauthorized" });
|
|
437
|
+
const container = await createRequestContainer();
|
|
438
|
+
const em = container.resolve("em");
|
|
439
|
+
await assertActorCanModifySuperAdminUserTarget({
|
|
440
|
+
em,
|
|
441
|
+
rbacService: container.resolve("rbacService"),
|
|
442
|
+
actorUserId: auth.sub,
|
|
443
|
+
tenantId: auth.tenantId ?? null,
|
|
444
|
+
organizationId: auth.orgId ?? null,
|
|
445
|
+
targetUserId
|
|
446
|
+
});
|
|
447
|
+
}
|
|
370
448
|
async function assertCanAssignRoles(req, roles, payload) {
|
|
371
449
|
if (!Array.isArray(roles)) return;
|
|
372
450
|
const auth = await getAuthFromRequest(req);
|
|
@@ -414,7 +492,7 @@ const openApi = {
|
|
|
414
492
|
methods: {
|
|
415
493
|
GET: {
|
|
416
494
|
summary: "List users",
|
|
417
|
-
description: "Returns users for the
|
|
495
|
+
description: "Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters.",
|
|
418
496
|
query: querySchema,
|
|
419
497
|
responses: [
|
|
420
498
|
{ status: 200, description: "User collection", schema: userListResponseSchema }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/auth/api/users/route.ts"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport { assertActorCanGrantRoleTokens } from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\nimport { normalizeDisplayNameInput } from '@open-mercato/core/modules/auth/lib/displayName'\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n name: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst passwordSchema = buildPasswordSchema()\n\nconst displayNameSchema = z.preprocess(\n normalizeDisplayNameInput,\n z.string().trim().min(1).max(120).nullable().optional(),\n)\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n name: z.string().nullable(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n roleIds: z.array(z.string().uuid()).optional(),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: ({ result }) => ({\n id: String(result.user.id),\n ...(result.warning ? { _warning: result.warning } : {}),\n }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n name: url.searchParams.get('name') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = false\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = !!acl?.isSuperAdmin\n }\n } catch (err) {\n console.error('users: failed to resolve rbac', err)\n }\n const { id, page, pageSize, search, name, organizationId, roleIds } = parsed.data\n const filters: any[] = [{ deletedAt: null }]\n const actorTenantId = auth.tenantId ? String(auth.tenantId) : null\n if (!isSuperAdmin) {\n if (!actorTenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n filters.push({ tenantId: actorTenantId })\n }\n if (organizationId) filters.push({ organizationId })\n const trimmedName = typeof name === 'string' ? name.trim() : ''\n if (trimmedName) {\n filters.push({ name: { $ilike: `%${escapeLikePattern(trimmedName)}%` } })\n }\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n const trimmedSearch = typeof search === 'string' ? search.trim() : ''\n if (trimmedSearch) {\n // Email is encrypted at rest, so plaintext search must go through search_tokens.\n const tenantScope: string | null | undefined = isSuperAdmin ? undefined : auth.tenantId ?? null\n const searchFilters: any[] = []\n\n const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope)\n if (matchedIds && matchedIds.length) {\n searchFilters.push({ id: { $in: matchedIds as any } })\n }\n\n const searchPattern = `%${escapeLikePattern(trimmedSearch)}%`\n const organizationSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n organizationSearchFilters.push({ tenant: tenantScope })\n }\n const matchingOrganizations = await em.find(\n Organization,\n organizationSearchFilters.length > 1 ? { $and: organizationSearchFilters } : organizationSearchFilters[0],\n )\n const matchingOrganizationIds = matchingOrganizations\n .map((org) => (org?.id ? String(org.id) : null))\n .filter((orgId): orgId is string => typeof orgId === 'string' && orgId.length > 0)\n if (matchingOrganizationIds.length) {\n searchFilters.push({ organizationId: { $in: matchingOrganizationIds as any } })\n }\n\n const roleSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n roleSearchFilters.push({ $or: [{ tenantId: tenantScope }, { tenantId: null }] })\n }\n const matchingRoles = await em.find(\n Role,\n roleSearchFilters.length > 1 ? { $and: roleSearchFilters } : roleSearchFilters[0],\n )\n const matchingRoleIds = matchingRoles\n .map((role) => (role?.id ? String(role.id) : null))\n .filter((roleId): roleId is string => typeof roleId === 'string' && roleId.length > 0)\n if (matchingRoleIds.length) {\n const roleSearchLinks = await em.find(\n UserRole,\n { role: { $in: matchingRoleIds as any } } as any,\n )\n const matchingRoleUserIds = Array.from(new Set(\n roleSearchLinks\n .map((link) => {\n const userRef = (link as any).user\n const userId = userRef?.id ?? userRef\n return userId ? String(userId) : null\n })\n .filter((userId): userId is string => typeof userId === 'string' && userId.length > 0),\n ))\n if (matchingRoleUserIds.length) {\n searchFilters.push({ id: { $in: matchingRoleUserIds as any } })\n }\n }\n\n if (!searchFilters.length) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n\n filters.push(searchFilters.length > 1 ? { $or: searchFilters } : searchFilters[0])\n }\n if (idFilter && idFilter.size) {\n filters.push({ id: { $in: Array.from(idFilter) as any } })\n } else if (id) {\n filters.push({ id })\n }\n const where = filters.length > 1 ? { $and: filters } : filters[0]\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n const roleIdMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n const rid = String((l as any).role?.id ?? '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (!roleIdMap[uid]) roleIdMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n if (rid) roleIdMap[uid].push(rid)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n name: u.name ? String(u.name) : null,\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n roleIds: roleIdMap[uid] || [],\n hasPassword: !!u.passwordHash,\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: null,\n tenantId: auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n return crud.PUT(req)\n}\n\nexport const DELETE = crud.DELETE\n\nasync function findUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nasync function assertCanAssignRoles(req: Request, roles: unknown, payload: Record<string, unknown>) {\n if (!Array.isArray(roles)) return\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const tenantId = await resolveTargetTenantIdForRoleGrant(em, payload, auth.tenantId ?? null)\n await assertActorCanGrantRoleTokens({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId,\n organizationId: auth.orgId ?? null,\n roleTokens: roles,\n })\n}\n\nasync function resolveTargetTenantIdForRoleGrant(\n em: EntityManager,\n payload: Record<string, unknown>,\n fallbackTenantId: string | null,\n): Promise<string | null> {\n const organizationId = typeof payload.organizationId === 'string' ? payload.organizationId : null\n if (organizationId) {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )\n return organization?.tenant?.id ? String(organization.tenant.id) : fallbackTenantId\n }\n\n const userId = typeof payload.id === 'string' ? payload.id : null\n if (userId) {\n const user = await findOneWithDecryption(\n em,\n User,\n { id: userId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n return user?.tenantId ? String(user.tenantId) : fallbackTenantId\n }\n\n return fallbackTenantId\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the current tenant. Search matches email, organization name, and role name. Super administrators may scope the response via organization or role filters.',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization, optional display name, and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields including display name, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { logCrudAccess, makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { loadCustomFieldValues } from '@open-mercato/shared/lib/crud/custom-fields'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { userCrudEvents, userCrudIndexer } from '@open-mercato/core/modules/auth/commands/users'\nimport {\n assertActorCanGrantRoleTokens,\n assertActorCanModifySuperAdminUserTarget,\n listSuperAdminUserIds,\n} from '@open-mercato/core/modules/auth/lib/grantChecks'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildPasswordSchema } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { resolveSearchConfig } from '@open-mercato/shared/lib/search/config'\nimport { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'\nimport { sql } from 'kysely'\nimport { normalizeDisplayNameInput } from '@open-mercato/core/modules/auth/lib/displayName'\nimport {\n getSelectedTenantFromRequest,\n resolveOrganizationScopeForRequest,\n} from '@open-mercato/core/modules/directory/utils/organizationScope'\n\nconst querySchema = z.object({\n id: z.string().uuid().optional(),\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n name: z.string().optional(),\n organizationId: z.string().uuid().optional(),\n roleIds: z.array(z.string().uuid()).optional(),\n}).passthrough()\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst passwordSchema = buildPasswordSchema()\n\nconst displayNameSchema = z.preprocess(\n normalizeDisplayNameInput,\n z.string().trim().min(1).max(120).nullable().optional(),\n)\n\nconst userCreateSchema = z.object({\n email: z.string().email(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n sendInviteEmail: z.boolean().optional(),\n organizationId: z.string().uuid(),\n roles: z.array(z.string()).optional(),\n}).refine(\n (data) => data.password || data.sendInviteEmail,\n { message: 'Either password or sendInviteEmail is required', path: ['password'] },\n)\n\nconst userUpdateSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email().optional(),\n name: displayNameSchema,\n password: passwordSchema.optional(),\n organizationId: z.string().uuid().optional(),\n roles: z.array(z.string()).optional(),\n})\n\nconst userListItemSchema = z.object({\n id: z.string().uuid(),\n email: z.string().email(),\n name: z.string().nullable(),\n organizationId: z.string().uuid().nullable(),\n organizationName: z.string().nullable(),\n tenantId: z.string().uuid().nullable(),\n tenantName: z.string().nullable(),\n roles: z.array(z.string()),\n roleIds: z.array(z.string().uuid()).optional(),\n})\n\nconst userListResponseSchema = z.object({\n items: z.array(userListItemSchema),\n total: z.number().int().nonnegative(),\n totalPages: z.number().int().positive(),\n isSuperAdmin: z.boolean().optional(),\n})\n\nconst okResponseSchema = z.object({ ok: z.literal(true) })\n\nconst errorResponseSchema = z.object({ error: z.string() })\n\ntype CrudInput = Record<string, unknown>\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['auth.users.list'] },\n POST: { requireAuth: true, requireFeatures: ['auth.users.create'] },\n PUT: { requireAuth: true, requireFeatures: ['auth.users.edit'] },\n DELETE: { requireAuth: true, requireFeatures: ['auth.users.delete'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute<CrudInput, CrudInput, Record<string, unknown>>({\n metadata: routeMetadata,\n orm: {\n entity: User,\n idField: 'id',\n orgField: null,\n tenantField: null,\n softDeleteField: 'deletedAt',\n },\n events: userCrudEvents,\n indexer: userCrudIndexer,\n actions: {\n create: {\n commandId: 'auth.users.create',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: ({ result }) => ({\n id: String(result.user.id),\n ...(result.warning ? { _warning: result.warning } : {}),\n }),\n status: 201,\n },\n update: {\n commandId: 'auth.users.update',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n if (ctx.request) {\n if (typeof parsed.id === 'string' && parsed.id.length) {\n await assertCanModifySuperAdminTarget(ctx.request, parsed.id)\n }\n await assertCanAssignRoles(ctx.request, parsed.roles, parsed)\n }\n return parsed\n },\n response: () => ({ ok: true }),\n },\n delete: {\n commandId: 'auth.users.delete',\n response: () => ({ ok: true }),\n },\n },\n})\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const url = new URL(req.url)\n const rawRoleIds = url.searchParams.getAll('roleId').filter((id): id is string => typeof id === 'string' && id.trim().length > 0)\n const parsed = querySchema.safeParse({\n id: url.searchParams.get('id') || undefined,\n page: url.searchParams.get('page') || undefined,\n pageSize: url.searchParams.get('pageSize') || undefined,\n search: url.searchParams.get('search') || undefined,\n name: url.searchParams.get('name') || undefined,\n organizationId: url.searchParams.get('organizationId') || undefined,\n roleIds: rawRoleIds.length ? rawRoleIds : undefined,\n })\n if (!parsed.success) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n let isSuperAdmin = auth.isSuperAdmin === true\n try {\n if (auth.sub) {\n const rbacService = container.resolve('rbacService') as any\n const acl = await rbacService.loadAcl(auth.sub, { tenantId: auth.tenantId ?? null, organizationId: auth.orgId ?? null })\n isSuperAdmin = isSuperAdmin || !!acl?.isSuperAdmin\n }\n } catch (err) {\n console.error('users: failed to resolve rbac', err)\n }\n const { id, page, pageSize, search, name, organizationId, roleIds } = parsed.data\n const filters: any[] = [{ deletedAt: null }]\n const actorTenantId = auth.tenantId ? String(auth.tenantId) : null\n let effectiveTenantId: string | null = null\n let effectiveOrganizationIds: string[] | null = null\n let effectiveSelectedOrganizationId: string | null = null\n let usesSelectedTenantScope = false\n if (!isSuperAdmin) {\n if (!actorTenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = actorTenantId\n const superAdminUserIds = await listSuperAdminUserIds(em, actorTenantId)\n if (superAdminUserIds.size) {\n filters.push({ id: { $nin: Array.from(superAdminUserIds) as any } })\n }\n } else {\n const selectedTenantId = getSelectedTenantFromRequest(req)\n if (typeof selectedTenantId === 'string' && selectedTenantId.trim().length > 0) {\n const scope = await resolveOrganizationScopeForRequest({\n container,\n auth,\n request: req,\n tenantId: selectedTenantId.trim(),\n })\n if (!scope.tenantId) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveTenantId = scope.tenantId\n effectiveSelectedOrganizationId = scope.selectedId\n usesSelectedTenantScope = true\n if (Array.isArray(scope.filterIds)) {\n if (scope.filterIds.length === 0) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n effectiveOrganizationIds = scope.filterIds\n }\n }\n }\n if (effectiveTenantId) {\n filters.push({ tenantId: effectiveTenantId })\n }\n if (effectiveOrganizationIds) {\n filters.push({ organizationId: { $in: effectiveOrganizationIds as any } })\n }\n const scopeOrganizationId = usesSelectedTenantScope\n ? effectiveSelectedOrganizationId\n : auth.orgId ?? null\n if (organizationId) filters.push({ organizationId })\n const trimmedName = typeof name === 'string' ? name.trim() : ''\n if (trimmedName) {\n filters.push({ name: { $ilike: `%${escapeLikePattern(trimmedName)}%` } })\n }\n let idFilter: Set<string> | null = id ? new Set([id]) : null\n if (Array.isArray(roleIds) && roleIds.length > 0) {\n const uniqueRoleIds = Array.from(new Set(roleIds))\n const linksForRoles = await em.find(UserRole, { role: { $in: uniqueRoleIds as any } } as any)\n const roleUserIds = new Set<string>()\n for (const link of linksForRoles) {\n const uid = String((link as any).user?.id || (link as any).user || '')\n if (uid) roleUserIds.add(uid)\n }\n if (roleUserIds.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n if (idFilter) {\n for (const uid of Array.from(idFilter)) {\n if (!roleUserIds.has(uid)) idFilter.delete(uid)\n }\n } else {\n idFilter = roleUserIds\n }\n if (!idFilter || idFilter.size === 0) return NextResponse.json({ items: [], total: 0, totalPages: 1 })\n }\n const trimmedSearch = typeof search === 'string' ? search.trim() : ''\n if (trimmedSearch) {\n // Email is encrypted at rest, so plaintext search must go through search_tokens.\n const tenantScope: string | null | undefined = isSuperAdmin ? (effectiveTenantId ?? undefined) : auth.tenantId ?? null\n const searchFilters: any[] = []\n\n const matchedIds = await findUserIdsBySearchTokens(em, E.auth.user, trimmedSearch, tenantScope)\n if (matchedIds && matchedIds.length) {\n searchFilters.push({ id: { $in: matchedIds as any } })\n }\n\n const searchPattern = `%${escapeLikePattern(trimmedSearch)}%`\n const organizationSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n organizationSearchFilters.push({ tenant: tenantScope })\n }\n const matchingOrganizations = await em.find(\n Organization,\n organizationSearchFilters.length > 1 ? { $and: organizationSearchFilters } : organizationSearchFilters[0],\n )\n const matchingOrganizationIds = matchingOrganizations\n .map((org) => (org?.id ? String(org.id) : null))\n .filter((orgId): orgId is string => typeof orgId === 'string' && orgId.length > 0)\n if (matchingOrganizationIds.length) {\n searchFilters.push({ organizationId: { $in: matchingOrganizationIds as any } })\n }\n\n const roleSearchFilters: any[] = [\n { deletedAt: null },\n { name: { $ilike: searchPattern } },\n ]\n if (tenantScope) {\n roleSearchFilters.push({ $or: [{ tenantId: tenantScope }, { tenantId: null }] })\n }\n const matchingRoles = await em.find(\n Role,\n roleSearchFilters.length > 1 ? { $and: roleSearchFilters } : roleSearchFilters[0],\n )\n const matchingRoleIds = matchingRoles\n .map((role) => (role?.id ? String(role.id) : null))\n .filter((roleId): roleId is string => typeof roleId === 'string' && roleId.length > 0)\n if (matchingRoleIds.length) {\n const roleSearchLinks = await em.find(\n UserRole,\n { role: { $in: matchingRoleIds as any } } as any,\n )\n const matchingRoleUserIds = Array.from(new Set(\n roleSearchLinks\n .map((link) => {\n const userRef = (link as any).user\n const userId = userRef?.id ?? userRef\n return userId ? String(userId) : null\n })\n .filter((userId): userId is string => typeof userId === 'string' && userId.length > 0),\n ))\n if (matchingRoleUserIds.length) {\n searchFilters.push({ id: { $in: matchingRoleUserIds as any } })\n }\n }\n\n if (!searchFilters.length) {\n return NextResponse.json({ items: [], total: 0, totalPages: 1, isSuperAdmin })\n }\n\n filters.push(searchFilters.length > 1 ? { $or: searchFilters } : searchFilters[0])\n }\n if (idFilter && idFilter.size) {\n filters.push({ id: { $in: Array.from(idFilter) as any } })\n } else if (id) {\n filters.push({ id })\n }\n const where = filters.length > 1 ? { $and: filters } : filters[0]\n const [rows, count] = await em.findAndCount(User, where, { limit: pageSize, offset: (page - 1) * pageSize })\n const userIds = rows.map((u: any) => u.id)\n const links = userIds.length\n ? await findWithDecryption(\n em,\n UserRole,\n { user: { $in: userIds as any } } as any,\n { populate: ['role'] },\n {\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n organizationId: scopeOrganizationId,\n },\n )\n : []\n const roleMap: Record<string, string[]> = {}\n const roleIdMap: Record<string, string[]> = {}\n for (const l of links) {\n const uid = String((l as any).user?.id || (l as any).user)\n const rname = String((l as any).role?.name || '')\n const rid = String((l as any).role?.id ?? '')\n if (!roleMap[uid]) roleMap[uid] = []\n if (!roleIdMap[uid]) roleIdMap[uid] = []\n if (rname) roleMap[uid].push(rname)\n if (rid) roleIdMap[uid].push(rid)\n }\n const orgIds = rows\n .map((u: any) => (u.organizationId ? String(u.organizationId) : null))\n .filter((id): id is string => !!id)\n const uniqueOrgIds = Array.from(new Set(orgIds))\n let orgMap: Record<string, string> = {}\n if (uniqueOrgIds.length) {\n const organizations = await em.find(\n Organization,\n { id: { $in: uniqueOrgIds as any }, deletedAt: null },\n )\n orgMap = organizations.reduce<Record<string, string>>((acc, org) => {\n const orgId = org?.id ? String(org.id) : null\n if (!orgId) return acc\n const rawName = (org as any)?.name\n const orgName = typeof rawName === 'string' && rawName.length > 0 ? rawName : orgId\n acc[orgId] = orgName\n return acc\n }, {})\n }\n const tenantIds = rows\n .map((u: any) => (u.tenantId ? String(u.tenantId) : null))\n .filter((id): id is string => !!id)\n const uniqueTenantIds = Array.from(new Set(tenantIds))\n let tenantMap: Record<string, string> = {}\n if (uniqueTenantIds.length) {\n const tenants = await em.find(\n Tenant,\n { id: { $in: uniqueTenantIds as any }, deletedAt: null },\n )\n tenantMap = tenants.reduce<Record<string, string>>((acc, tenant) => {\n const tenantId = tenant?.id ? String(tenant.id) : null\n if (!tenantId) return acc\n const rawName = (tenant as any)?.name\n const tenantName = typeof rawName === 'string' && rawName.length > 0 ? rawName : tenantId\n acc[tenantId] = tenantName\n return acc\n }, {})\n }\n const tenantByUser: Record<string, string | null> = {}\n const organizationByUser: Record<string, string | null> = {}\n for (const u of rows) {\n const uid = String(u.id)\n tenantByUser[uid] = u.tenantId ? String(u.tenantId) : null\n organizationByUser[uid] = u.organizationId ? String(u.organizationId) : null\n }\n const cfByUser = userIds.length\n ? await loadCustomFieldValues({\n em,\n entityId: E.auth.user,\n recordIds: userIds.map(String),\n tenantIdByRecord: tenantByUser,\n organizationIdByRecord: organizationByUser,\n tenantFallbacks: effectiveTenantId ? [effectiveTenantId] : auth.tenantId ? [auth.tenantId] : [],\n })\n : {}\n\n const items = rows.map((u: any) => {\n const uid = String(u.id)\n const orgId = u.organizationId ? String(u.organizationId) : null\n return {\n id: uid,\n email: String(u.email),\n name: u.name ? String(u.name) : null,\n organizationId: orgId,\n organizationName: orgId ? orgMap[orgId] ?? orgId : null,\n tenantId: u.tenantId ? String(u.tenantId) : null,\n tenantName: u.tenantId ? tenantMap[String(u.tenantId)] ?? String(u.tenantId) : null,\n roles: roleMap[uid] || [],\n roleIds: roleIdMap[uid] || [],\n hasPassword: !!u.passwordHash,\n ...(cfByUser[uid] || {}),\n }\n })\n const totalPages = Math.max(1, Math.ceil(count / pageSize))\n await logCrudAccess({\n container,\n auth,\n request: req,\n items,\n idField: 'id',\n resourceKind: 'auth.user',\n organizationId: effectiveSelectedOrganizationId,\n tenantId: effectiveTenantId ?? auth.tenantId ?? null,\n query: parsed.data,\n accessType: id ? 'read:item' : undefined,\n })\n return NextResponse.json({ items, total: count, totalPages, isSuperAdmin })\n}\n\nexport const POST = async (req: Request) => {\n return crud.POST(req)\n}\n\nexport const PUT = async (req: Request) => {\n return crud.PUT(req)\n}\n\nexport const DELETE = async (req: Request) => {\n const targetId = new URL(req.url).searchParams.get('id')\n if (targetId) {\n try {\n await assertCanModifySuperAdminTarget(req, targetId)\n } catch (err) {\n if (err instanceof CrudHttpError) {\n return NextResponse.json(err.body, { status: err.status })\n }\n throw err\n }\n }\n return crud.DELETE(req)\n}\n\nasync function findUserIdsBySearchTokens(\n em: EntityManager,\n entityType: string,\n search: string,\n tenantScope: string | null | undefined,\n): Promise<string[] | null> {\n const trimmed = search.trim()\n if (!trimmed) return null\n const searchConfig = resolveSearchConfig()\n if (!searchConfig.enabled) return []\n const { hashes } = tokenizeText(trimmed, searchConfig)\n if (!hashes.length) return []\n\n const db = (em as any).getKysely() as any\n let query = db\n .selectFrom('search_tokens')\n .select('entity_id')\n .where('entity_type', '=', entityType)\n .where('token_hash', 'in', hashes)\n .groupBy('entity_id')\n .having(sql<boolean>`count(distinct token_hash) >= ${hashes.length}`)\n if (tenantScope !== undefined) {\n query = query.where(sql<boolean>`tenant_id is not distinct from ${tenantScope}`)\n }\n const rows = (await query.execute()) as Array<{ entity_id?: unknown }>\n return rows\n .map((row) => (typeof row.entity_id === 'string' ? row.entity_id : null))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n}\n\nasync function assertCanModifySuperAdminTarget(req: Request, targetUserId: string) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n await assertActorCanModifySuperAdminUserTarget({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n targetUserId,\n })\n}\n\nasync function assertCanAssignRoles(req: Request, roles: unknown, payload: Record<string, unknown>) {\n if (!Array.isArray(roles)) return\n const auth = await getAuthFromRequest(req)\n if (!auth?.sub) throw new CrudHttpError(401, { error: 'Unauthorized' })\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n const tenantId = await resolveTargetTenantIdForRoleGrant(em, payload, auth.tenantId ?? null)\n await assertActorCanGrantRoleTokens({\n em,\n rbacService: container.resolve('rbacService') as RbacService,\n actorUserId: auth.sub,\n tenantId,\n organizationId: auth.orgId ?? null,\n roleTokens: roles,\n })\n}\n\nasync function resolveTargetTenantIdForRoleGrant(\n em: EntityManager,\n payload: Record<string, unknown>,\n fallbackTenantId: string | null,\n): Promise<string | null> {\n const organizationId = typeof payload.organizationId === 'string' ? payload.organizationId : null\n if (organizationId) {\n const organization = await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )\n return organization?.tenant?.id ? String(organization.tenant.id) : fallbackTenantId\n }\n\n const userId = typeof payload.id === 'string' ? payload.id : null\n if (userId) {\n const user = await findOneWithDecryption(\n em,\n User,\n { id: userId, deletedAt: null },\n {},\n { tenantId: null, organizationId: null },\n )\n return user?.tenantId ? String(user.tenantId) : fallbackTenantId\n }\n\n return fallbackTenantId\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Authentication & Accounts',\n summary: 'User management',\n methods: {\n GET: {\n summary: 'List users',\n description:\n 'Returns users for the effective selected tenant and organization scope. Search matches email, organization name, and role name. Super administrators may scope the response via the topbar context, organization filters, or role filters.',\n query: querySchema,\n responses: [\n { status: 200, description: 'User collection', schema: userListResponseSchema },\n ],\n },\n POST: {\n summary: 'Create user',\n description: 'Creates a new confirmed user within the specified organization, optional display name, and optional roles.',\n requestBody: {\n contentType: 'application/json',\n schema: userCreateSchema,\n },\n responses: [\n {\n status: 201,\n description: 'User created',\n schema: z.object({ id: z.string().uuid() }),\n },\n ],\n errors: [\n { status: 400, description: 'Invalid payload or duplicate email', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n ],\n },\n PUT: {\n summary: 'Update user',\n description: 'Updates profile fields including display name, organization assignment, credentials, or role memberships.',\n requestBody: {\n contentType: 'application/json',\n schema: userUpdateSchema,\n },\n responses: [\n { status: 200, description: 'User updated', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid payload', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 403, description: 'Attempted to assign privileged roles', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n DELETE: {\n summary: 'Delete user',\n description: 'Deletes a user by identifier. Undo support is provided via the command bus.',\n query: z.object({ id: z.string().uuid().describe('User identifier') }),\n responses: [\n { status: 200, description: 'User deleted', schema: okResponseSchema },\n ],\n errors: [\n { status: 400, description: 'User cannot be deleted', schema: errorResponseSchema },\n { status: 401, description: 'Unauthorized', schema: errorResponseSchema },\n { status: 404, description: 'User not found', schema: errorResponseSchema },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["id"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
5
5
|
import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
|
|
@@ -10,6 +10,7 @@ import { AclEditor } from "@open-mercato/core/modules/auth/components/AclEditor"
|
|
|
10
10
|
import { WidgetVisibilityEditor } from "@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor";
|
|
11
11
|
import { E } from "../../../../../../generated/entities.ids.generated.js";
|
|
12
12
|
import { TenantSelect } from "@open-mercato/core/modules/directory/components/TenantSelect";
|
|
13
|
+
import { Alert } from "@open-mercato/ui/primitives/alert";
|
|
13
14
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
14
15
|
import { extractCustomFieldEntries } from "@open-mercato/shared/lib/crud/custom-fields-client";
|
|
15
16
|
function EditRolePage({ params }) {
|
|
@@ -72,6 +73,7 @@ function EditRolePage({ params }) {
|
|
|
72
73
|
label: t("auth.roles.form.field.tenant", "Tenant"),
|
|
73
74
|
type: "custom",
|
|
74
75
|
required: true,
|
|
76
|
+
disabled,
|
|
75
77
|
component: ({ value, setValue }) => {
|
|
76
78
|
const normalizedValue = typeof value === "string" ? value : typeof selectedTenantId === "string" ? selectedTenantId : null;
|
|
77
79
|
return /* @__PURE__ */ jsx(
|
|
@@ -82,10 +84,13 @@ function EditRolePage({ params }) {
|
|
|
82
84
|
onChange: (next) => {
|
|
83
85
|
const resolved = next ?? null;
|
|
84
86
|
setValue(resolved);
|
|
87
|
+
if (selectedTenantId !== resolved) {
|
|
88
|
+
setAclData((prev) => ({ ...prev, organizations: null }));
|
|
89
|
+
}
|
|
85
90
|
setSelectedTenantId(resolved);
|
|
86
|
-
setAclData({ isSuperAdmin: false, features: [], organizations: null });
|
|
87
91
|
},
|
|
88
92
|
includeEmptyOption: true,
|
|
93
|
+
disabled,
|
|
89
94
|
className: "w-full h-9 rounded border px-2 text-sm",
|
|
90
95
|
tenants: preloadedTenants
|
|
91
96
|
}
|
|
@@ -107,18 +112,30 @@ function EditRolePage({ params }) {
|
|
|
107
112
|
id: "acl",
|
|
108
113
|
title: t("auth.roles.form.group.access", "Access"),
|
|
109
114
|
column: 1,
|
|
110
|
-
component: () =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
component: () => {
|
|
116
|
+
if (!id) return null;
|
|
117
|
+
const initialTenantId = initial?.tenantId ?? null;
|
|
118
|
+
const tenantReassigned = actorIsSuperAdmin && initial != null && (selectedTenantId ?? null) !== (initialTenantId ?? null);
|
|
119
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
120
|
+
tenantReassigned ? /* @__PURE__ */ jsx(Alert, { status: "warning", style: "lighter", children: t(
|
|
121
|
+
"auth.roles.form.tenantReassignWarning",
|
|
122
|
+
"This role is being reassigned to a different tenant. The permissions selected here will be saved under the new tenant when you submit."
|
|
123
|
+
) }) : null,
|
|
124
|
+
/* @__PURE__ */ jsx(
|
|
125
|
+
AclEditor,
|
|
126
|
+
{
|
|
127
|
+
kind: "role",
|
|
128
|
+
targetId: String(id),
|
|
129
|
+
canEditOrganizations: true,
|
|
130
|
+
value: aclData,
|
|
131
|
+
onChange: setAclData,
|
|
132
|
+
currentUserIsSuperAdmin: actorIsSuperAdmin,
|
|
133
|
+
tenantId: selectedTenantId ?? null,
|
|
134
|
+
preserveOnTenantChange: true
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
] });
|
|
138
|
+
}
|
|
122
139
|
},
|
|
123
140
|
{
|
|
124
141
|
id: "dashboardWidgets",
|
|
@@ -130,6 +147,7 @@ function EditRolePage({ params }) {
|
|
|
130
147
|
kind: "role",
|
|
131
148
|
targetId: String(id),
|
|
132
149
|
tenantId: selectedTenantId ?? (initial?.tenantId ?? null),
|
|
150
|
+
preserveOnTenantChange: true,
|
|
133
151
|
ref: widgetEditorRef
|
|
134
152
|
}
|
|
135
153
|
) : null
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/auth/backend/roles/%5Bid%5D/edit/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { AclEditor, type AclData } from '@open-mercato/core/modules/auth/components/AclEditor'\nimport { WidgetVisibilityEditor, type WidgetVisibilityEditorHandle } from '@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor'\nimport { E } from '#generated/entities.ids.generated'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\n\ntype EditRoleFormValues = {\n name?: string\n tenantId?: string | null\n} & Record<string, unknown>\n\ntype RoleRecord = {\n id: string\n name: string\n tenantId: string | null\n tenantName?: string | null\n usersCount?: number | null\n} & Record<string, unknown>\n\ntype RoleListResponse = {\n items?: RoleRecord[]\n isSuperAdmin?: boolean\n}\n\nexport default function EditRolePage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const [initial, setInitial] = React.useState<RoleRecord | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const widgetEditorRef = React.useRef<WidgetVisibilityEditorHandle | null>(null)\n\n React.useEffect(() => {\n if (!id) return\n const roleId = id\n let cancelled = false\n async function load() {\n try {\n const { ok, result } = await apiCall<RoleListResponse>(`/api/auth/roles?id=${encodeURIComponent(roleId)}`)\n if (!ok) throw new Error(t('auth.roles.form.errors.load', 'Failed to load role'))\n const foundList = Array.isArray(result?.items) ? result?.items : []\n const found = (foundList?.[0] ?? null) as RoleRecord | null\n if (!cancelled) {\n setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n setInitial(found ? { ...found, ...extractCustomFieldEntries(found) } : null)\n const tenant = found && typeof found.tenantId === 'string' ? found.tenantId : null\n setSelectedTenantId(tenant)\n }\n } catch {\n if (!cancelled) {\n setInitial(null)\n setSelectedTenantId(null)\n }\n }\n if (!cancelled) setLoading(false)\n }\n load()\n return () => { cancelled = true }\n }, [id, t])\n\n const preloadedTenants = React.useMemo(() => {\n if (!selectedTenantId) return null\n const name = initial?.tenantId === selectedTenantId\n ? (initial?.tenantName ?? selectedTenantId)\n : selectedTenantId\n return [{ id: selectedTenantId, name, isActive: true }]\n }, [initial, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => {\n const disabled = !!(initial && typeof initial.usersCount === 'number' && initial.usersCount > 0)\n const list: CrudField[] = [\n {\n id: 'name',\n label: t('auth.roles.form.field.name', 'Name'),\n type: 'text',\n required: true,\n disabled,\n },\n ]\n if (actorIsSuperAdmin) {\n list.push({\n id: 'tenantId',\n label: t('auth.roles.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => {\n const normalizedValue = typeof value === 'string'\n ? value\n : (typeof selectedTenantId === 'string' ? selectedTenantId : null)\n return (\n <TenantSelect\n id=\"tenantId\"\n value={normalizedValue}\n onChange={(next) => {\n const resolved = next ?? null\n setValue(resolved)\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { deleteCrud, updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { AclEditor, type AclData } from '@open-mercato/core/modules/auth/components/AclEditor'\nimport { WidgetVisibilityEditor, type WidgetVisibilityEditorHandle } from '@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor'\nimport { E } from '#generated/entities.ids.generated'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { Alert } from '@open-mercato/ui/primitives/alert'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'\n\ntype EditRoleFormValues = {\n name?: string\n tenantId?: string | null\n} & Record<string, unknown>\n\ntype RoleRecord = {\n id: string\n name: string\n tenantId: string | null\n tenantName?: string | null\n usersCount?: number | null\n} & Record<string, unknown>\n\ntype RoleListResponse = {\n items?: RoleRecord[]\n isSuperAdmin?: boolean\n}\n\nexport default function EditRolePage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const [initial, setInitial] = React.useState<RoleRecord | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const widgetEditorRef = React.useRef<WidgetVisibilityEditorHandle | null>(null)\n\n React.useEffect(() => {\n if (!id) return\n const roleId = id\n let cancelled = false\n async function load() {\n try {\n const { ok, result } = await apiCall<RoleListResponse>(`/api/auth/roles?id=${encodeURIComponent(roleId)}`)\n if (!ok) throw new Error(t('auth.roles.form.errors.load', 'Failed to load role'))\n const foundList = Array.isArray(result?.items) ? result?.items : []\n const found = (foundList?.[0] ?? null) as RoleRecord | null\n if (!cancelled) {\n setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n setInitial(found ? { ...found, ...extractCustomFieldEntries(found) } : null)\n const tenant = found && typeof found.tenantId === 'string' ? found.tenantId : null\n setSelectedTenantId(tenant)\n }\n } catch {\n if (!cancelled) {\n setInitial(null)\n setSelectedTenantId(null)\n }\n }\n if (!cancelled) setLoading(false)\n }\n load()\n return () => { cancelled = true }\n }, [id, t])\n\n const preloadedTenants = React.useMemo(() => {\n if (!selectedTenantId) return null\n const name = initial?.tenantId === selectedTenantId\n ? (initial?.tenantName ?? selectedTenantId)\n : selectedTenantId\n return [{ id: selectedTenantId, name, isActive: true }]\n }, [initial, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => {\n const disabled = !!(initial && typeof initial.usersCount === 'number' && initial.usersCount > 0)\n const list: CrudField[] = [\n {\n id: 'name',\n label: t('auth.roles.form.field.name', 'Name'),\n type: 'text',\n required: true,\n disabled,\n },\n ]\n if (actorIsSuperAdmin) {\n list.push({\n id: 'tenantId',\n label: t('auth.roles.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n disabled,\n component: ({ value, setValue }) => {\n const normalizedValue = typeof value === 'string'\n ? value\n : (typeof selectedTenantId === 'string' ? selectedTenantId : null)\n return (\n <TenantSelect\n id=\"tenantId\"\n value={normalizedValue}\n onChange={(next) => {\n const resolved = next ?? null\n setValue(resolved)\n if (selectedTenantId !== resolved) {\n setAclData((prev) => ({ ...prev, organizations: null }))\n }\n setSelectedTenantId(resolved)\n }}\n includeEmptyOption\n disabled={disabled}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n tenants={preloadedTenants}\n />\n )\n },\n })\n }\n return list\n }, [actorIsSuperAdmin, initial, preloadedTenants, selectedTenantId, t])\n\n const detailFieldIds = React.useMemo(() => {\n const base = ['name']\n if (actorIsSuperAdmin) base.push('tenantId')\n return base\n }, [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => ([\n { id: 'details', title: t('auth.roles.form.group.details', 'Details'), column: 1, fields: detailFieldIds },\n { id: 'customFields', title: t('entities.customFields.title', 'Custom Fields'), column: 2, kind: 'customFields' },\n {\n id: 'acl',\n title: t('auth.roles.form.group.access', 'Access'),\n column: 1,\n component: () => {\n if (!id) return null\n const initialTenantId = initial?.tenantId ?? null\n const tenantReassigned = actorIsSuperAdmin\n && initial != null\n && (selectedTenantId ?? null) !== (initialTenantId ?? null)\n return (\n <div className=\"space-y-3\">\n {tenantReassigned ? (\n <Alert status=\"warning\" style=\"lighter\">\n {t(\n 'auth.roles.form.tenantReassignWarning',\n 'This role is being reassigned to a different tenant. The permissions selected here will be saved under the new tenant when you submit.',\n )}\n </Alert>\n ) : null}\n <AclEditor\n kind=\"role\"\n targetId={String(id)}\n canEditOrganizations\n value={aclData}\n onChange={setAclData}\n currentUserIsSuperAdmin={actorIsSuperAdmin}\n tenantId={selectedTenantId ?? null}\n preserveOnTenantChange\n />\n </div>\n )\n },\n },\n {\n id: 'dashboardWidgets',\n title: t('auth.roles.form.group.widgets', 'Dashboard Widgets'),\n column: 2,\n component: () => (id && !loading\n ? (\n <WidgetVisibilityEditor\n kind=\"role\"\n targetId={String(id)}\n tenantId={selectedTenantId ?? (initial?.tenantId ?? null)}\n preserveOnTenantChange\n ref={widgetEditorRef}\n />\n )\n : null),\n },\n ]), [aclData, actorIsSuperAdmin, detailFieldIds, id, initial, loading, selectedTenantId, t])\n\n if (!id) return null\n return (\n <Page>\n <PageBody>\n <CrudForm<EditRoleFormValues>\n title={t('auth.roles.form.title.edit', 'Edit Role')}\n backHref=\"/backend/roles\"\n versionHistory={{ resourceKind: 'auth.role', resourceId: id ? String(id) : '' }}\n entityId={E.auth.role}\n fields={fields}\n groups={groups}\n initialValues={initial || { id, tenantId: null }}\n isLoading={loading}\n loadingMessage={t('auth.roles.form.loading', 'Loading data...')}\n cancelHref=\"/backend/roles\"\n successRedirect={`/backend/roles?flash=${encodeURIComponent(t('auth.roles.flash.updated', 'Role saved'))}&type=success`}\n onSubmit={async (values) => {\n const customFields = collectCustomFieldValues(values)\n const payload: Record<string, unknown> = { id }\n if (values.name !== undefined) payload.name = values.name\n let effectiveTenantId: string | null = selectedTenantId ?? (initial?.tenantId ?? null)\n if (actorIsSuperAdmin) {\n const rawTenant = typeof values.tenantId === 'string' ? values.tenantId.trim() : selectedTenantId\n effectiveTenantId = rawTenant && rawTenant.length ? rawTenant : null\n payload.tenantId = effectiveTenantId\n }\n if (Object.keys(customFields).length) {\n payload.customFields = customFields\n }\n await updateCrud('auth/roles', payload)\n await updateCrud('auth/roles/acl', { roleId: id, tenantId: effectiveTenantId, ...aclData }, {\n errorMessage: t('auth.roles.form.errors.aclUpdate', 'Failed to update role access control'),\n })\n await widgetEditorRef.current?.save()\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }}\n onDelete={async () => {\n await deleteCrud('auth/roles', String(id), {\n errorMessage: t('auth.roles.form.errors.delete', 'Failed to delete role'),\n })\n }}\n deleteRedirect={`/backend/roles?flash=${encodeURIComponent(t('auth.roles.flash.deleted', 'Role deleted'))}&type=success`}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -183,7 +183,7 @@ function EditUserPage({ params }) {
|
|
|
183
183
|
if (!actorResolved) return [];
|
|
184
184
|
if (actorIsSuperAdmin) {
|
|
185
185
|
if (!selectedTenantId) return [];
|
|
186
|
-
return fetchRoleOptions(query, { tenantId: selectedTenantId });
|
|
186
|
+
return fetchRoleOptions(query, { tenantId: selectedTenantId, includeSuperAdmin: true });
|
|
187
187
|
}
|
|
188
188
|
return fetchRoleOptions(query);
|
|
189
189
|
}, [actorIsSuperAdmin, actorResolved, selectedTenantId]);
|