@open-mercato/core 0.4.2-canary-49d47ff90e → 0.4.2-canary-02d8ce2991
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/modules/auth/backend/auth/profile/page.js.map +1 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +4 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/cli.js +13 -12
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/business_rules/api/execute/route.js +7 -1
- package/dist/modules/business_rules/api/execute/route.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +33 -3
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/dashboards/cli.js +12 -4
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +16 -11
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +3 -3
- package/dist/modules/dashboards/services/widgetDataService.js +46 -2
- package/dist/modules/dashboards/services/widgetDataService.js.map +2 -2
- package/dist/modules/notifications/data/validators.js +5 -1
- package/dist/modules/notifications/data/validators.js.map +2 -2
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js +2 -1
- package/dist/modules/notifications/frontend/NotificationSettingsPageClient.js.map +2 -2
- package/dist/modules/notifications/lib/deliveryConfig.js +4 -2
- package/dist/modules/notifications/lib/deliveryConfig.js.map +2 -2
- package/dist/modules/notifications/lib/deliveryStrategies.js +14 -0
- package/dist/modules/notifications/lib/deliveryStrategies.js.map +7 -0
- package/dist/modules/notifications/subscribers/deliver-notification.js +33 -7
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +14 -6
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/package.json +2 -2
- package/src/modules/auth/README.md +1 -1
- package/src/modules/auth/__tests__/cli-setup-acl.test.ts +1 -1
- package/src/modules/auth/backend/auth/profile/page.tsx +2 -2
- package/src/modules/auth/backend/roles/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +4 -1
- package/src/modules/auth/cli.ts +25 -12
- package/src/modules/business_rules/api/execute/route.ts +8 -1
- package/src/modules/business_rules/lib/__tests__/rule-engine.test.ts +51 -0
- package/src/modules/business_rules/lib/rule-engine.ts +57 -3
- package/src/modules/dashboards/cli.ts +14 -4
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +22 -11
- package/src/modules/dashboards/services/widgetDataService.ts +52 -2
- package/src/modules/notifications/__tests__/deliver-notification.test.ts +195 -0
- package/src/modules/notifications/__tests__/deliveryStrategies.test.ts +19 -0
- package/src/modules/notifications/__tests__/notificationService.test.ts +208 -0
- package/src/modules/notifications/data/validators.ts +5 -0
- package/src/modules/notifications/frontend/NotificationSettingsPageClient.tsx +2 -0
- package/src/modules/notifications/lib/deliveryConfig.ts +8 -0
- package/src/modules/notifications/lib/deliveryStrategies.ts +50 -0
- package/src/modules/notifications/subscribers/deliver-notification.ts +39 -10
- package/src/modules/workflows/lib/transition-handler.ts +18 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/auth/backend/auth/profile/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { z } from 'zod'\nimport { Save } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildPasswordSchema, formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\ntype ProfileResponse = {\n email?: string | null\n}\n\ntype ProfileUpdateResponse = {\n ok?: boolean\n email?: string | null\n}\n\ntype ProfileFormValues = {\n email: string\n password
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { z } from 'zod'\nimport { Save } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { buildPasswordSchema, formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\ntype ProfileResponse = {\n email?: string | null\n}\n\ntype ProfileUpdateResponse = {\n ok?: boolean\n email?: string | null\n}\n\ntype ProfileFormValues = {\n email: string\n password?: string\n confirmPassword?: string\n}\n\nexport default function AuthProfilePage() {\n const t = useT()\n const router = useRouter()\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [email, setEmail] = React.useState('')\n const [formKey, setFormKey] = React.useState(0)\n const formId = React.useId()\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(\n () => formatPasswordRequirements(passwordPolicy, t),\n [passwordPolicy, t],\n )\n const passwordDescription = React.useMemo(() => (\n passwordRequirements\n ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })\n : undefined\n ), [passwordRequirements, t])\n\n React.useEffect(() => {\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n try {\n const { ok, result } = await apiCall<ProfileResponse>('/api/auth/profile')\n if (!ok) throw new Error('load_failed')\n const resolvedEmail = typeof result?.email === 'string' ? result.email : ''\n if (!cancelled) setEmail(resolvedEmail)\n } catch (err) {\n console.error('Failed to load auth profile', err)\n if (!cancelled) setError(t('auth.profile.form.errors.load', 'Failed to load profile.'))\n } finally {\n if (!cancelled) setLoading(false)\n }\n }\n load()\n return () => { cancelled = true }\n }, [t])\n\n const fields = React.useMemo<CrudField[]>(() => [\n { id: 'email', label: t('auth.profile.form.email', 'Email'), type: 'text', required: true },\n {\n id: 'password',\n label: t('auth.profile.form.password', 'New password'),\n type: 'text',\n description: passwordDescription,\n },\n { id: 'confirmPassword', label: t('auth.profile.form.confirmPassword', 'Confirm new password'), type: 'text' },\n ], [passwordDescription, t])\n\n const schema = React.useMemo(() => {\n const passwordSchema = buildPasswordSchema({\n policy: passwordPolicy,\n message: t('auth.profile.form.errors.passwordRequirements', 'Password must meet the requirements.'),\n })\n const optionalPasswordSchema = z.union([z.literal(''), passwordSchema]).optional()\n return z.object({\n email: z.string().trim().min(1, t('auth.profile.form.errors.emailRequired', 'Email is required.')),\n password: optionalPasswordSchema,\n confirmPassword: z.string().optional(),\n }).superRefine((values, ctx) => {\n const password = values.password?.trim() ?? ''\n const confirmPassword = values.confirmPassword?.trim() ?? ''\n if ((password || confirmPassword) && password !== confirmPassword) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: t('auth.profile.form.errors.passwordMismatch', 'Passwords do not match.'),\n path: ['confirmPassword'],\n })\n }\n })\n }, [passwordPolicy, t])\n\n const handleSubmit = React.useCallback(async (values: ProfileFormValues) => {\n const nextEmail = values.email?.trim() ?? ''\n const password = values.password?.trim() ?? ''\n\n if (!password && nextEmail === email) {\n throw createCrudFormError(t('auth.profile.form.errors.noChanges', 'No changes to save.'))\n }\n\n const payload: { email: string; password?: string } = { email: nextEmail }\n if (password) payload.password = password\n\n const result = await readApiResultOrThrow<ProfileUpdateResponse>(\n '/api/auth/profile',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n { errorMessage: t('auth.profile.form.errors.save', 'Failed to update profile.') },\n )\n\n const resolvedEmail = typeof result?.email === 'string' ? result.email : nextEmail\n setEmail(resolvedEmail)\n setFormKey((prev) => prev + 1)\n flash(t('auth.profile.form.success', 'Profile updated.'), 'success')\n router.refresh()\n }, [email, router, t])\n\n return (\n <Page>\n <PageBody>\n {loading ? (\n <LoadingMessage label={t('auth.profile.form.loading', 'Loading profile...')} />\n ) : error ? (\n <ErrorMessage label={error} />\n ) : (\n <section className=\"space-y-6 rounded-lg border bg-background p-6\">\n <header className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-1\">\n <h2 className=\"text-lg font-semibold\">{t('auth.profile.title', 'Profile')}</h2>\n <p className=\"text-sm text-muted-foreground\">\n {t('auth.profile.subtitle', 'Change password')}\n </p>\n </div>\n <Button type=\"submit\" form={formId}>\n <Save className=\"size-4 mr-2\" />\n {t('auth.profile.form.save', 'Save changes')}\n </Button>\n </header>\n <CrudForm<ProfileFormValues>\n key={formKey}\n formId={formId}\n schema={schema}\n fields={fields}\n initialValues={{\n email,\n password: '',\n confirmPassword: '',\n }}\n submitLabel={t('auth.profile.form.save', 'Save changes')}\n onSubmit={handleSubmit}\n embedded\n hideFooterActions\n />\n </section>\n )}\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -19,6 +19,7 @@ function EditRolePage({ params }) {
|
|
|
19
19
|
const [aclData, setAclData] = React.useState({ isSuperAdmin: false, features: [], organizations: null });
|
|
20
20
|
const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false);
|
|
21
21
|
const [selectedTenantId, setSelectedTenantId] = React.useState(null);
|
|
22
|
+
const widgetEditorRef = React.useRef(null);
|
|
22
23
|
React.useEffect(() => {
|
|
23
24
|
if (!id) return;
|
|
24
25
|
const roleId = id;
|
|
@@ -127,7 +128,8 @@ function EditRolePage({ params }) {
|
|
|
127
128
|
{
|
|
128
129
|
kind: "role",
|
|
129
130
|
targetId: String(id),
|
|
130
|
-
tenantId: selectedTenantId ?? (initial?.tenantId ?? null)
|
|
131
|
+
tenantId: selectedTenantId ?? (initial?.tenantId ?? null),
|
|
132
|
+
ref: widgetEditorRef
|
|
131
133
|
}
|
|
132
134
|
) : null
|
|
133
135
|
}
|
|
@@ -163,6 +165,7 @@ function EditRolePage({ params }) {
|
|
|
163
165
|
await updateCrud("auth/roles/acl", { roleId: id, tenantId: effectiveTenantId, ...aclData }, {
|
|
164
166
|
errorMessage: t("auth.roles.form.errors.aclUpdate", "Failed to update role access control")
|
|
165
167
|
});
|
|
168
|
+
await widgetEditorRef.current?.save();
|
|
166
169
|
try {
|
|
167
170
|
window.dispatchEvent(new Event("om:refresh-sidebar"));
|
|
168
171
|
} catch {
|
|
@@ -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 } 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'\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\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 || 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 setSelectedTenantId(resolved)\n setAclData({ isSuperAdmin: false, features: [], organizations: null })\n }}\n includeEmptyOption\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: () => (id\n ? (\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 />\n )\n : null),\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 />\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 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 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": ";
|
|
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'\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 || 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 setSelectedTenantId(resolved)\n setAclData({ isSuperAdmin: false, features: [], organizations: null })\n }}\n includeEmptyOption\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: () => (id\n ? (\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 />\n )\n : null),\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 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 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
|
}
|
|
@@ -62,6 +62,7 @@ function EditUserPage({ params }) {
|
|
|
62
62
|
const [aclData, setAclData] = React.useState({ isSuperAdmin: false, features: [], organizations: null });
|
|
63
63
|
const [customFieldValues, setCustomFieldValues] = React.useState({});
|
|
64
64
|
const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false);
|
|
65
|
+
const widgetEditorRef = React.useRef(null);
|
|
65
66
|
const passwordPolicy = React.useMemo(() => getPasswordPolicy(), []);
|
|
66
67
|
const passwordRequirements = React.useMemo(
|
|
67
68
|
() => formatPasswordRequirements(passwordPolicy, t),
|
|
@@ -245,7 +246,8 @@ function EditUserPage({ params }) {
|
|
|
245
246
|
kind: "user",
|
|
246
247
|
targetId: String(id),
|
|
247
248
|
tenantId: selectedTenantId ?? null,
|
|
248
|
-
organizationId: initialUser?.organizationId ?? null
|
|
249
|
+
organizationId: initialUser?.organizationId ?? null,
|
|
250
|
+
ref: widgetEditorRef
|
|
249
251
|
}
|
|
250
252
|
) : null
|
|
251
253
|
}
|
|
@@ -301,6 +303,7 @@ function EditUserPage({ params }) {
|
|
|
301
303
|
await updateCrud("auth/users/acl", { userId: id, ...aclData }, {
|
|
302
304
|
errorMessage: t("auth.users.form.errors.aclUpdate", "Failed to update user access control")
|
|
303
305
|
});
|
|
306
|
+
await widgetEditorRef.current?.save();
|
|
304
307
|
try {
|
|
305
308
|
window.dispatchEvent(new Event("om:refresh-sidebar"));
|
|
306
309
|
} catch {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/auth/backend/users/%5Bid%5D/edit/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { E } from '#generated/entities.ids.generated'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup, type CrudFieldOption } 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 { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'\nimport { WidgetVisibilityEditor } from '@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\ntype EditUserFormValues = {\n email: string\n password: string\n tenantId: string | null\n organizationId: string | null\n roles: string[]\n} & Record<string, unknown>\n\ntype LoadedUser = {\n id: string\n email: string\n organizationId: string | null\n tenantId: string | null\n tenantName: string | null\n organizationName: string | null\n roles: string[]\n}\n\ntype UserApiItem = {\n id?: string | null\n email?: string | null\n organizationId?: string | null\n tenantId?: string | null\n tenantName?: string | null\n organizationName?: string | null\n roles?: unknown\n}\n\ntype UserListResponse = {\n items?: UserApiItem[]\n isSuperAdmin?: boolean\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n}\n\ntype TenantAwareOrganizationSelectProps = {\n fieldId: string\n value: string | null\n setValue: (value: string | null) => void\n tenantId: string | null\n includeInactiveIds?: Iterable<string | null | undefined>\n}\n\nfunction TenantAwareOrganizationSelectInput({\n fieldId,\n value,\n setValue,\n tenantId,\n includeInactiveIds,\n}: TenantAwareOrganizationSelectProps) {\n const prevTenantRef = React.useRef<string | null>(tenantId)\n const hydratedRef = React.useRef(false)\n const handleChange = React.useCallback((next: string | null) => {\n setValue(next ?? null)\n }, [setValue])\n\n React.useEffect(() => {\n if (!hydratedRef.current) {\n hydratedRef.current = true\n prevTenantRef.current = tenantId\n return\n }\n if (prevTenantRef.current !== tenantId) {\n prevTenantRef.current = tenantId\n setValue(null)\n }\n }, [tenantId, setValue])\n\n return (\n <OrganizationSelect\n id={fieldId}\n value={value}\n onChange={handleChange}\n required\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n includeInactiveIds={includeInactiveIds}\n tenantId={tenantId}\n />\n )\n}\n\nexport default function EditUserPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const [initialUser, setInitialUser] = React.useState<LoadedUser | null>(null)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [canEditOrgs, setCanEditOrgs] = React.useState(false)\n const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })\n const [customFieldValues, setCustomFieldValues] = React.useState<Record<string, unknown>>({})\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(\n () => formatPasswordRequirements(passwordPolicy, t),\n [passwordPolicy, t],\n )\n const passwordDescription = React.useMemo(() => (\n passwordRequirements\n ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })\n : undefined\n ), [passwordRequirements, t])\n\n React.useEffect(() => {\n if (!id) {\n setLoading(false)\n setError(t('auth.users.form.errors.noId', 'No user ID provided'))\n return\n }\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n setCustomFieldValues({})\n try {\n const { ok, result } = await apiCall<UserListResponse>(\n `/api/auth/users?id=${encodeURIComponent(String(id))}&page=1&pageSize=1`,\n )\n if (!ok) throw new Error('load_failed')\n const item = Array.isArray(result?.items) ? result?.items?.[0] : undefined\n if (!cancelled) {\n setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n if (!item) {\n setError(t('auth.users.form.errors.notFound', 'User not found'))\n setCustomFieldValues({})\n setInitialUser(null)\n setSelectedTenantId(null)\n } else {\n setInitialUser({\n id: item.id ? String(item.id) : String(id),\n email: item.email ? String(item.email) : '',\n organizationId: item.organizationId ? String(item.organizationId) : null,\n tenantId: item.tenantId ? String(item.tenantId) : null,\n tenantName: item.tenantName ? String(item.tenantName) : null,\n organizationName: item.organizationName ? String(item.organizationName) : null,\n roles: Array.isArray(item.roles)\n ? item.roles\n .map((role) => (typeof role === 'string' ? role : role == null ? '' : String(role)))\n .filter((role) => role.trim().length > 0)\n : [],\n })\n setSelectedTenantId(item.tenantId ? String(item.tenantId) : null)\n const custom: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) custom[key] = value as unknown\n else if (key.startsWith('cf:')) custom[`cf_${key.slice(3)}`] = value as unknown\n }\n setCustomFieldValues(custom)\n }\n }\n } catch (err) {\n console.error('Failed to load user:', err)\n if (!cancelled) setError(t('auth.users.form.errors.load', 'Failed to load user data'))\n if (!cancelled) setCustomFieldValues({})\n }\n try {\n const featureCheck = await apiCall<FeatureCheckResponse>(\n '/api/auth/feature-check',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.organizations.view'] }),\n },\n { fallback: { ok: false } },\n )\n if (!cancelled) setCanEditOrgs(Boolean(featureCheck.result?.ok))\n } catch (err) {\n console.error('Failed to check features:', err)\n }\n if (!cancelled) setLoading(false)\n }\n load()\n return () => { cancelled = true }\n }, [id, t])\n\n const selectedOrgId = initialUser?.organizationId ? String(initialUser.organizationId) : null\n const preloadedTenants = React.useMemo(() => {\n if (!selectedTenantId) return null\n const name = initialUser?.tenantId === selectedTenantId\n ? (initialUser?.tenantName ?? selectedTenantId)\n : selectedTenantId\n return [{ id: selectedTenantId, name, isActive: true }]\n }, [initialUser, selectedTenantId])\n\n const loadRoleOptions = React.useCallback(async (query?: string): Promise<CrudFieldOption[]> => {\n if (actorIsSuperAdmin) {\n if (!selectedTenantId) return []\n return fetchRoleOptions(query, { tenantId: selectedTenantId })\n }\n return fetchRoleOptions(query)\n }, [actorIsSuperAdmin, selectedTenantId])\n\n const fields: CrudField[] = React.useMemo(() => {\n const items: CrudField[] = [\n { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },\n {\n id: 'password',\n label: t('auth.users.form.field.password', 'Password'),\n type: 'text',\n description: passwordDescription,\n },\n ]\n if (actorIsSuperAdmin) {\n items.push({\n id: 'tenantId',\n label: t('auth.users.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 setSelectedTenantId(resolved)\n setAclData({ isSuperAdmin: false, features: [], organizations: null })\n }}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n required\n tenants={preloadedTenants}\n />\n )\n },\n })\n }\n items.push({\n id: 'organizationId',\n label: t('auth.users.form.field.organization', 'Organization'),\n type: 'custom',\n component: ({ id, value, setValue }) => {\n const normalizedValue = typeof value === 'string' ? (value.length > 0 ? value : null) : null\n return (\n <TenantAwareOrganizationSelectInput\n fieldId={id}\n value={normalizedValue}\n setValue={(next) => setValue(next ?? null)}\n tenantId={selectedTenantId}\n includeInactiveIds={selectedOrgId ? [selectedOrgId] : undefined}\n />\n )\n },\n })\n items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })\n return items\n }, [actorIsSuperAdmin, loadRoleOptions, passwordDescription, preloadedTenants, selectedOrgId, selectedTenantId, t])\n\n const detailFieldIds = React.useMemo(() => {\n const base: string[] = ['email', 'password', 'organizationId', 'roles']\n if (actorIsSuperAdmin) base.splice(2, 0, 'tenantId')\n return base\n }, [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => [\n { id: 'details', title: t('auth.users.form.group.details', 'Details'), column: 1, fields: detailFieldIds },\n { id: 'custom', title: t('auth.users.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n {\n id: 'acl',\n title: t('auth.users.form.group.access', 'Access'),\n column: 1,\n component: () => (id\n ? (\n <AclEditor\n kind=\"user\"\n targetId={String(id)}\n canEditOrganizations={canEditOrgs}\n value={aclData}\n onChange={setAclData}\n userRoles={initialUser?.roles || []}\n currentUserIsSuperAdmin={actorIsSuperAdmin}\n tenantId={selectedTenantId ?? null}\n />\n )\n : null),\n },\n {\n id: 'dashboardWidgets',\n title: t('auth.users.form.group.widgets', 'Dashboard Widgets'),\n column: 2,\n component: () => (id && initialUser\n ? (\n <WidgetVisibilityEditor\n kind=\"user\"\n targetId={String(id)}\n tenantId={selectedTenantId ?? null}\n organizationId={initialUser?.organizationId ?? null}\n />\n ) : null\n ),\n },\n ], [aclData, actorIsSuperAdmin, canEditOrgs, detailFieldIds, id, initialUser, selectedTenantId, t])\n\n const initialValues = React.useMemo(() => {\n if (initialUser) {\n return {\n email: initialUser.email,\n password: '',\n tenantId: initialUser.tenantId,\n organizationId: initialUser.organizationId,\n roles: initialUser.roles,\n ...customFieldValues,\n }\n }\n return {\n email: '',\n password: '',\n tenantId: selectedTenantId ?? null,\n organizationId: null,\n roles: [],\n ...customFieldValues,\n }\n }, [initialUser, customFieldValues, selectedTenantId])\n\n return (\n <Page>\n <PageBody>\n {error && (\n <div className=\"p-4 mb-4 bg-red-50 border border-red-200 rounded text-red-800\">\n {error}\n </div>\n )}\n <CrudForm<EditUserFormValues>\n title={t('auth.users.form.title.edit', 'Edit User')}\n backHref=\"/backend/users\"\n fields={fields}\n groups={groups}\n entityId={E.auth.user}\n initialValues={initialValues}\n isLoading={loading}\n loadingMessage={t('auth.users.form.loading', 'Loading user data...')}\n submitLabel={t('auth.users.form.action.save', 'Save')}\n cancelHref=\"/backend/users\"\n successRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.updated', 'User saved'))}&type=success`}\n onSubmit={async (values) => {\n if (!id) return\n const customFields = collectCustomFieldValues(values)\n const payload = {\n id: id ? String(id) : '',\n email: values.email,\n password: values.password && values.password.trim() ? values.password : undefined,\n organizationId: values.organizationId ? values.organizationId : undefined,\n roles: Array.isArray(values.roles) ? values.roles : [],\n ...(Object.keys(customFields).length ? { customFields } : {}),\n }\n await updateCrud('auth/users', payload)\n await updateCrud('auth/users/acl', { userId: id, ...aclData }, {\n errorMessage: t('auth.users.form.errors.aclUpdate', 'Failed to update user access control'),\n })\n try { window.dispatchEvent(new Event('om:refresh-sidebar')) } catch {}\n }}\n onDelete={async () => {\n await deleteCrud('auth/users', String(id), {\n errorMessage: t('auth.users.form.errors.delete', 'Failed to delete user'),\n })\n }}\n deleteRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.deleted', 'User deleted'))}&type=success`}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AAuFI,
|
|
4
|
+
"sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { E } from '#generated/entities.ids.generated'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup, type CrudFieldOption } 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 { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { fetchRoleOptions } from '@open-mercato/core/modules/auth/backend/users/roleOptions'\nimport { WidgetVisibilityEditor, type WidgetVisibilityEditorHandle } from '@open-mercato/core/modules/dashboards/components/WidgetVisibilityEditor'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { formatPasswordRequirements, getPasswordPolicy } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\ntype EditUserFormValues = {\n email: string\n password: string\n tenantId: string | null\n organizationId: string | null\n roles: string[]\n} & Record<string, unknown>\n\ntype LoadedUser = {\n id: string\n email: string\n organizationId: string | null\n tenantId: string | null\n tenantName: string | null\n organizationName: string | null\n roles: string[]\n}\n\ntype UserApiItem = {\n id?: string | null\n email?: string | null\n organizationId?: string | null\n tenantId?: string | null\n tenantName?: string | null\n organizationName?: string | null\n roles?: unknown\n}\n\ntype UserListResponse = {\n items?: UserApiItem[]\n isSuperAdmin?: boolean\n}\n\ntype FeatureCheckResponse = {\n ok?: boolean\n}\n\ntype TenantAwareOrganizationSelectProps = {\n fieldId: string\n value: string | null\n setValue: (value: string | null) => void\n tenantId: string | null\n includeInactiveIds?: Iterable<string | null | undefined>\n}\n\nfunction TenantAwareOrganizationSelectInput({\n fieldId,\n value,\n setValue,\n tenantId,\n includeInactiveIds,\n}: TenantAwareOrganizationSelectProps) {\n const prevTenantRef = React.useRef<string | null>(tenantId)\n const hydratedRef = React.useRef(false)\n const handleChange = React.useCallback((next: string | null) => {\n setValue(next ?? null)\n }, [setValue])\n\n React.useEffect(() => {\n if (!hydratedRef.current) {\n hydratedRef.current = true\n prevTenantRef.current = tenantId\n return\n }\n if (prevTenantRef.current !== tenantId) {\n prevTenantRef.current = tenantId\n setValue(null)\n }\n }, [tenantId, setValue])\n\n return (\n <OrganizationSelect\n id={fieldId}\n value={value}\n onChange={handleChange}\n required\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n includeInactiveIds={includeInactiveIds}\n tenantId={tenantId}\n />\n )\n}\n\nexport default function EditUserPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const [initialUser, setInitialUser] = React.useState<LoadedUser | null>(null)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const [loading, setLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [canEditOrgs, setCanEditOrgs] = React.useState(false)\n const [aclData, setAclData] = React.useState<AclData>({ isSuperAdmin: false, features: [], organizations: null })\n const [customFieldValues, setCustomFieldValues] = React.useState<Record<string, unknown>>({})\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const widgetEditorRef = React.useRef<WidgetVisibilityEditorHandle | null>(null)\n const passwordPolicy = React.useMemo(() => getPasswordPolicy(), [])\n const passwordRequirements = React.useMemo(\n () => formatPasswordRequirements(passwordPolicy, t),\n [passwordPolicy, t],\n )\n const passwordDescription = React.useMemo(() => (\n passwordRequirements\n ? t('auth.password.requirements.help', 'Password requirements: {requirements}', { requirements: passwordRequirements })\n : undefined\n ), [passwordRequirements, t])\n\n React.useEffect(() => {\n if (!id) {\n setLoading(false)\n setError(t('auth.users.form.errors.noId', 'No user ID provided'))\n return\n }\n let cancelled = false\n async function load() {\n setLoading(true)\n setError(null)\n setCustomFieldValues({})\n try {\n const { ok, result } = await apiCall<UserListResponse>(\n `/api/auth/users?id=${encodeURIComponent(String(id))}&page=1&pageSize=1`,\n )\n if (!ok) throw new Error('load_failed')\n const item = Array.isArray(result?.items) ? result?.items?.[0] : undefined\n if (!cancelled) {\n setActorIsSuperAdmin(Boolean(result?.isSuperAdmin))\n if (!item) {\n setError(t('auth.users.form.errors.notFound', 'User not found'))\n setCustomFieldValues({})\n setInitialUser(null)\n setSelectedTenantId(null)\n } else {\n setInitialUser({\n id: item.id ? String(item.id) : String(id),\n email: item.email ? String(item.email) : '',\n organizationId: item.organizationId ? String(item.organizationId) : null,\n tenantId: item.tenantId ? String(item.tenantId) : null,\n tenantName: item.tenantName ? String(item.tenantName) : null,\n organizationName: item.organizationName ? String(item.organizationName) : null,\n roles: Array.isArray(item.roles)\n ? item.roles\n .map((role) => (typeof role === 'string' ? role : role == null ? '' : String(role)))\n .filter((role) => role.trim().length > 0)\n : [],\n })\n setSelectedTenantId(item.tenantId ? String(item.tenantId) : null)\n const custom: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(item)) {\n if (key.startsWith('cf_')) custom[key] = value as unknown\n else if (key.startsWith('cf:')) custom[`cf_${key.slice(3)}`] = value as unknown\n }\n setCustomFieldValues(custom)\n }\n }\n } catch (err) {\n console.error('Failed to load user:', err)\n if (!cancelled) setError(t('auth.users.form.errors.load', 'Failed to load user data'))\n if (!cancelled) setCustomFieldValues({})\n }\n try {\n const featureCheck = await apiCall<FeatureCheckResponse>(\n '/api/auth/feature-check',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ features: ['directory.organizations.view'] }),\n },\n { fallback: { ok: false } },\n )\n if (!cancelled) setCanEditOrgs(Boolean(featureCheck.result?.ok))\n } catch (err) {\n console.error('Failed to check features:', err)\n }\n if (!cancelled) setLoading(false)\n }\n load()\n return () => { cancelled = true }\n }, [id, t])\n\n const selectedOrgId = initialUser?.organizationId ? String(initialUser.organizationId) : null\n const preloadedTenants = React.useMemo(() => {\n if (!selectedTenantId) return null\n const name = initialUser?.tenantId === selectedTenantId\n ? (initialUser?.tenantName ?? selectedTenantId)\n : selectedTenantId\n return [{ id: selectedTenantId, name, isActive: true }]\n }, [initialUser, selectedTenantId])\n\n const loadRoleOptions = React.useCallback(async (query?: string): Promise<CrudFieldOption[]> => {\n if (actorIsSuperAdmin) {\n if (!selectedTenantId) return []\n return fetchRoleOptions(query, { tenantId: selectedTenantId })\n }\n return fetchRoleOptions(query)\n }, [actorIsSuperAdmin, selectedTenantId])\n\n const fields: CrudField[] = React.useMemo(() => {\n const items: CrudField[] = [\n { id: 'email', label: t('auth.users.form.field.email', 'Email'), type: 'text', required: true },\n {\n id: 'password',\n label: t('auth.users.form.field.password', 'Password'),\n type: 'text',\n description: passwordDescription,\n },\n ]\n if (actorIsSuperAdmin) {\n items.push({\n id: 'tenantId',\n label: t('auth.users.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 setSelectedTenantId(resolved)\n setAclData({ isSuperAdmin: false, features: [], organizations: null })\n }}\n includeEmptyOption\n className=\"w-full h-9 rounded border px-2 text-sm\"\n required\n tenants={preloadedTenants}\n />\n )\n },\n })\n }\n items.push({\n id: 'organizationId',\n label: t('auth.users.form.field.organization', 'Organization'),\n type: 'custom',\n component: ({ id, value, setValue }) => {\n const normalizedValue = typeof value === 'string' ? (value.length > 0 ? value : null) : null\n return (\n <TenantAwareOrganizationSelectInput\n fieldId={id}\n value={normalizedValue}\n setValue={(next) => setValue(next ?? null)}\n tenantId={selectedTenantId}\n includeInactiveIds={selectedOrgId ? [selectedOrgId] : undefined}\n />\n )\n },\n })\n items.push({ id: 'roles', label: t('auth.users.form.field.roles', 'Roles'), type: 'tags', loadOptions: loadRoleOptions })\n return items\n }, [actorIsSuperAdmin, loadRoleOptions, passwordDescription, preloadedTenants, selectedOrgId, selectedTenantId, t])\n\n const detailFieldIds = React.useMemo(() => {\n const base: string[] = ['email', 'password', 'organizationId', 'roles']\n if (actorIsSuperAdmin) base.splice(2, 0, 'tenantId')\n return base\n }, [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => [\n { id: 'details', title: t('auth.users.form.group.details', 'Details'), column: 1, fields: detailFieldIds },\n { id: 'custom', title: t('auth.users.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n {\n id: 'acl',\n title: t('auth.users.form.group.access', 'Access'),\n column: 1,\n component: () => (id\n ? (\n <AclEditor\n kind=\"user\"\n targetId={String(id)}\n canEditOrganizations={canEditOrgs}\n value={aclData}\n onChange={setAclData}\n userRoles={initialUser?.roles || []}\n currentUserIsSuperAdmin={actorIsSuperAdmin}\n tenantId={selectedTenantId ?? null}\n />\n )\n : null),\n },\n {\n id: 'dashboardWidgets',\n title: t('auth.users.form.group.widgets', 'Dashboard Widgets'),\n column: 2,\n component: () => (id && initialUser\n ? (\n <WidgetVisibilityEditor\n kind=\"user\"\n targetId={String(id)}\n tenantId={selectedTenantId ?? null}\n organizationId={initialUser?.organizationId ?? null}\n ref={widgetEditorRef}\n />\n ) : null\n ),\n },\n ], [aclData, actorIsSuperAdmin, canEditOrgs, detailFieldIds, id, initialUser, selectedTenantId, t])\n\n const initialValues = React.useMemo(() => {\n if (initialUser) {\n return {\n email: initialUser.email,\n password: '',\n tenantId: initialUser.tenantId,\n organizationId: initialUser.organizationId,\n roles: initialUser.roles,\n ...customFieldValues,\n }\n }\n return {\n email: '',\n password: '',\n tenantId: selectedTenantId ?? null,\n organizationId: null,\n roles: [],\n ...customFieldValues,\n }\n }, [initialUser, customFieldValues, selectedTenantId])\n\n return (\n <Page>\n <PageBody>\n {error && (\n <div className=\"p-4 mb-4 bg-red-50 border border-red-200 rounded text-red-800\">\n {error}\n </div>\n )}\n <CrudForm<EditUserFormValues>\n title={t('auth.users.form.title.edit', 'Edit User')}\n backHref=\"/backend/users\"\n fields={fields}\n groups={groups}\n entityId={E.auth.user}\n initialValues={initialValues}\n isLoading={loading}\n loadingMessage={t('auth.users.form.loading', 'Loading user data...')}\n submitLabel={t('auth.users.form.action.save', 'Save')}\n cancelHref=\"/backend/users\"\n successRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.updated', 'User saved'))}&type=success`}\n onSubmit={async (values) => {\n if (!id) return\n const customFields = collectCustomFieldValues(values)\n const payload = {\n id: id ? String(id) : '',\n email: values.email,\n password: values.password && values.password.trim() ? values.password : undefined,\n organizationId: values.organizationId ? values.organizationId : undefined,\n roles: Array.isArray(values.roles) ? values.roles : [],\n ...(Object.keys(customFields).length ? { customFields } : {}),\n }\n await updateCrud('auth/users', payload)\n await updateCrud('auth/users/acl', { userId: id, ...aclData }, {\n errorMessage: t('auth.users.form.errors.aclUpdate', 'Failed to update user 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/users', String(id), {\n errorMessage: t('auth.users.form.errors.delete', 'Failed to delete user'),\n })\n }}\n deleteRedirect={`/backend/users?flash=${encodeURIComponent(t('auth.users.flash.deleted', 'User deleted'))}&type=success`}\n />\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["id"]
|
|
7
7
|
}
|
package/dist/modules/auth/cli.js
CHANGED
|
@@ -14,6 +14,7 @@ import { decryptWithAesGcm } from "@open-mercato/shared/lib/encryption/aes";
|
|
|
14
14
|
import { env } from "process";
|
|
15
15
|
import crypto from "node:crypto";
|
|
16
16
|
import { formatPasswordRequirements, getPasswordPolicy, validatePassword } from "@open-mercato/shared/lib/auth/passwordPolicy";
|
|
17
|
+
import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
|
|
17
18
|
const addUser = {
|
|
18
19
|
command: "add-user",
|
|
19
20
|
async run(rest) {
|
|
@@ -372,21 +373,21 @@ const addOrganization = {
|
|
|
372
373
|
const setupApp = {
|
|
373
374
|
command: "setup",
|
|
374
375
|
async run(rest) {
|
|
375
|
-
const args =
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
const
|
|
382
|
-
const email = args.email;
|
|
383
|
-
const password = args.password;
|
|
384
|
-
const rolesCsv = (args.roles ?? "superadmin,admin,employee").trim();
|
|
376
|
+
const args = parseArgs(rest);
|
|
377
|
+
const orgName = typeof args.orgName === "string" ? args.orgName : typeof args.name === "string" ? args.name : void 0;
|
|
378
|
+
const email = typeof args.email === "string" ? args.email : void 0;
|
|
379
|
+
const password = typeof args.password === "string" ? args.password : void 0;
|
|
380
|
+
const rolesCsv = typeof args.roles === "string" ? args.roles.trim() : "superadmin,admin,employee";
|
|
381
|
+
const skipPasswordPolicyRaw = args["skip-password-policy"] ?? args.skipPasswordPolicy ?? args["allow-weak-password"] ?? args.allowWeakPassword;
|
|
382
|
+
const skipPasswordPolicy = typeof skipPasswordPolicyRaw === "boolean" ? skipPasswordPolicyRaw : parseBooleanToken(typeof skipPasswordPolicyRaw === "string" ? skipPasswordPolicyRaw : null) ?? false;
|
|
385
383
|
if (!orgName || !email || !password) {
|
|
386
|
-
console.error("Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]");
|
|
384
|
+
console.error("Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee] [--skip-password-policy]");
|
|
387
385
|
return;
|
|
388
386
|
}
|
|
389
|
-
if (!ensurePasswordPolicy(password)) return;
|
|
387
|
+
if (!skipPasswordPolicy && !ensurePasswordPolicy(password)) return;
|
|
388
|
+
if (skipPasswordPolicy) {
|
|
389
|
+
console.warn("\u26A0\uFE0F Password policy validation skipped for setup.");
|
|
390
|
+
}
|
|
390
391
|
const { resolve } = await createRequestContainer();
|
|
391
392
|
const em = resolve("em");
|
|
392
393
|
const roleNames = rolesCsv ? rolesCsv.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/auth/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { computeEmailHash } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\nimport { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persistAndFlush(u)\n if (rolesCsv) {\n const names = rolesCsv.split(',').map(s => s.trim()).filter(Boolean)\n for (const name of names) {\n let role = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (!role && normalizedTenantId !== null) {\n role = await em.findOne(Role, { name, tenantId: null })\n }\n if (!role) {\n role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persistAndFlush(role)\n } else if (normalizedTenantId !== null && role.tenantId !== normalizedTenantId) {\n role.tenantId = normalizedTenantId\n await em.persistAndFlush(role)\n }\n const link = em.create(UserRole, { user: u, role })\n await em.persistAndFlush(link)\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nfunction ensurePasswordPolicy(password: string): boolean {\n const policy = getPasswordPolicy()\n const result = validatePassword(password, policy)\n if (result.ok) return true\n const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)\n const suffix = requirements ? `: ${requirements}` : ''\n console.error(`Password does not meet the requirements${suffix}.`)\n return false\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persistAndFlush(tenant)\n const org = em.create(Organization, { name, tenant })\n await em.persistAndFlush(org)\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const orgName = args.orgName || args.name\n const email = args.email\n const password = args.password\n const rolesCsv = (args.roles ?? 'superadmin,admin,employee').trim()\n if (!orgName || !email || !password) {\n console.error('Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? rolesCsv.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n\n try {\n const result = await setupInitialTenant(em, {\n orgName,\n roleNames,\n primaryUser: { email, password, confirm: true },\n includeDerivedUsers: true,\n })\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if(env.NODE_ENV !== 'test') { \n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.user.email === email && password) {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', password)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if(env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n console.error('Setup aborted: user already exists with the provided email.')\n return\n }\n throw err\n }\n },\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n \n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n \n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n \n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n \n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n \n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n \n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n \n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n \n const users = await em.find(User, where)\n \n if (users.length === 0) {\n console.log('No users found')\n return\n }\n \n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n \n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n \n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n \n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name?.substring(0, 19) + '...' || user.organizationId.substring(0, 8) + '...'\n }\n \n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name?.substring(0, 19) + '...' || user.tenantId.substring(0, 8) + '...'\n }\n \n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n \n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const email = args.email\n const password = args.password\n \n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const emailHash = computeEmailHash(email)\n const user = await em.findOne(User, { $or: [{ email }, { emailHash }] })\n \n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n \n user.passwordHash = await hash(password, 10)\n await em.persistAndFlush(user)\n \n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,8BAA8B;AACvC,SAAS,YAAY;AAErB,SAAS,MAAM,MAAM,gBAAgB;AACrC,SAAS,QAAQ,oBAAoB;AACrC,SAAS,iCAAiC;AAC1C,SAAS,aAAa,0BAA0B;AAChD,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,oBAAoB,6BAA6B;AAC1D,SAAS,qCAAqC;AAC9C,SAAS,wBAAwB;AACjC,SAAS,mCAAmC;AAC5C,SAAS,yBAAyB;AAClC,SAAS,WAAW;AAEpB,OAAO,YAAY;AACnB,SAAS,4BAA4B,mBAAmB,wBAAwB;AAEhF,MAAM,UAAqB;AAAA,EACzB,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACpC,YAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAI,EAAG,MAAK,CAAC,IAAI;AAAA,IACnB;AACA,UAAM,QAAQ,KAAK;AACnB,UAAM,WAAW,KAAK;AACtB,UAAM,iBAAiB,OAAO,KAAK,kBAAkB,KAAK,SAAS,KAAK,GAAG;AAC3E,UAAM,YAAY,KAAK,SAAS,IAAI,KAAK;AACzC,QAAI,CAAC,SAAS,CAAC,YAAY,CAAC,gBAAgB;AAC1C,cAAQ,MAAM,sHAAsH;AACpI;AAAA,IACF;AACA,QAAI,CAAC,qBAAqB,QAAQ,EAAG;AACrC,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,MACH,MAAM;AAAA,MACL;AAAA,MACA;AAAA,MACA,EAAE,IAAI,eAAe;AAAA,MACrB,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,MACvB,EAAE,UAAU,MAAM,eAAe;AAAA,IACnC,KAAM;AACR,QAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wBAAwB;AAClD,UAAM,cAAc,IAAI,QAAQ,KAAK,OAAO,IAAI,OAAO,EAAE,IAAI;AAC7D,UAAM,qBAAqB,kBAAkB,eAAe,IAAI,KAAK;AACrE,UAAM,IAAI,GAAG,OAAO,MAAM;AAAA,MACxB;AAAA,MACA,WAAW,iBAAiB,KAAK;AAAA,MACjC,cAAc,MAAM,KAAK,UAAU,EAAE;AAAA,MACrC,aAAa;AAAA,MACb,gBAAgB,IAAI;AAAA,MACpB,UAAU,IAAI,OAAO;AAAA,IACvB,CAAC;AACD,UAAM,GAAG,gBAAgB,CAAC;AAC1B,QAAI,UAAU;AACZ,YAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AACnE,iBAAW,QAAQ,OAAO;AACxB,YAAI,OAAO,MAAM,GAAG,QAAQ,MAAM,EAAE,MAAM,UAAU,mBAAmB,CAAC;AACxE,YAAI,CAAC,QAAQ,uBAAuB,MAAM;AACxC,iBAAO,MAAM,GAAG,QAAQ,MAAM,EAAE,MAAM,UAAU,KAAK,CAAC;AAAA,QACxD;AACA,YAAI,CAAC,MAAM;AACX,iBAAO,GAAG,OAAO,MAAM,EAAE,MAAM,UAAU,oBAAoB,WAAW,oBAAI,KAAK,EAAE,CAAC;AAClF,gBAAM,GAAG,gBAAgB,IAAI;AAAA,QAC/B,WAAW,uBAAuB,QAAQ,KAAK,aAAa,oBAAoB;AAC9E,eAAK,WAAW;AAChB,gBAAM,GAAG,gBAAgB,IAAI;AAAA,QAC/B;AACA,cAAM,OAAO,GAAG,OAAO,UAAU,EAAE,MAAM,GAAG,KAAK,CAAC;AAClD,cAAM,GAAG,gBAAgB,IAAI;AAAA,MAC/B;AAAA,IACF;AACA,YAAQ,IAAI,wBAAwB,EAAE,EAAE;AAAA,EAC1C;AACF;AAEA,SAAS,UAAU,MAAgB;AACjC,QAAM,OAAyC,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,CAAC,EAAG;AACR,QAAI,EAAE,WAAW,IAAI,GAAG;AACtB,YAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,OAAO,EAAE,EAAE,MAAM,GAAG;AAC7C,UAAI,MAAM,OAAW,MAAK,CAAC,IAAI;AAAA,eACtB,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAG,WAAW,IAAI,GAAG;AAAE,aAAK,CAAC,IAAI,KAAK,IAAI,CAAC;AAAI;AAAA,MAAI,MACjF,MAAK,CAAC,IAAI;AAAA,IACjB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAuB;AAChD,SAAO,MAAM,KAAK,EAAE,QAAQ,gBAAgB,EAAE;AAChD;AAEA,SAAS,WAAW,OAAiD;AACnE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,kBAAkB,KAAK,CAAC,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC/F;AAEA,SAAS,qBAAqB,UAA2B;AACvD,QAAM,SAAS,kBAAkB;AACjC,QAAM,SAAS,iBAAiB,UAAU,MAAM;AAChD,MAAI,OAAO,GAAI,QAAO;AACtB,QAAM,eAAe,2BAA2B,QAAQ,CAAC,MAAM,aAAa,QAAQ;AACpF,QAAM,SAAS,eAAe,KAAK,YAAY,KAAK;AACpD,UAAQ,MAAM,0CAA0C,MAAM,GAAG;AACjE,SAAO;AACT;AAEA,eAAe,4BAA+B,IAAkC;AAC9E,QAAM,WAAW,QAAQ,IAAI;AAC7B,UAAQ,IAAI,+BAA+B;AAC3C,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,UAAE;AACA,QAAI,aAAa,QAAW;AAC1B,aAAO,QAAQ,IAAI;AAAA,IACrB,OAAO;AACL,cAAQ,IAAI,+BAA+B;AAAA,IAC7C;AAAA,EACF;AACF;AAEA,MAAM,qBAA2C;AAAA,EAE/C,YAAY,QAAgB;AAC1B,SAAK,OAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,kBAAkB,MAAM,CAAC,EAAE,OAAO;AAAA,EACnF;AAAA,EAEA,YAAqB;AACnB,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,UAA0B;AAC1C,UAAM,aAAa;AACnB,UAAM,YAAY;AAClB,UAAM,UAAU,OAAO,WAAW,KAAK,MAAM,UAAU,YAAY,WAAW,QAAQ;AACtF,WAAO,QAAQ,SAAS,QAAQ;AAAA,EAClC;AAAA,EAEA,MAAM,aAAa,UAA6C;AAC9D,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,EAAE,UAAU,KAAK,KAAK,UAAU,QAAQ,GAAG,WAAW,KAAK,IAAI,EAAE;AAAA,EAC1E;AAAA,EAEA,MAAM,gBAAgB,UAA6C;AACjE,WAAO,KAAK,aAAa,QAAQ;AAAA,EACnC;AACF;AAEA,SAAS,eAAe,KAAsC;AAC5D,MAAI,CAAC,KAAK,IAAK,QAAO;AACtB,SAAO,OAAO,WAAW,QAAQ,EAAE,OAAO,IAAI,GAAG,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAC9E;AAEA,SAAS,kBACP,SACA,KACe;AACf,MAAI,CAAC,KAAK,IAAK,QAAO;AACtB,SAAO,kBAAkB,SAAS,IAAI,GAAG;AAC3C;AAEA,MAAM,YAAuB;AAAA,EAC3B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,MAAM,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACtC,UAAI,CAAC,IAAK;AACV,YAAM,QAAQ,KAAK,IAAI,CAAC;AACxB,UAAI,MAAO,MAAK,GAAG,IAAI;AAAA,IACzB;AACA,UAAM,WAAW,KAAK,YAAY,KAAK,UAAU,KAAK,aAAa;AACnE,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAuB,IAAI;AACtC,QAAI,UAAU;AACZ,YAAM,YAAY,IAAI,EAAE,SAAS,CAAC;AAClC,cAAQ,IAAI,4CAAgC,QAAQ;AACpD;AAAA,IACF;AACA,UAAM,UAAU,MAAM,GAAG,KAAK,QAAQ,CAAC,CAAC;AACxC,QAAI,CAAC,QAAQ,QAAQ;AACnB,cAAQ,IAAI,oCAAoC;AAChD;AAAA,IACF;AACA,eAAW,UAAU,SAAS;AAC5B,YAAM,KAAK,OAAO,KAAK,OAAO,OAAO,EAAE,IAAI;AAC3C,UAAI,CAAC,GAAI;AACT,YAAM,YAAY,IAAI,EAAE,UAAU,GAAG,CAAC;AACtC,cAAQ,IAAI,4CAAgC,EAAE;AAAA,IAChD;AAAA,EACF;AACF;AAEA,MAAM,sBAAiC;AAAA,EACrC,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAAO,UAAU,IAAI;AAC3B,UAAM,WAAY,KAAK,YAAwB,KAAK,UAAsB,KAAK,aAAwB;AACvG,UAAM,iBAAkB,KAAK,kBAA8B,KAAK,SAAqB,KAAK,OAAkB;AAC5G,UAAM,SAAU,KAAK,SAAS,KAAiB,KAAK,UAAqB;AACzE,UAAM,SAAS,QAAQ,KAAK,SAAS,KAAK,KAAK,GAAG;AAClD,UAAM,QAAQ,QAAQ,KAAK,KAAK;AAChC,UAAM,SAAS,QAAQ,MAAM;AAC7B,QAAI,UAAU,CAAC,UAAU;AACvB,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,8BAA8B,GAAG;AACpC,cAAQ,MAAM,+CAA+C;AAC7D;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAuB,IAAI;AACtC,UAAM,oBAAoB,IAAI,4BAA4B,IAAW,EAAE,KAAK,iBAAiB,EAAE,CAAC;AAChG,UAAM,SAAS,UAAU,SAAS,IAAI,qBAAqB,MAAM,IAAI;AACrE,QAAI,OAAO;AACT,cAAQ,IAAI,2BAA2B;AAAA,QACrC,WAAW,QAAQ,MAAM;AAAA,QACzB;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,gBAAgB,kBAAkB;AAAA,MACpC,CAAC;AACD,cAAQ,IAAI,4CAA4C;AAAA,QACtD,QAAQ,WAAW,MAAM;AAAA,QACzB,YAAY,WAAW,QAAQ,IAAI,mCAAmC;AAAA,MACxE,CAAC;AAAA,IACH;AACA,QAAI,CAAC,kBAAkB,UAAU,GAAG;AAClC,cAAQ,MAAM,wEAAwE;AACtF;AAAA,IACF;AACA,UAAM,OAAa,GAAW,gBAAgB;AAC9C,QAAI,CAAC,QAAQ,OAAO,KAAK,YAAY,YAAY;AAC/C,cAAQ,MAAM,4CAA4C;AAC1D;AAAA,IACF;AACA,UAAM,OAAQ,IAAY,cAAc,GAAG,MAAM,IAAI;AACrD,UAAM,YAAY,MAAM,aAAa;AACrC,UAAM,SAAS,MAAM;AACrB,UAAM,iBAAiB,SAAS,IAAI,MAAM,MAAM,SAAS,MAAM,IAAI,SAAS;AAC5E,UAAM,qBAAqB,CAAC,UAA4B;AACtD,UAAI,OAAO,UAAU,SAAU,QAAO;AACtC,YAAM,QAAQ,MAAM,MAAM,GAAG;AAC7B,aAAO,MAAM,WAAW,KAAK,MAAM,CAAC,MAAM;AAAA,IAC5C;AACA,UAAM,aAAa,oBAAI,IAAY;AACnC,UAAM,cAAc,oBAAI,IAA8B;AACtD,UAAM,eAAe,OAAO,eAAuB,wBAAiD;AAClG,UAAI,SAAS,CAAC,WAAW,IAAI,aAAa,GAAG;AAC3C,mBAAW,IAAI,aAAa;AAC5B,cAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,QAAQ,IAAI;AAAA,UACzC,QAAQ,aAAa,aAAa,KAAK,QAAQ,QAAQ,IAAI;AAAA,UAC3D,kBAAkB,OAAO,aAAa;AAAA,QACxC,CAAC;AACD,gBAAQ,IAAI,4CAA4C;AAAA,UACtD,UAAU;AAAA,UACV,QAAQ,eAAe,MAAM;AAAA,UAC7B,YAAY,eAAe,MAAM;AAAA,QACnC,CAAC;AAAA,MACH;AACA,YAAM,UAAU,MAAM,KAAK;AAAA,QACzB,qCAAqC,cAAc;AAAA,QACnD,CAAC,eAAe,mBAAmB;AAAA,MACrC;AACA,YAAM,OAAO,MAAM,QAAQ,OAAO,IAAI,UAAU,CAAC;AACjD,YAAM,UAAU,SACZ,OACA,KAAK,OAAO,CAAC,QAAa,CAAC,mBAAmB,KAAK,KAAK,CAAC;AAC7D,UAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,cAAQ;AAAA,QACN,SAAS,QAAQ,MAAM,yCAAyC,mBAAmB,GAAG,SAAS,eAAe,EAAE;AAAA,MAClH;AACA,UAAI,OAAQ,QAAO;AACnB,YAAM,MAAM,QAAQ,IAAI,CAAC,QAAa,OAAO,IAAI,EAAE,CAAC;AACpD,YAAM,QAAQ,SACV,MAAM,GAAG;AAAA,QACP;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,IAAI,GAAG,UAAU,eAAe,gBAAgB,oBAAoB;AAAA,MACnF,IACA,MAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA,EAAE,IAAI,EAAE,KAAK,IAAI,GAAG,UAAU,eAAe,gBAAgB,oBAAoB;AAAA,QACjF,CAAC;AAAA,QACD,EAAE,UAAU,eAAe,gBAAgB,qBAAqB,kBAAkB;AAAA,MACpF;AACJ,YAAM,YAAY,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;AACtE,UAAI,UAAU;AACd,iBAAW,OAAO,SAAS;AACzB,cAAM,OAAO,UAAU,IAAI,OAAO,IAAI,EAAE,CAAC;AACzC,YAAI,CAAC,KAAM;AACX,cAAM,WAAW,OAAO,IAAI,UAAU,WAAW,IAAI,QAAQ,OAAO,IAAI,SAAS,EAAE;AACnF,YAAI,CAAC,SAAU;AACf,YAAI,WAAW,CAAC,mBAAmB,QAAQ,KAAK,CAAC,SAAS;AACxD;AAAA,QACF;AACA,YAAI,aAAa;AACjB,YAAI,UAAU,mBAAmB,QAAQ,KAAK,QAAQ;AACpD,cAAI,OAAO;AACT,oBAAQ,IAAI,sCAAsC;AAAA,cAChD,QAAQ,IAAI;AAAA,cACZ,UAAU;AAAA,cACV,gBAAgB;AAAA,YAClB,CAAC;AAAA,UACH;AACA,cAAI,SAAS,YAAY,IAAI,aAAa,KAAK;AAC/C,cAAI,CAAC,YAAY,IAAI,aAAa,GAAG;AACnC,qBAAS,MAAM,OAAO,aAAa,aAAa;AAChD,wBAAY,IAAI,eAAe,MAAM;AAAA,UACvC;AACA,gBAAM,aAAa,kBAAkB,UAAU,MAAM;AACrD,cAAI,OAAO,eAAe,YAAY,mBAAmB,UAAU,EAAG;AACtE,uBAAa;AAAA,QACf;AACA,YAAI,CAAC,WAAY;AACjB,cAAM,YAAY,MAAM,kBAAkB;AAAA,UACxC;AAAA,UACA,EAAE,OAAO,WAAW;AAAA,UACpB;AAAA,UACA;AAAA,QACF;AACA,cAAM,YAAY,UAAU;AAC5B,YAAI,aAAa,cAAc,KAAK,OAAO;AACzC,eAAK,QAAQ;AACb,eAAK,YAAa,UAAkB,aAAa,iBAAiB,UAAU;AAC5E,aAAG,QAAQ,IAAI;AACf,qBAAW;AAAA,QACb;AAAA,MACF;AACA,UAAI,UAAU,GAAG;AACf,cAAM,GAAG,MAAM;AAAA,MACjB;AACA,aAAO;AAAA,IACT;AAEA,QAAI,YAAY,gBAAgB;AAC9B,YAAM,UAAU,MAAM,aAAa,OAAO,QAAQ,GAAG,OAAO,cAAc,CAAC;AAC3E,UAAI,CAAC,SAAS;AACZ,gBAAQ,IAAI,gEAAgE;AAAA,MAC9E,OAAO;AACL,gBAAQ,IAAI,aAAa,OAAO,sBAAsB;AAAA,MACxD;AACA;AAAA,IACF;AAEA,UAAM,gBAAgB,MAAM,GAAG,KAAK,cAAc,CAAC,CAAC;AACpD,QAAI,CAAC,cAAc,QAAQ;AACzB,cAAQ,IAAI,6CAA6C;AACzD;AAAA,IACF;AACA,QAAI,QAAQ;AACZ,eAAW,OAAO,eAAe;AAC/B,YAAM,gBAAgB,IAAI,QAAQ,KAAK,OAAO,IAAI,OAAO,EAAE,IAAI,IAAI,OAAO,KAAK,OAAO,IAAI,OAAO,EAAE,IAAI;AACvG,YAAM,sBAAsB,IAAI,KAAK,OAAO,IAAI,EAAE,IAAI;AACtD,UAAI,CAAC,iBAAiB,CAAC,oBAAqB;AAC5C,eAAS,MAAM,aAAa,eAAe,mBAAmB;AAAA,IAChE;AACA,QAAI,QAAQ,GAAG;AACb,cAAQ,IAAI,aAAa,KAAK,+CAA+C;AAAA,IAC/E,OAAO;AACL,cAAQ,IAAI,kEAAkE;AAAA,IAChF;AAAA,EACF;AACF;AAIA,MAAM,kBAA6B;AAAA,EACjC,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACpC,YAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAI,EAAG,MAAK,CAAC,IAAI;AAAA,IACnB;AACA,UAAM,OAAO,KAAK,QAAQ,KAAK;AAC/B,QAAI,CAAC,MAAM;AACT,cAAQ,MAAM,wDAAwD;AACtE;AAAA,IACF;AACA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AAEvB,UAAM,SAAS,GAAG,OAAO,QAAQ,EAAE,MAAM,GAAG,IAAI,UAAU,CAAC;AAC3D,UAAM,GAAG,gBAAgB,MAAM;AAC/B,UAAM,MAAM,GAAG,OAAO,cAAc,EAAE,MAAM,OAAO,CAAC;AACpD,UAAM,GAAG,gBAAgB,GAAG;AAC5B,UAAM,0BAA0B,IAAI,OAAO,OAAO,EAAE,CAAC;AACrD,YAAQ,IAAI,gCAAgC,IAAI,IAAI,aAAa,OAAO,EAAE;AAAA,EAC5E;AACF;AAEA,MAAM,WAAsB;AAAA,EAC1B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACpC,YAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAI,EAAG,MAAK,CAAC,IAAI;AAAA,IACnB;AACA,UAAM,UAAU,KAAK,WAAW,KAAK;AACrC,UAAM,QAAQ,KAAK;AACnB,UAAM,WAAW,KAAK;AACtB,UAAM,YAAY,KAAK,SAAS,6BAA6B,KAAK;AAClE,QAAI,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU;AACnC,cAAQ,MAAM,sHAAsH;AACpI;AAAA,IACF;AACA,QAAI,CAAC,qBAAqB,QAAQ,EAAG;AACrC,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAuB,IAAI;AACtC,UAAM,YAAY,WACd,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO,IACvD;AAEJ,QAAI;AACF,YAAM,SAAS,MAAM,mBAAmB,IAAI;AAAA,QAC1C;AAAA,QACA;AAAA,QACA,aAAa,EAAE,OAAO,UAAU,SAAS,KAAK;AAAA,QAC9C,qBAAqB;AAAA,MACvB,CAAC;AAED,UAAI,OAAO,oBAAoB;AAC7B,gBAAQ,IAAI,4DAAkD;AAC9D,gBAAQ,IAAI,wBAAc,KAAK,EAAE;AACjC,gBAAQ,IAAI,wEAA8D;AAAA,MAC5E;AAEA,UAAG,IAAI,aAAa,QAAQ;AAC1B,mBAAW,YAAY,OAAO,OAAO;AACnC,cAAI,SAAS,SAAS;AACpB,gBAAI,SAAS,KAAK,UAAU,SAAS,UAAU;AAC7C,sBAAQ,IAAI,0BAAmB,SAAS,KAAK,OAAO,aAAa,QAAQ;AAAA,YAC3E,OAAO;AACL,sBAAQ,IAAI,0BAAmB,SAAS,KAAK,KAAK;AAAA,YACpD;AAAA,UACF,OAAO;AACL,oBAAQ,IAAI,gBAAgB,SAAS,KAAK,KAAK,EAAE;AAAA,UACnD;AAAA,QACF;AAAA,MACF;AAEA,UAAG,IAAI,aAAa,OAAU,SAAQ,IAAI,0BAAqB,EAAE,UAAU,OAAO,UAAU,gBAAgB,OAAO,eAAe,CAAC;AAAA,IACrI,SAAS,KAAK;AACZ,UAAI,eAAe,SAAS,IAAI,YAAY,eAAe;AACzD,gBAAQ,MAAM,6DAA6D;AAC3E;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAEA,MAAM,oBAA+B;AAAA,EACnC,SAAS;AAAA,EACT,MAAM,MAAM;AACV,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,OAAO,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,MACA,CAAC;AAAA,MACD,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,MACvB,EAAE,UAAU,MAAM,gBAAgB,KAAK;AAAA,IACzC;AAEA,QAAI,KAAK,WAAW,GAAG;AACrB,cAAQ,IAAI,wBAAwB;AACpC;AAAA,IACF;AAEA,YAAQ,IAAI,SAAS,KAAK,MAAM,mBAAmB;AACnD,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,iHAAiH;AAC7H,YAAQ,IAAI,2HAA2H;AAEvI,eAAW,OAAO,MAAM;AACtB,YAAM,UAAU,IAAI,YAAY,IAAI,KAAK,IAAI,SAAS,EAAE,mBAAmB,IAAI;AAC/E,YAAM,KAAK,IAAI,MAAM;AACrB,YAAM,WAAW,IAAI,QAAQ,MAAM;AACnC,YAAM,QAAQ,IAAI,QAAQ,WAAW,OAAO,EAAE;AAC9C,cAAQ,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC,MAAM,IAAI,MAAM,SAAS,OAAO,EAAE,CAAC,MAAM,OAAO,EAAE;AAAA,IAChF;AAAA,EACF;AACF;AAEA,MAAM,cAAyB;AAAA,EAC7B,SAAS;AAAA,EACT,MAAM,MAAM;AACV,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,UAAU,MAAM,GAAG,KAAK,QAAQ,CAAC,CAAC;AAExC,QAAI,QAAQ,WAAW,GAAG;AACxB,cAAQ,IAAI,kBAAkB;AAC9B;AAAA,IACF;AAEA,YAAQ,IAAI,SAAS,QAAQ,MAAM,aAAa;AAChD,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,0EAA0E;AACtF,YAAQ,IAAI,qFAAqF;AAEjG,eAAW,UAAU,SAAS;AAC5B,YAAM,UAAU,OAAO,YAAY,IAAI,KAAK,OAAO,SAAS,EAAE,mBAAmB,IAAI;AACrF,YAAM,KAAK,OAAO,MAAM;AACxB,YAAM,QAAQ,OAAO,QAAQ,WAAW,OAAO,EAAE;AACjD,cAAQ,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC,MAAM,IAAI,MAAM,OAAO,EAAE;AAAA,IACvD;AAAA,EACF;AACF;AAEA,MAAM,YAAuB;AAAA,EAC3B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACpC,YAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAI,EAAG,MAAK,CAAC,IAAI;AAAA,IACnB;AAEA,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AAGvB,UAAM,QAAa,CAAC;AACpB,QAAI,KAAK,kBAAkB,KAAK,SAAS,KAAK,KAAK;AACjD,YAAM,iBAAiB,KAAK,kBAAkB,KAAK,SAAS,KAAK;AAAA,IACnE;AACA,QAAI,KAAK,YAAY,KAAK,QAAQ;AAChC,YAAM,WAAW,KAAK,YAAY,KAAK;AAAA,IACzC;AAEA,UAAM,QAAQ,MAAM,GAAG,KAAK,MAAM,KAAK;AAEvC,QAAI,MAAM,WAAW,GAAG;AACtB,cAAQ,IAAI,gBAAgB;AAC5B;AAAA,IACF;AAEA,YAAQ,IAAI,SAAS,MAAM,MAAM,WAAW;AAC5C,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,gJAAgJ;AAC5J,YAAQ,IAAI,2JAA2J;AAEvK,eAAW,QAAQ,OAAO;AAExB,YAAM,YAAY,MAAM;AAAA,QACtB;AAAA,QACA;AAAA,QACA,EAAE,MAAM,KAAK,GAAG;AAAA,QAChB,EAAE,UAAU,CAAC,MAAM,EAAE;AAAA,QACrB,EAAE,UAAU,KAAK,YAAY,MAAM,gBAAgB,KAAK,kBAAkB,KAAK;AAAA,MACjF;AACA,YAAM,QAAQ,UAAU,IAAI,CAAC,OAAY,GAAG,MAAM,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI,KAAK;AAGtF,UAAI,UAAU;AACd,UAAI,aAAa;AAEjB,UAAI,KAAK,gBAAgB;AACvB,cAAM,MAAM,MAAM,GAAG,QAAQ,cAAc,EAAE,IAAI,KAAK,eAAe,CAAC;AACtE,kBAAU,KAAK,MAAM,UAAU,GAAG,EAAE,IAAI,SAAS,KAAK,eAAe,UAAU,GAAG,CAAC,IAAI;AAAA,MACzF;AAEA,UAAI,KAAK,UAAU;AACjB,cAAM,SAAS,MAAM,GAAG,QAAQ,QAAQ,EAAE,IAAI,KAAK,SAAS,CAAC;AAC7D,qBAAa,QAAQ,MAAM,UAAU,GAAG,EAAE,IAAI,SAAS,KAAK,SAAS,UAAU,GAAG,CAAC,IAAI;AAAA,MACzF;AAEA,YAAM,KAAK,KAAK,MAAM;AACtB,YAAM,SAAS,KAAK,SAAS,OAAO,OAAO,EAAE;AAC7C,YAAM,QAAQ,KAAK,QAAQ,WAAW,OAAO,EAAE;AAE/C,cAAQ,IAAI,GAAG,GAAG,OAAO,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,QAAQ,OAAO,EAAE,CAAC,MAAM,WAAW,OAAO,EAAE,CAAC,MAAM,KAAK,EAAE;AAAA,IACnH;AAAA,EACF;AACF;AAEA,MAAM,cAAyB;AAAA,EAC7B,SAAS;AAAA,EACT,MAAM,IAAI,MAAM;AACd,UAAM,OAA+B,CAAC;AACtC,aAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;AACvC,YAAM,IAAI,KAAK,CAAC,GAAG,QAAQ,OAAO,EAAE;AACpC,YAAM,IAAI,KAAK,IAAI,CAAC;AACpB,UAAI,EAAG,MAAK,CAAC,IAAI;AAAA,IACnB;AAEA,UAAM,QAAQ,KAAK;AACnB,UAAM,WAAW,KAAK;AAEtB,QAAI,CAAC,SAAS,CAAC,UAAU;AACvB,cAAQ,MAAM,2EAA2E;AACzF;AAAA,IACF;AACA,QAAI,CAAC,qBAAqB,QAAQ,EAAG;AAErC,UAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,UAAM,KAAK,QAAQ,IAAI;AACvB,UAAM,YAAY,iBAAiB,KAAK;AACxC,UAAM,OAAO,MAAM,GAAG,QAAQ,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,EAAE,UAAU,CAAC,EAAE,CAAC;AAEvE,QAAI,CAAC,MAAM;AACT,cAAQ,MAAM,oBAAoB,KAAK,aAAa;AACpD;AAAA,IACF;AAEA,SAAK,eAAe,MAAM,KAAK,UAAU,EAAE;AAC3C,UAAM,GAAG,gBAAgB,IAAI;AAE7B,YAAQ,IAAI,kDAA6C,KAAK,EAAE;AAAA,EAClE;AACF;AAGA,IAAO,cAAQ,CAAC,SAAS,WAAW,qBAAqB,iBAAiB,UAAU,mBAAmB,aAAa,WAAW,WAAW;",
|
|
4
|
+
"sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { computeEmailHash } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\nimport { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persistAndFlush(u)\n if (rolesCsv) {\n const names = rolesCsv.split(',').map(s => s.trim()).filter(Boolean)\n for (const name of names) {\n let role = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (!role && normalizedTenantId !== null) {\n role = await em.findOne(Role, { name, tenantId: null })\n }\n if (!role) {\n role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persistAndFlush(role)\n } else if (normalizedTenantId !== null && role.tenantId !== normalizedTenantId) {\n role.tenantId = normalizedTenantId\n await em.persistAndFlush(role)\n }\n const link = em.create(UserRole, { user: u, role })\n await em.persistAndFlush(link)\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/^['\"]|['\"]$/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nfunction ensurePasswordPolicy(password: string): boolean {\n const policy = getPasswordPolicy()\n const result = validatePassword(password, policy)\n if (result.ok) return true\n const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)\n const suffix = requirements ? `: ${requirements}` : ''\n console.error(`Password does not meet the requirements${suffix}.`)\n return false\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persistAndFlush(tenant)\n const org = em.create(Organization, { name, tenant })\n await em.persistAndFlush(org)\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args = parseArgs(rest)\n const orgName = typeof args.orgName === 'string'\n ? args.orgName\n : typeof args.name === 'string'\n ? args.name\n : undefined\n const email = typeof args.email === 'string' ? args.email : undefined\n const password = typeof args.password === 'string' ? args.password : undefined\n const rolesCsv = typeof args.roles === 'string'\n ? args.roles.trim()\n : 'superadmin,admin,employee'\n const skipPasswordPolicyRaw =\n args['skip-password-policy'] ??\n args.skipPasswordPolicy ??\n args['allow-weak-password'] ??\n args.allowWeakPassword\n const skipPasswordPolicy = typeof skipPasswordPolicyRaw === 'boolean'\n ? skipPasswordPolicyRaw\n : parseBooleanToken(typeof skipPasswordPolicyRaw === 'string' ? skipPasswordPolicyRaw : null) ?? false\n if (!orgName || !email || !password) {\n console.error('Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--roles superadmin,admin,employee] [--skip-password-policy]')\n return\n }\n if (!skipPasswordPolicy && !ensurePasswordPolicy(password)) return\n if (skipPasswordPolicy) {\n console.warn('\u26A0\uFE0F Password policy validation skipped for setup.')\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? rolesCsv.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n\n try {\n const result = await setupInitialTenant(em, {\n orgName,\n roleNames,\n primaryUser: { email, password, confirm: true },\n includeDerivedUsers: true,\n })\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if(env.NODE_ENV !== 'test') { \n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.user.email === email && password) {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', password)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if(env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n console.error('Setup aborted: user already exists with the provided email.')\n return\n }\n throw err\n }\n },\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n \n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n \n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n \n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n \n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n \n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n \n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n \n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n \n const users = await em.find(User, where)\n \n if (users.length === 0) {\n console.log('No users found')\n return\n }\n \n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n \n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n \n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n \n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name?.substring(0, 19) + '...' || user.organizationId.substring(0, 8) + '...'\n }\n \n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name?.substring(0, 19) + '...' || user.tenantId.substring(0, 8) + '...'\n }\n \n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n \n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n \n const email = args.email\n const password = args.password\n \n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n \n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const emailHash = computeEmailHash(email)\n const user = await em.findOne(User, { $or: [{ email }, { emailHash }] })\n \n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n \n user.passwordHash = await hash(password, 10)\n await em.persistAndFlush(user)\n \n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -37,6 +37,12 @@ async function POST(req) {
|
|
|
37
37
|
}
|
|
38
38
|
const container = await createRequestContainer();
|
|
39
39
|
const em = container.resolve("em");
|
|
40
|
+
let eventBus = null;
|
|
41
|
+
try {
|
|
42
|
+
eventBus = container.resolve("eventBus");
|
|
43
|
+
} catch {
|
|
44
|
+
eventBus = null;
|
|
45
|
+
}
|
|
40
46
|
let body;
|
|
41
47
|
try {
|
|
42
48
|
body = await req.json();
|
|
@@ -70,7 +76,7 @@ async function POST(req) {
|
|
|
70
76
|
return NextResponse.json({ error: `Invalid execution context: ${errors.join(", ")}` }, { status: 400 });
|
|
71
77
|
}
|
|
72
78
|
try {
|
|
73
|
-
const result = await ruleEngine.executeRules(em, context);
|
|
79
|
+
const result = await ruleEngine.executeRules(em, context, { eventBus });
|
|
74
80
|
const response = {
|
|
75
81
|
allowed: result.allowed,
|
|
76
82
|
executedRules: result.executedRules.map((r) => ({
|