@seedgrid/fe-security 2026.7.0 → 2026.7.1
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/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +5 -0
- package/dist/client/permission-provider.d.ts +18 -0
- package/dist/client/permission-provider.d.ts.map +1 -0
- package/dist/client/permission-provider.js +57 -0
- package/dist/client/pick-list-field.d.ts +15 -0
- package/dist/client/pick-list-field.d.ts.map +1 -0
- package/dist/client/pick-list-field.js +30 -0
- package/dist/client/status-badge.d.ts +6 -0
- package/dist/client/status-badge.d.ts.map +1 -0
- package/dist/client/status-badge.js +6 -0
- package/dist/config.d.ts +34 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +4 -0
- package/dist/dtos.d.ts +88 -0
- package/dist/dtos.d.ts.map +1 -0
- package/dist/dtos.js +3 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/menu.d.ts +19 -0
- package/dist/menu.d.ts.map +1 -0
- package/dist/menu.js +41 -0
- package/dist/messages.d.ts +1858 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +990 -0
- package/dist/module.d.ts +3 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +37 -0
- package/dist/module.json +21 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +3 -0
- package/dist/permissions.d.ts +14 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +39 -0
- package/dist/server/action.d.ts +3 -0
- package/dist/server/action.d.ts.map +1 -0
- package/dist/server/action.js +21 -0
- package/dist/server/create-security-server.d.ts +4 -0
- package/dist/server/create-security-server.d.ts.map +1 -0
- package/dist/server/create-security-server.js +166 -0
- package/dist/server/errors.d.ts +13 -0
- package/dist/server/errors.d.ts.map +1 -0
- package/dist/server/errors.js +26 -0
- package/dist/server/index.d.ts +8 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +8 -0
- package/dist/server/problem-response.d.ts +3 -0
- package/dist/server/problem-response.d.ts.map +1 -0
- package/dist/server/problem-response.js +28 -0
- package/dist/server/route.d.ts +3 -0
- package/dist/server/route.d.ts.map +1 -0
- package/dist/server/route.js +22 -0
- package/dist/server/to-problem.d.ts +3 -0
- package/dist/server/to-problem.d.ts.map +1 -0
- package/dist/server/to-problem.js +22 -0
- package/dist/server-contract.d.ts +23 -0
- package/dist/server-contract.d.ts.map +1 -0
- package/dist/server-contract.js +4 -0
- package/dist/session-contract.d.ts +36 -0
- package/dist/session-contract.d.ts.map +1 -0
- package/dist/session-contract.js +8 -0
- package/dist/session-result.d.ts +22 -0
- package/dist/session-result.d.ts.map +1 -0
- package/dist/session-result.js +14 -0
- package/package.json +38 -3
- package/scaffold/README.md +97 -0
- package/scaffold/src/app/api/security/session/2fa/resend/route.ts +8 -0
- package/scaffold/src/app/api/security/session/2fa/verify/route.ts +8 -0
- package/scaffold/src/app/api/security/session/login/route.ts +7 -0
- package/scaffold/src/app/api/security/session/logout/route.ts +5 -0
- package/scaffold/src/app/api/security/session/refresh/route.ts +7 -0
- package/scaffold/src/app/api/security/users/[publicId]/avatar/route.ts +15 -0
- package/scaffold/src/app/auth/2fa/verify/page.tsx +286 -0
- package/scaffold/src/app/auth/forgot-password/confirm/page.tsx +96 -0
- package/scaffold/src/app/auth/forgot-password/page.tsx +165 -0
- package/scaffold/src/app/auth/forgot-password/success/page.tsx +31 -0
- package/scaffold/src/app/auth/login/page.tsx +386 -0
- package/scaffold/src/app/auth/welcome/page.tsx +90 -0
- package/scaffold/src/app/login/page.tsx +7 -0
- package/scaffold/src/app/page.tsx +12 -0
- package/scaffold/src/app/security/2fa/page.tsx +370 -0
- package/scaffold/src/app/security/companies/(create)/new/page.tsx +12 -0
- package/scaffold/src/app/security/companies/(edit)/[publicId]/page.tsx +18 -0
- package/scaffold/src/app/security/companies/(list)/page.tsx +298 -0
- package/scaffold/src/app/security/companies/editors/CompanyEditor.tsx +754 -0
- package/scaffold/src/app/security/profile/page.tsx +492 -0
- package/scaffold/src/app/security/roles/(create)/new/page.tsx +11 -0
- package/scaffold/src/app/security/roles/(edit)/[publicId]/page.tsx +17 -0
- package/scaffold/src/app/security/roles/(list)/page.tsx +269 -0
- package/scaffold/src/app/security/roles/editors/RoleEditor.tsx +328 -0
- package/scaffold/src/app/security/users/(create)/new/page.tsx +10 -0
- package/scaffold/src/app/security/users/(edit)/[publicId]/page.tsx +16 -0
- package/scaffold/src/app/security/users/(list)/page.tsx +289 -0
- package/scaffold/src/app/security/users/editors/UserEditor.tsx +478 -0
- package/scaffold/src/app/users/change-password/page.tsx +172 -0
- package/scaffold/src/modules/security/AuthProvider.tsx +405 -0
- package/scaffold/src/modules/security/components/AppShell.tsx.hbs +889 -0
- package/scaffold/src/modules/security/components/CompanyManagementGuard.tsx +59 -0
- package/scaffold/src/modules/security/components/PageFrame.tsx +105 -0
- package/scaffold/src/modules/security/components/RouteGuard.tsx +114 -0
- package/scaffold/src/modules/security/components/SecurityDashboard.tsx.hbs +315 -0
- package/scaffold/src/modules/security/config.ts +55 -0
- package/scaffold/src/modules/security/hooks/useAuth.ts +7 -0
- package/scaffold/src/modules/security/hooks/useDebouncedSearch.ts +13 -0
- package/scaffold/src/modules/security/hooks/usePermission.ts +35 -0
- package/scaffold/src/modules/security/host-access.ts +59 -0
- package/scaffold/src/modules/security/index.ts +44 -0
- package/scaffold/src/modules/security/navigation.ts +41 -0
- package/scaffold/src/modules/security/server/avatar-routes.ts +86 -0
- package/scaffold/src/modules/security/server/public-api-base-url.ts +26 -0
- package/scaffold/src/modules/security/server/session-routes.ts.hbs +322 -0
- package/scaffold/src/modules/security/server/session-server.ts.hbs +30 -0
- package/scaffold/src/modules/security/services/auth-service.ts +332 -0
- package/scaffold/src/modules/security/services/security-api.test.ts +178 -0
- package/scaffold/src/modules/security/services/security-api.ts +281 -0
- package/scaffold/src/modules/security/services/security-service.test.ts +71 -0
- package/scaffold/src/modules/security/services/security-service.ts +727 -0
- package/scaffold/src/modules/security/services/session-storage.ts +80 -0
- package/scaffold/src/modules/security/types.ts +295 -0
- package/scaffold/src/modules/security/useSecurityAppShellSections.tsx.hbs +180 -0
- package/scaffold/src/modules/security/utils.ts +194 -0
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { PermissionProvider, usePermissions, type PermissionContextValue, type PermissionProviderProps, } from "./permission-provider";
|
|
2
|
+
export { StatusBadge } from "./status-badge";
|
|
3
|
+
export { PickListField } from "./pick-list-field";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,kBAAkB,EAClB,cAAc,EACd,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,GAC7B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// Barrel CLIENT (@seedgrid/fe-security/client). React + next/navigation (hooks client).
|
|
2
|
+
// Não importe daqui em código server-only.
|
|
3
|
+
export { PermissionProvider, usePermissions, } from "./permission-provider";
|
|
4
|
+
export { StatusBadge } from "./status-badge";
|
|
5
|
+
export { PickListField } from "./pick-list-field";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { MePermissions } from "../session-contract";
|
|
3
|
+
export interface PermissionContextValue {
|
|
4
|
+
loading: boolean;
|
|
5
|
+
me: MePermissions | null;
|
|
6
|
+
hasRole: (role: string) => boolean;
|
|
7
|
+
hasPermission: (permission: string) => boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface PermissionProviderProps {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/** Endpoint que devolve MePermissions. Default: "/api/auth/me". */
|
|
12
|
+
meUrl?: string;
|
|
13
|
+
/** Role que implica TODAS as permissões (superuser). Default: "ADMIN". */
|
|
14
|
+
adminRole?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function PermissionProvider({ children, meUrl, adminRole, }: PermissionProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare function usePermissions(): PermissionContextValue;
|
|
18
|
+
//# sourceMappingURL=permission-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"permission-provider.d.ts","sourceRoot":"","sources":["../../src/client/permission-provider.tsx"],"names":[],"mappings":"AAOA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAEf,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,EAAE,EAAE,aAAa,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AASD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,KAAsB,EACtB,SAAmB,GACpB,EAAE,uBAAuB,2CAmDzB;AAED,wBAAgB,cAAc,IAAI,sBAAsB,CAEvD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
// Contexto de permissões do usuário corrente (portado do admin-web AdminAccessProvider).
|
|
4
|
+
// Client-safe: usa next/navigation (hook client) e react. Endpoint e role-superuser são
|
|
5
|
+
// parametrizáveis — o "ADMIN implica tudo" e o "/api/auth/me" eram hardcode do admin.
|
|
6
|
+
import { usePathname } from "next/navigation";
|
|
7
|
+
import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
|
|
8
|
+
const PermissionContext = createContext({
|
|
9
|
+
loading: true,
|
|
10
|
+
me: null,
|
|
11
|
+
hasRole: () => false,
|
|
12
|
+
hasPermission: () => false,
|
|
13
|
+
});
|
|
14
|
+
export function PermissionProvider({ children, meUrl = "/api/auth/me", adminRole = "ADMIN", }) {
|
|
15
|
+
const pathname = usePathname();
|
|
16
|
+
const [loading, setLoading] = useState(true);
|
|
17
|
+
const [me, setMe] = useState(null);
|
|
18
|
+
const loadPermissions = useCallback(async (signal) => {
|
|
19
|
+
try {
|
|
20
|
+
const response = await fetch(meUrl, { cache: "no-store", signal });
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
setMe(null);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setMe((await response.json()));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Aborts são ignorados; demais erros deixam me = null.
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
setLoading(false);
|
|
32
|
+
}
|
|
33
|
+
}, [meUrl]);
|
|
34
|
+
// Rebusca o `me` a CADA navegação — a sessão pode ter trocado de usuário (logout +
|
|
35
|
+
// login com outra conta). O cookie é httpOnly (não observável no client), então a
|
|
36
|
+
// navegação é o sinal de "sessão pode ter mudado". Refetch em background (não mexe em
|
|
37
|
+
// `loading` após a 1ª carga), então não pisca a UI.
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
const controller = new AbortController();
|
|
40
|
+
void loadPermissions(controller.signal);
|
|
41
|
+
return () => controller.abort();
|
|
42
|
+
}, [pathname, loadPermissions]);
|
|
43
|
+
const value = useMemo(() => {
|
|
44
|
+
const roles = new Set(me?.roles ?? []);
|
|
45
|
+
const permissions = new Set(me?.permissions ?? []);
|
|
46
|
+
return {
|
|
47
|
+
loading,
|
|
48
|
+
me,
|
|
49
|
+
hasRole: (role) => roles.has(role),
|
|
50
|
+
hasPermission: (permission) => roles.has(adminRole) || permissions.has(permission),
|
|
51
|
+
};
|
|
52
|
+
}, [loading, me, adminRole]);
|
|
53
|
+
return (_jsx(PermissionContext.Provider, { value: value, children: children }));
|
|
54
|
+
}
|
|
55
|
+
export function usePermissions() {
|
|
56
|
+
return useContext(PermissionContext);
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PickListOption } from "../dtos";
|
|
2
|
+
type PickListFieldProps = {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
sourceHeader: string;
|
|
6
|
+
targetHeader: string;
|
|
7
|
+
options: PickListOption[];
|
|
8
|
+
selectedValues: Array<string | number>;
|
|
9
|
+
onChange: (values: Array<string | number>) => void;
|
|
10
|
+
emptyMessage: string;
|
|
11
|
+
readOnly?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function PickListField({ id, title, sourceHeader, targetHeader, options, selectedValues, onChange, emptyMessage, readOnly, }: PickListFieldProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=pick-list-field.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pick-list-field.d.ts","sourceRoot":"","sources":["../../src/client/pick-list-field.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAM9C,KAAK,kBAAkB,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,cAAc,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;IACvC,QAAQ,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,wBAAgB,aAAa,CAAC,EAC5B,EAAE,EACF,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,QAAgB,GACjB,EAAE,kBAAkB,2CAyCpB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { SgPickList } from "@seedgrid/fe-components";
|
|
5
|
+
const groupBoxProps = {
|
|
6
|
+
className: "w-full",
|
|
7
|
+
};
|
|
8
|
+
export function PickListField({ id, title, sourceHeader, targetHeader, options, selectedValues, onChange, emptyMessage, readOnly = false, }) {
|
|
9
|
+
const sourceItems = useMemo(() => options
|
|
10
|
+
.filter((option) => !selectedValues.some((value) => isSameValue(value, option.value)))
|
|
11
|
+
.map(toItem), [options, selectedValues]);
|
|
12
|
+
const targetItems = useMemo(() => selectedValues
|
|
13
|
+
.map((selectedValue) => options.find((option) => isSameValue(selectedValue, option.value)))
|
|
14
|
+
.filter((option) => Boolean(option))
|
|
15
|
+
.map(toItem), [options, selectedValues]);
|
|
16
|
+
return (_jsx(SgPickList, { id: id, title: title, source: sourceItems, target: targetItems, sourceHeader: sourceHeader, targetHeader: targetHeader, showSourceFilter: true, showTargetFilter: true, emptyMessage: emptyMessage, readOnly: readOnly, disabled: readOnly, onChange: (event) => {
|
|
17
|
+
onChange(event.target.map((item) => item.value));
|
|
18
|
+
}, groupBoxProps: groupBoxProps }));
|
|
19
|
+
}
|
|
20
|
+
function toItem(option) {
|
|
21
|
+
return {
|
|
22
|
+
label: option.label,
|
|
23
|
+
value: option.value,
|
|
24
|
+
disabled: option.disabled,
|
|
25
|
+
data: option.data,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function isSameValue(left, right) {
|
|
29
|
+
return String(left) === String(right);
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-badge.d.ts","sourceRoot":"","sources":["../../src/client/status-badge.tsx"],"names":[],"mappings":"AAIA,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,WAAW,EACX,aAAa,GACd,EAAE;IACD,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;CACvB,2CASA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { SgBadge } from "@seedgrid/fe-components";
|
|
4
|
+
export function StatusBadge({ active, activeLabel, inactiveLabel, }) {
|
|
5
|
+
return (_jsx(SgBadge, { value: active ? activeLabel : inactiveLabel, severity: active ? "success" : "neutral", badgeStyle: "solid", rounded: true }));
|
|
6
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Nomes dos cookies httpOnly que guardam a sessão. */
|
|
2
|
+
export interface SecuritySessionCookieNames {
|
|
3
|
+
/** Access token. Ex.: "sg_admin_token". */
|
|
4
|
+
token: string;
|
|
5
|
+
/** Refresh token. Ex.: "sg_admin_refresh_token". */
|
|
6
|
+
refresh: string;
|
|
7
|
+
/** Flag de troca de senha obrigatória. Ex.: "sg_admin_password_change_required". */
|
|
8
|
+
passwordChangeRequired: string;
|
|
9
|
+
}
|
|
10
|
+
export interface SecurityServerConfig {
|
|
11
|
+
/** Base URL do report-api (ex.: "http://localhost:8080"). O app lê do env e passa aqui. */
|
|
12
|
+
apiBaseUrl: string;
|
|
13
|
+
/** Nomes dos cookies de sessão. */
|
|
14
|
+
cookies: SecuritySessionCookieNames;
|
|
15
|
+
/**
|
|
16
|
+
* Flag `secure` dos cookies. Default: `process.env.NODE_ENV === "production"`.
|
|
17
|
+
* Passe explicitamente para não depender do NODE_ENV.
|
|
18
|
+
*/
|
|
19
|
+
secureCookies?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Service token (server-to-server) usado como Authorization APENAS quando não há
|
|
22
|
+
* sessão de usuário no cookie. Corresponde ao antigo SEEDGRID_REPORT_API_TOKEN.
|
|
23
|
+
*/
|
|
24
|
+
serviceToken?: string;
|
|
25
|
+
/**
|
|
26
|
+
* PONTO DE PLUG DA TENANCY. Headers extras resolvidos por request e mesclados em
|
|
27
|
+
* toda chamada ao report-api:
|
|
28
|
+
* - account-tenancy injeta `X-Company-Id` (empresa escopada);
|
|
29
|
+
* - multi-tenancy injeta o header de tenant resolvido por host/subdomínio;
|
|
30
|
+
* - security-core (sem tenancy) omite.
|
|
31
|
+
*/
|
|
32
|
+
resolveTenancyHeaders?: () => Promise<HeadersInit> | HeadersInit;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,uDAAuD;AACvD,MAAM,WAAW,0BAA0B;IACzC,2CAA2C;IAC3C,KAAK,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAC;IAChB,oFAAoF;IACpF,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,oBAAoB;IACnC,2FAA2F;IAC3F,UAAU,EAAE,MAAM,CAAC;IAEnB,mCAAmC;IACnC,OAAO,EAAE,0BAA0B,CAAC;IAEpC;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC;CAClE"}
|
package/dist/config.js
ADDED
package/dist/dtos.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/** Página genérica do backend (list endpoints). */
|
|
2
|
+
export interface PaginatedResult<T> {
|
|
3
|
+
items: T[];
|
|
4
|
+
totalRecords: number;
|
|
5
|
+
page: number;
|
|
6
|
+
size: number;
|
|
7
|
+
sortField: string;
|
|
8
|
+
sortOrder: "asc" | "desc";
|
|
9
|
+
query: string;
|
|
10
|
+
}
|
|
11
|
+
export interface PermissionOption {
|
|
12
|
+
publicId: string;
|
|
13
|
+
name: string;
|
|
14
|
+
description: string;
|
|
15
|
+
}
|
|
16
|
+
/** Resumo de role usado dentro de UserEntry. */
|
|
17
|
+
export interface RoleSummary {
|
|
18
|
+
publicId: string;
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
export interface RoleEntry {
|
|
22
|
+
id: number;
|
|
23
|
+
publicId: string;
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
active: boolean;
|
|
27
|
+
forIntegration: boolean;
|
|
28
|
+
createdAt: string;
|
|
29
|
+
updatedAt: string;
|
|
30
|
+
permissionsCount: number;
|
|
31
|
+
permissions: PermissionOption[];
|
|
32
|
+
}
|
|
33
|
+
export interface UserEntry {
|
|
34
|
+
id: number;
|
|
35
|
+
publicId: string;
|
|
36
|
+
email: string;
|
|
37
|
+
name: string;
|
|
38
|
+
whatsApp: string;
|
|
39
|
+
active: boolean;
|
|
40
|
+
root: boolean;
|
|
41
|
+
changePasswordRequired: boolean;
|
|
42
|
+
passwordExpiresAt: string | null;
|
|
43
|
+
twoFactorEnabled: boolean;
|
|
44
|
+
emailConfirmed: boolean;
|
|
45
|
+
companiesCount: number;
|
|
46
|
+
createdAt: string;
|
|
47
|
+
updatedAt: string;
|
|
48
|
+
roles: RoleSummary[];
|
|
49
|
+
}
|
|
50
|
+
export type TwoFactorMethod = "TOTP" | "EMAIL" | "WHATSAPP";
|
|
51
|
+
export interface TwoFactorStatus {
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
primaryMethod: TwoFactorMethod | null;
|
|
54
|
+
availableMethods: TwoFactorMethod[];
|
|
55
|
+
hasBackupCodes: boolean;
|
|
56
|
+
unusedBackupCodesCount: number;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resposta de /2f/enable, /2f/totp/confirm e /2f/setup/confirm. Campos variam por
|
|
60
|
+
* etapa: enable TOTP traz qrCode/secret; confirm traz backupCodes; EMAIL/WHATSAPP
|
|
61
|
+
* enable traz apenas metadata de confirmação.
|
|
62
|
+
*/
|
|
63
|
+
export interface TwoFactorEnableResponse {
|
|
64
|
+
status: string;
|
|
65
|
+
method?: TwoFactorMethod;
|
|
66
|
+
primaryMethod?: TwoFactorMethod;
|
|
67
|
+
secret?: string;
|
|
68
|
+
otpauthUrl?: string;
|
|
69
|
+
qrCode?: {
|
|
70
|
+
format: string;
|
|
71
|
+
width: number;
|
|
72
|
+
height: number;
|
|
73
|
+
base64: string;
|
|
74
|
+
} | null;
|
|
75
|
+
backupCodes?: string[];
|
|
76
|
+
metadata?: {
|
|
77
|
+
email?: string;
|
|
78
|
+
whatsApp?: string;
|
|
79
|
+
[key: string]: unknown;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export interface PickListOption {
|
|
83
|
+
label: string;
|
|
84
|
+
value: string | number;
|
|
85
|
+
disabled?: boolean;
|
|
86
|
+
data?: unknown;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=dtos.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../src/dtos.ts"],"names":[],"mappings":"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"}
|
package/dist/dtos.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
1
|
export * from "./paths";
|
|
2
|
+
export * from "./config";
|
|
3
|
+
export * from "./session-contract";
|
|
4
|
+
export * from "./session-result";
|
|
5
|
+
export * from "./server-contract";
|
|
6
|
+
export * from "./dtos";
|
|
7
|
+
export * from "./menu";
|
|
8
|
+
export * from "./permissions";
|
|
9
|
+
export { securityMessages } from "./messages";
|
|
2
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
|
+
// Barrel client-safe (@seedgrid/fe-security). Sem imports de next/* — pode entrar em
|
|
2
|
+
// bundle client. O motor de sessão server-side fica em "@seedgrid/fe-security/server".
|
|
1
3
|
export * from "./paths";
|
|
4
|
+
export * from "./config";
|
|
5
|
+
export * from "./session-contract";
|
|
6
|
+
export * from "./session-result";
|
|
7
|
+
export * from "./server-contract";
|
|
8
|
+
export * from "./dtos";
|
|
9
|
+
export * from "./menu";
|
|
10
|
+
export * from "./permissions";
|
|
11
|
+
export { securityMessages } from "./messages";
|
package/dist/menu.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface NavItem {
|
|
2
|
+
href: string;
|
|
3
|
+
label: string;
|
|
4
|
+
children?: NavItem[];
|
|
5
|
+
/**
|
|
6
|
+
* Permissão de READ exigida para ver o item. Regras (canShowItem):
|
|
7
|
+
* - grupo (com children): aparece se tiver >=1 filho visível;
|
|
8
|
+
* - leaf com `permission`: exige hasPermission(permission);
|
|
9
|
+
* - leaf SEM `permission` (feature de plataforma sem perm dedicada): só adminRole;
|
|
10
|
+
* - home "/" é sempre pública.
|
|
11
|
+
*/
|
|
12
|
+
permission?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Href efetivo de um item: o do 1º filho (se grupo) ou o próprio. */
|
|
15
|
+
export declare function resolveNavHref(item: NavItem): string;
|
|
16
|
+
export declare function canShowItem(item: NavItem, hasPermission: (permission: string) => boolean, hasRole: (role: string) => boolean, adminRole?: string): boolean;
|
|
17
|
+
/** Filtra recursivamente a árvore, dropando grupos que ficaram sem filhos visíveis. */
|
|
18
|
+
export declare function filterNavItems(items: NavItem[], hasPermission: (permission: string) => boolean, hasRole: (role: string) => boolean, adminRole?: string): NavItem[];
|
|
19
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../src/menu.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAEpD;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,EAC9C,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EAClC,SAAS,SAAU,GAClB,OAAO,CAiBT;AAED,uFAAuF;AACvF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,OAAO,EAAE,EAChB,aAAa,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,EAC9C,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,EAClC,SAAS,SAAU,GAClB,OAAO,EAAE,CAiBX"}
|
package/dist/menu.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Algoritmo de menu-por-permissão (portado do admin-web Shell.tsx). Funções PURAS —
|
|
2
|
+
// sem React, client-safe. A app fornece a árvore `NavItem[]` (conteúdo específico dela)
|
|
3
|
+
// e as funções hasPermission/hasRole (do usePermissions); estas funções filtram.
|
|
4
|
+
/** Href efetivo de um item: o do 1º filho (se grupo) ou o próprio. */
|
|
5
|
+
export function resolveNavHref(item) {
|
|
6
|
+
return item.children?.[0]?.href ?? item.href;
|
|
7
|
+
}
|
|
8
|
+
export function canShowItem(item, hasPermission, hasRole, adminRole = "ADMIN") {
|
|
9
|
+
// Grupo: aparece se tiver >=1 filho visível — quem decide são os filhos (o
|
|
10
|
+
// filterNavItems dropa o grupo se sobrar 0). NÃO gateie o grupo pelo próprio href,
|
|
11
|
+
// senão some um grupo cujo filho o usuário PODE ver.
|
|
12
|
+
if (item.children?.length) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
// Home sempre pública.
|
|
16
|
+
if (item.href === "/") {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
// Leaf com permissão declarada: exige a permissão de read.
|
|
20
|
+
if (item.permission) {
|
|
21
|
+
return hasPermission(item.permission);
|
|
22
|
+
}
|
|
23
|
+
// Leaf de plataforma sem permissão dedicada no backend: só adminRole.
|
|
24
|
+
return hasRole(adminRole);
|
|
25
|
+
}
|
|
26
|
+
/** Filtra recursivamente a árvore, dropando grupos que ficaram sem filhos visíveis. */
|
|
27
|
+
export function filterNavItems(items, hasPermission, hasRole, adminRole = "ADMIN") {
|
|
28
|
+
return items.flatMap((item) => {
|
|
29
|
+
if (!canShowItem(item, hasPermission, hasRole, adminRole)) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
if (!item.children?.length) {
|
|
33
|
+
return [item];
|
|
34
|
+
}
|
|
35
|
+
const children = filterNavItems(item.children, hasPermission, hasRole, adminRole);
|
|
36
|
+
if (children.length === 0) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
return [{ ...item, children }];
|
|
40
|
+
});
|
|
41
|
+
}
|