@goplusvn/core 0.1.0 → 0.1.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/package.json +2 -1
- package/src/assets/erp_wallpaper.png +0 -0
- package/src/assets/goeat_logo.png +0 -0
- package/src/audit/audit-manager.ts +139 -0
- package/src/audit/index.ts +11 -0
- package/src/audit/memory-audit-logger.ts +86 -0
- package/src/audit/types.ts +50 -0
- package/src/auth/auth-service.ts +97 -0
- package/src/auth/index.ts +266 -0
- package/src/code-generation/index.ts +69 -0
- package/src/configs/auth-routes.ts +17 -0
- package/src/configs/crud.ts +136 -0
- package/src/configs/data/navigations.ts +781 -0
- package/src/configs/data/oauth-links.ts +10 -0
- package/src/configs/entities/material-categories.config.ts +125 -0
- package/src/configs/i18n.ts +12 -0
- package/src/configs/index.ts +26 -0
- package/src/configs/status.ts +25 -0
- package/src/configs/themes.ts +100 -0
- package/src/crud/components/crud-bulk-actions.tsx +91 -0
- package/src/crud/components/crud-card-view.tsx +241 -0
- package/src/crud/components/crud-context.tsx +122 -0
- package/src/crud/components/crud-delete-dialog.tsx +145 -0
- package/src/crud/components/crud-dialog.tsx +406 -0
- package/src/crud/components/crud-empty-state.tsx +104 -0
- package/src/crud/components/crud-export-button.tsx +170 -0
- package/src/crud/components/crud-field-renderer.tsx +653 -0
- package/src/crud/components/crud-filter-chips.tsx +102 -0
- package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
- package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
- package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
- package/src/crud/components/crud-filters/index.tsx +76 -0
- package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
- package/src/crud/components/crud-filters/select-filter.tsx +141 -0
- package/src/crud/components/crud-filters/text-filter.tsx +86 -0
- package/src/crud/components/crud-form.tsx +642 -0
- package/src/crud/components/crud-import-dialog.tsx +440 -0
- package/src/crud/components/crud-infinite-scroll.tsx +116 -0
- package/src/crud/components/crud-page.tsx +1017 -0
- package/src/crud/components/crud-provider.tsx +277 -0
- package/src/crud/components/crud-row-actions.tsx +189 -0
- package/src/crud/components/crud-search.tsx +82 -0
- package/src/crud/components/crud-sheet.tsx +336 -0
- package/src/crud/components/crud-table-skeleton.tsx +26 -0
- package/src/crud/components/crud-table-toolbar.tsx +91 -0
- package/src/crud/components/crud-table.tsx +352 -0
- package/src/crud/components/crud-virtual-table.tsx +55 -0
- package/src/crud/components/index.tsx +20 -0
- package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
- package/src/crud/crud-filters/datetime-filter.tsx +82 -0
- package/src/crud/crud-filters/filter-builder.tsx +64 -0
- package/src/crud/crud-filters/index.tsx +78 -0
- package/src/crud/crud-filters/radio-filter.tsx +79 -0
- package/src/crud/crud-filters/select-filter.tsx +148 -0
- package/src/crud/crud-filters/text-filter.tsx +81 -0
- package/src/crud/index.ts +43 -0
- package/src/crud/lib/crud-service.test.ts +334 -0
- package/src/crud/lib/crud-service.ts +358 -0
- package/src/crud/lib/crud-utils.test.ts +354 -0
- package/src/crud/lib/crud-utils.ts +299 -0
- package/src/crud/lib/crud-validator.ts +247 -0
- package/src/crud/lib/data-loader.ts +234 -0
- package/src/crud/lib/field-calculator.ts +241 -0
- package/src/crud/lib/field-formatter.ts +240 -0
- package/src/crud/lib/import-export-service.test.ts +290 -0
- package/src/crud/lib/import-export-service.ts +352 -0
- package/src/crud/lib/import-server-utils.ts +109 -0
- package/src/crud/lib/lazy-loader.ts +241 -0
- package/src/crud/lib/parse-filters.ts +85 -0
- package/src/crud/lib/permissions.ts +52 -0
- package/src/crud/lib/serialize-config.ts +60 -0
- package/src/crud/lib/stream-loader.ts +145 -0
- package/src/crud/lib/translate-config.ts +335 -0
- package/src/crud/lib/types.ts +11 -0
- package/src/crud/pages/entity-crud-page.tsx +144 -0
- package/src/crud/server.ts +8 -0
- package/src/home/constants.tsx +142 -0
- package/src/home/feature-showcase.tsx +171 -0
- package/src/home/home-page.tsx +191 -0
- package/src/home/hooks/index.ts +1 -0
- package/src/home/hooks/useWidgetPreferences.ts +167 -0
- package/src/home/index.ts +33 -0
- package/src/home/quick-access-dialog.tsx +271 -0
- package/src/home/quick-access-menu.tsx +267 -0
- package/src/home/types.ts +140 -0
- package/src/home/welcome-card.tsx +92 -0
- package/src/home/widget-container.tsx +258 -0
- package/src/home/widgets/base-widget.tsx +200 -0
- package/src/home/widgets/customers-widget.tsx +74 -0
- package/src/home/widgets/index.ts +6 -0
- package/src/home/widgets/orders-widget.tsx +87 -0
- package/src/home/widgets/revenue-widget.tsx +71 -0
- package/src/home/widgets/stock-widget.tsx +109 -0
- package/src/hooks/index.tsx +598 -0
- package/src/hooks/use-tenant.test.tsx +30 -0
- package/src/hooks/use-tenant.ts +5 -0
- package/src/index.ts +17 -0
- package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
- package/src/infrastructure/api-service.ts +317 -0
- package/src/infrastructure/cache/cache-manager.ts +107 -0
- package/src/infrastructure/cache/cache.ts +120 -0
- package/src/infrastructure/cache/index.ts +8 -0
- package/src/infrastructure/cache/types.ts +48 -0
- package/src/infrastructure/cron/cron-manager.ts +239 -0
- package/src/infrastructure/cron/index.ts +6 -0
- package/src/infrastructure/cron/types.ts +41 -0
- package/src/infrastructure/event-bus/event-bus.ts +145 -0
- package/src/infrastructure/event-bus/index.ts +2 -0
- package/src/infrastructure/event-bus/types.ts +22 -0
- package/src/infrastructure/index.ts +32 -0
- package/src/infrastructure/lock/decorators.ts +67 -0
- package/src/infrastructure/lock/index.ts +2 -0
- package/src/infrastructure/lock/lock-manager.ts +33 -0
- package/src/infrastructure/logger/index.ts +2 -0
- package/src/infrastructure/logger/logger.ts +96 -0
- package/src/infrastructure/logger/types.ts +25 -0
- package/src/layout/index.tsx +185 -0
- package/src/navigation/index.ts +91 -0
- package/src/notification/index.ts +14 -0
- package/src/notification/notification-service.ts +120 -0
- package/src/notification/storage/in-memory.ts +56 -0
- package/src/notification/storage/index.ts +1 -0
- package/src/notification/types.ts +51 -0
- package/src/organization/branch-service.ts +299 -0
- package/src/organization/branches.config.ts +154 -0
- package/src/organization/index.ts +5 -0
- package/src/plugin/apps-registry.ts +97 -0
- package/src/plugin/index.ts +5 -0
- package/src/plugin/types.ts +41 -0
- package/src/providers/index.tsx +109 -0
- package/src/providers/tenant-provider.tsx +45 -0
- package/src/rbac/components/roles/role-card.tsx +158 -0
- package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
- package/src/rbac/components/roles/role-toolbar.tsx +123 -0
- package/src/rbac/hooks/use-role-operations.ts +159 -0
- package/src/rbac/hooks/use-roles-data.ts +59 -0
- package/src/rbac/index.ts +297 -0
- package/src/rbac/lib/permission-helpers.ts +63 -0
- package/src/rbac/pages/action-list-page.tsx +25 -0
- package/src/rbac/pages/resource-list-page.tsx +25 -0
- package/src/rbac/pages/role-list-page.tsx +378 -0
- package/src/rbac/permission-service.ts +140 -0
- package/src/rbac/permissions.ts +135 -0
- package/src/rbac/resource-service.ts +115 -0
- package/src/rbac/resource-validator.ts +119 -0
- package/src/rbac/role-service.ts +165 -0
- package/src/rbac/server.ts +16 -0
- package/src/rbac/types.ts +38 -0
- package/src/schemas/action.schema.ts +66 -0
- package/src/schemas/branch.schema.ts +52 -0
- package/src/schemas/coming-soon-schema.ts +9 -0
- package/src/schemas/company.schema.ts +44 -0
- package/src/schemas/forgot-passward-schema.ts +9 -0
- package/src/schemas/index.ts +30 -0
- package/src/schemas/material-category.schema.ts +43 -0
- package/src/schemas/material-pricing.schema.ts +74 -0
- package/src/schemas/material.schema.ts +76 -0
- package/src/schemas/materials.ts +52 -0
- package/src/schemas/new-passward-schema.ts +15 -0
- package/src/schemas/partner-company.schema.ts +149 -0
- package/src/schemas/register-schema.ts +36 -0
- package/src/schemas/resource.schema.ts +133 -0
- package/src/schemas/role.schema.ts +11 -0
- package/src/schemas/sign-in-schema.ts +24 -0
- package/src/schemas/supplier-pricing.schema.ts +15 -0
- package/src/schemas/supplier.schema.ts +120 -0
- package/src/schemas/system-category-group.schema.ts +67 -0
- package/src/schemas/system-category.schema.ts +77 -0
- package/src/schemas/system-config.schema.ts +118 -0
- package/src/schemas/uom.schema.ts +75 -0
- package/src/schemas/user-supplier.schema.ts +179 -0
- package/src/schemas/user.schema.ts +18 -0
- package/src/schemas/verify-email-schema.ts +9 -0
- package/src/schemas/warehouse.schema.ts +49 -0
- package/src/system/components/categories/category-list.tsx +529 -0
- package/src/system/components/categories/category-manager.tsx +89 -0
- package/src/system/components/categories/group-sidebar.tsx +308 -0
- package/src/system/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/components/settings/setting-field.tsx +291 -0
- package/src/system/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/components/settings/settings-groups.ts +80 -0
- package/src/system/components/settings/settings-search.tsx +71 -0
- package/src/system/components/settings/settings-section.tsx +74 -0
- package/src/system/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/constants.ts +3 -0
- package/src/system/index.ts +150 -0
- package/src/system/job-manager.ts +176 -0
- package/src/system/pages/components/categories/category-list.tsx +537 -0
- package/src/system/pages/components/categories/category-manager.tsx +90 -0
- package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
- package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
- package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
- package/src/system/pages/components/settings/setting-field.tsx +292 -0
- package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
- package/src/system/pages/components/settings/settings-groups.ts +87 -0
- package/src/system/pages/components/settings/settings-page.tsx +372 -0
- package/src/system/pages/components/settings/settings-search.tsx +71 -0
- package/src/system/pages/components/settings/settings-section.tsx +74 -0
- package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
- package/src/system/pages/components/settings/system-settings.tsx +244 -0
- package/src/system/pages/system-category-page.tsx +15 -0
- package/src/system/pages/system-settings-page.tsx +380 -0
- package/src/system/schemas/system-category-group.schema.ts +46 -0
- package/src/system/schemas/system-category.schema.ts +56 -0
- package/src/system/services/settings-service.ts +127 -0
- package/src/system/services/system-category-service.ts +63 -0
- package/src/system/types.ts +45 -0
- package/src/types/index.ts +703 -0
- package/src/ui/auth/auth-layout.tsx +135 -0
- package/src/ui/auth/forgot-password-form.tsx +98 -0
- package/src/ui/auth/index.tsx +7 -0
- package/src/ui/auth/new-password-form.tsx +107 -0
- package/src/ui/auth/oauth-links.tsx +30 -0
- package/src/ui/auth/register-form.tsx +202 -0
- package/src/ui/auth/sign-in-form.tsx +238 -0
- package/src/ui/auth/verify-email-form.tsx +104 -0
- package/src/ui/crud/index.tsx +10 -0
- package/src/ui/data-display/accordion.tsx +65 -0
- package/src/ui/data-display/aspect-ratio.tsx +11 -0
- package/src/ui/data-display/avatar.tsx +163 -0
- package/src/ui/data-display/bento-grid.tsx +77 -0
- package/src/ui/data-display/carousel.tsx +249 -0
- package/src/ui/data-display/chart.tsx +363 -0
- package/src/ui/data-display/code-block-highlight.tsx +54 -0
- package/src/ui/data-display/collapsible.tsx +42 -0
- package/src/ui/data-display/compact-stat-bar.tsx +149 -0
- package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
- package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
- package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
- package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
- package/src/ui/data-display/data-table/data-table.tsx +597 -0
- package/src/ui/data-display/data-table/index.ts +44 -0
- package/src/ui/data-display/data-table-column-header.tsx +75 -0
- package/src/ui/data-display/data-table-pagination.tsx +130 -0
- package/src/ui/data-display/data-table-view-options.tsx +59 -0
- package/src/ui/data-display/formatted-number-input.tsx +210 -0
- package/src/ui/data-display/highlight.tsx +20 -0
- package/src/ui/data-display/hover-card.tsx +48 -0
- package/src/ui/data-display/index.tsx +50 -0
- package/src/ui/data-display/iphone-15-pro.tsx +114 -0
- package/src/ui/data-display/kanban/index.ts +4 -0
- package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
- package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
- package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
- package/src/ui/data-display/kanban/kanban-types.ts +21 -0
- package/src/ui/data-display/kpi-card.tsx +68 -0
- package/src/ui/data-display/media-grid.tsx +110 -0
- package/src/ui/data-display/safari.tsx +175 -0
- package/src/ui/data-display/show-more-text.tsx +55 -0
- package/src/ui/data-display/tabs.tsx +68 -0
- package/src/ui/data-display/timeline.tsx +256 -0
- package/src/ui/feedback/alert.tsx +60 -0
- package/src/ui/feedback/context-menu.tsx +245 -0
- package/src/ui/feedback/drawer.tsx +132 -0
- package/src/ui/feedback/error-dialog.tsx +273 -0
- package/src/ui/feedback/index.tsx +183 -0
- package/src/ui/feedback/progress.tsx +32 -0
- package/src/ui/feedback/sheet.tsx +148 -0
- package/src/ui/feedback/sonner.tsx +36 -0
- package/src/ui/forms/command.tsx +157 -0
- package/src/ui/forms/date-picker.tsx +73 -0
- package/src/ui/forms/date-range-picker.tsx +76 -0
- package/src/ui/forms/date-time-picker.tsx +109 -0
- package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
- package/src/ui/forms/editor/index.tsx +130 -0
- package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
- package/src/ui/forms/emoji-picker.tsx +109 -0
- package/src/ui/forms/file-dropzone.tsx +169 -0
- package/src/ui/forms/file-thumbnail.tsx +29 -0
- package/src/ui/forms/index.tsx +201 -0
- package/src/ui/forms/input-file.tsx +99 -0
- package/src/ui/forms/input-group.tsx +46 -0
- package/src/ui/forms/input-otp.tsx +81 -0
- package/src/ui/forms/input-phone.tsx +172 -0
- package/src/ui/forms/input-spin.tsx +116 -0
- package/src/ui/forms/input-tags.tsx +219 -0
- package/src/ui/forms/input-time.tsx +42 -0
- package/src/ui/forms/multi-select.tsx +629 -0
- package/src/ui/forms/multiple-date-picker.tsx +74 -0
- package/src/ui/forms/radio-group.tsx +42 -0
- package/src/ui/forms/rating.tsx +158 -0
- package/src/ui/forms/time-picker.tsx +57 -0
- package/src/ui/index.tsx +17 -0
- package/src/ui/layout/animated-list.tsx +77 -0
- package/src/ui/layout/animated-sidebar.tsx +294 -0
- package/src/ui/layout/command-menu.tsx +355 -0
- package/src/ui/layout/customizer.tsx +324 -0
- package/src/ui/layout/footer.tsx +43 -0
- package/src/ui/layout/full-screen-toggle.tsx +52 -0
- package/src/ui/layout/header-breadcrumb.tsx +77 -0
- package/src/ui/layout/horizontal-layout-header.tsx +83 -0
- package/src/ui/layout/horizontal-layout.tsx +50 -0
- package/src/ui/layout/index.tsx +25 -0
- package/src/ui/layout/language-dropdown.tsx +103 -0
- package/src/ui/layout/logo.tsx +63 -0
- package/src/ui/layout/main-layout.tsx +57 -0
- package/src/ui/layout/mode-dropdown.tsx +58 -0
- package/src/ui/layout/notification-dropdown.tsx +127 -0
- package/src/ui/layout/page-tabs.tsx +306 -0
- package/src/ui/layout/route-cache.tsx +214 -0
- package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
- package/src/ui/layout/sidebar.tsx +279 -0
- package/src/ui/layout/tab-content-cache.tsx +201 -0
- package/src/ui/layout/tab-navigation-provider.tsx +536 -0
- package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
- package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
- package/src/ui/layout/user-dropdown.tsx +188 -0
- package/src/ui/layout/vertical-layout-header.tsx +65 -0
- package/src/ui/layout/vertical-layout.tsx +47 -0
- package/src/ui/management/audit-log-page.tsx +209 -0
- package/src/ui/management/cache-management.tsx +349 -0
- package/src/ui/management/index.ts +3 -0
- package/src/ui/management/job-management.tsx +308 -0
- package/src/ui/pages/not-found.tsx +30 -0
- package/src/ui/primitives/badge.tsx +66 -0
- package/src/ui/primitives/breadcrumb.tsx +103 -0
- package/src/ui/primitives/button.tsx +129 -0
- package/src/ui/primitives/calendar.tsx +74 -0
- package/src/ui/primitives/card.tsx +86 -0
- package/src/ui/primitives/checkbox.tsx +31 -0
- package/src/ui/primitives/client.ts +30 -0
- package/src/ui/primitives/combobox.tsx +290 -0
- package/src/ui/primitives/dialog.tsx +121 -0
- package/src/ui/primitives/dropdown-menu.tsx +239 -0
- package/src/ui/primitives/dynamic-icon.tsx +24 -0
- package/src/ui/primitives/index.tsx +134 -0
- package/src/ui/primitives/input-number.tsx +131 -0
- package/src/ui/primitives/input.tsx +22 -0
- package/src/ui/primitives/keyboard.tsx +23 -0
- package/src/ui/primitives/label.tsx +24 -0
- package/src/ui/primitives/menubar.tsx +262 -0
- package/src/ui/primitives/navigation-menu.tsx +157 -0
- package/src/ui/primitives/pagination.tsx +118 -0
- package/src/ui/primitives/popover.tsx +56 -0
- package/src/ui/primitives/prefetch-link.tsx +60 -0
- package/src/ui/primitives/resizable.tsx +59 -0
- package/src/ui/primitives/scroll-area.tsx +63 -0
- package/src/ui/primitives/select.tsx +172 -0
- package/src/ui/primitives/separator.tsx +51 -0
- package/src/ui/primitives/sidebar.tsx +844 -0
- package/src/ui/primitives/slider.tsx +27 -0
- package/src/ui/primitives/status-badge.tsx +47 -0
- package/src/ui/primitives/sticky-layout.tsx +50 -0
- package/src/ui/primitives/switch.tsx +29 -0
- package/src/ui/primitives/table.tsx +116 -0
- package/src/ui/primitives/tabs.tsx +55 -0
- package/src/ui/primitives/toggle-group.tsx +70 -0
- package/src/ui/primitives/toggle.tsx +47 -0
- package/src/ui/primitives/tooltip.tsx +59 -0
- package/src/user/components/dangerous-zone.tsx +34 -0
- package/src/user/components/delete-account-form.tsx +40 -0
- package/src/user/components/index.ts +4 -0
- package/src/user/components/profile-info-form.tsx +390 -0
- package/src/user/components/profile-info.tsx +32 -0
- package/src/user/components/unified-profile-dialog.tsx +1019 -0
- package/src/user/components/user-stats.tsx +27 -0
- package/src/user/components/user-toolbar.tsx +137 -0
- package/src/user/components/users-card-view.tsx +253 -0
- package/src/user/index.ts +11 -0
- package/src/user/pages/user-list-page.tsx +234 -0
- package/src/user/pages/users-client-page.tsx +385 -0
- package/src/user/profile-page.tsx +19 -0
- package/src/user/schemas.ts +68 -0
- package/src/user/types.ts +34 -0
- package/src/user/user-service.ts +538 -0
- package/src/utils/index.ts +906 -0
- package/src/workflow/activity-timeline.tsx +412 -0
- package/src/workflow/approval-workflow.tsx +31 -0
- package/src/workflow/index.ts +2 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// @goerp/core/rbac
|
|
2
|
+
// Core RBAC (Role-Based Access Control) utilities for GoERP platform
|
|
3
|
+
// Consolidated from packages/shared/rbac
|
|
4
|
+
|
|
5
|
+
import type { Permission } from "../types";
|
|
6
|
+
import type { Session } from "next-auth";
|
|
7
|
+
import { logger } from "../utils";
|
|
8
|
+
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Action Mapping
|
|
11
|
+
// ============================================================================
|
|
12
|
+
|
|
13
|
+
export const CRUD_ACTIONS = {
|
|
14
|
+
create: "create",
|
|
15
|
+
view: "view",
|
|
16
|
+
update: "update",
|
|
17
|
+
delete: "delete",
|
|
18
|
+
export: "export",
|
|
19
|
+
import: "import",
|
|
20
|
+
approve: "approve",
|
|
21
|
+
reject: "reject",
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export type CrudAction = keyof typeof CRUD_ACTIONS;
|
|
25
|
+
|
|
26
|
+
export function getActionCode(operation: CrudAction): string {
|
|
27
|
+
return CRUD_ACTIONS[operation];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function getAllCrudActionCodes(): string[] {
|
|
31
|
+
return Object.values(CRUD_ACTIONS);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isCrudAction(actionCode: string): boolean {
|
|
35
|
+
return Object.values(CRUD_ACTIONS).includes(actionCode as CrudAction);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ============================================================================
|
|
39
|
+
// Permission Cache
|
|
40
|
+
// ============================================================================
|
|
41
|
+
|
|
42
|
+
import { cacheManager } from "../infrastructure/cache/cache-manager";
|
|
43
|
+
|
|
44
|
+
// ... (keep PERMISSIONS_TTL_MS logic if needed, but CacheManager uses seconds usually? No, CacheManager options usually take seconds?
|
|
45
|
+
// MemoryCache implementation (checked in step 448) uses seconds for TTL in constructor?
|
|
46
|
+
// L20: this.defaultTtl = options.ttl || 3600;
|
|
47
|
+
// L74: expireAt: ttlSeconds > 0 ? Date.now() + ttlSeconds * 1000 : null
|
|
48
|
+
// So CacheManager expects SECONDS.
|
|
49
|
+
// PERMISSIONS_TTL_MS is in MS (5 * 60 * 1000).
|
|
50
|
+
// So I should pass 5 * 60 (300) seconds.
|
|
51
|
+
|
|
52
|
+
const DEFAULT_PERMISSIONS_CACHE_TTL_SECONDS = 5 * 60; // 5 minutes
|
|
53
|
+
|
|
54
|
+
const PERMISSIONS_TTL_SECONDS = (() => {
|
|
55
|
+
const ttlEnv = process.env.PERMISSIONS_CACHE_TTL_MS; // Metric used MS in env
|
|
56
|
+
if (!ttlEnv) {
|
|
57
|
+
return DEFAULT_PERMISSIONS_CACHE_TTL_SECONDS;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const parsedTtlMs = Number.parseInt(ttlEnv, 10);
|
|
61
|
+
if (Number.isNaN(parsedTtlMs) || parsedTtlMs <= 0) {
|
|
62
|
+
logger.warn(
|
|
63
|
+
`Invalid PERMISSIONS_CACHE_TTL_MS="${ttlEnv}". Falling back to default ${DEFAULT_PERMISSIONS_CACHE_TTL_SECONDS}s`,
|
|
64
|
+
);
|
|
65
|
+
return DEFAULT_PERMISSIONS_CACHE_TTL_SECONDS;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return Math.floor(parsedTtlMs / 1000);
|
|
69
|
+
})();
|
|
70
|
+
|
|
71
|
+
// Create cache instance
|
|
72
|
+
const permissionCache = cacheManager.create({
|
|
73
|
+
name: "rbac-permissions",
|
|
74
|
+
ttl: PERMISSIONS_TTL_SECONDS,
|
|
75
|
+
max: 10000,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
async function getFromCache(userId: string): Promise<Permission[] | undefined> {
|
|
79
|
+
return permissionCache.get<Permission[]>(userId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function setToCache(userId: string, data: Permission[]) {
|
|
83
|
+
// Use default TTL configured in cache instance
|
|
84
|
+
await permissionCache.set(userId, data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const pendingQueries = new Map<string, Promise<Permission[]>>();
|
|
88
|
+
|
|
89
|
+
export async function clearAllPermissionsCache() {
|
|
90
|
+
await permissionCache.reset();
|
|
91
|
+
pendingQueries.clear();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function clearUserPermissionsCache(userId: string) {
|
|
95
|
+
await permissionCache.del(userId);
|
|
96
|
+
pendingQueries.delete(userId);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function invalidateUserPermissions(userId: string) {
|
|
100
|
+
await permissionCache.del(userId);
|
|
101
|
+
pendingQueries.delete(userId);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export { getFromCache, setToCache, pendingQueries };
|
|
105
|
+
|
|
106
|
+
// ============================================================================
|
|
107
|
+
// Permissions
|
|
108
|
+
// ============================================================================
|
|
109
|
+
|
|
110
|
+
interface ExtendedUser {
|
|
111
|
+
id: string;
|
|
112
|
+
name?: string | null;
|
|
113
|
+
email?: string | null;
|
|
114
|
+
image?: string | null;
|
|
115
|
+
roles?: string[];
|
|
116
|
+
permissions?: Permission[];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function getUserPermissions(session: Session | null): Permission[] {
|
|
120
|
+
if (!session?.user) return [];
|
|
121
|
+
const user = session.user as ExtendedUser;
|
|
122
|
+
|
|
123
|
+
if (!user.permissions) {
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
return user.permissions;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const BYPASS_AUTH =
|
|
130
|
+
process.env.BYPASS_AUTH === "true" || process.env.BYPASS_AUTH === "1";
|
|
131
|
+
|
|
132
|
+
const ADMIN_ROLE_CODES = ["admin", "SUPER_ADMIN"];
|
|
133
|
+
|
|
134
|
+
export function getCrudPermissionsFromSession(
|
|
135
|
+
session: Session | null,
|
|
136
|
+
entity: string,
|
|
137
|
+
): {
|
|
138
|
+
create: boolean;
|
|
139
|
+
view: boolean;
|
|
140
|
+
update: boolean;
|
|
141
|
+
delete: boolean;
|
|
142
|
+
export: boolean;
|
|
143
|
+
import: boolean;
|
|
144
|
+
approve: boolean;
|
|
145
|
+
reject: boolean;
|
|
146
|
+
} {
|
|
147
|
+
if (BYPASS_AUTH) {
|
|
148
|
+
return {
|
|
149
|
+
create: true,
|
|
150
|
+
view: true,
|
|
151
|
+
update: true,
|
|
152
|
+
delete: true,
|
|
153
|
+
export: true,
|
|
154
|
+
import: true,
|
|
155
|
+
approve: true,
|
|
156
|
+
reject: true,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (!session?.user) {
|
|
161
|
+
return {
|
|
162
|
+
create: false,
|
|
163
|
+
view: false,
|
|
164
|
+
update: false,
|
|
165
|
+
delete: false,
|
|
166
|
+
export: false,
|
|
167
|
+
import: false,
|
|
168
|
+
approve: false,
|
|
169
|
+
reject: false,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const user = session.user as ExtendedUser;
|
|
174
|
+
|
|
175
|
+
if (!user.id) {
|
|
176
|
+
return {
|
|
177
|
+
create: false,
|
|
178
|
+
view: false,
|
|
179
|
+
update: false,
|
|
180
|
+
delete: false,
|
|
181
|
+
export: false,
|
|
182
|
+
import: false,
|
|
183
|
+
approve: false,
|
|
184
|
+
reject: false,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const isAdmin = user.roles?.some((role) => ADMIN_ROLE_CODES.includes(role));
|
|
189
|
+
if (isAdmin) {
|
|
190
|
+
return {
|
|
191
|
+
create: true,
|
|
192
|
+
view: true,
|
|
193
|
+
update: true,
|
|
194
|
+
delete: true,
|
|
195
|
+
export: true,
|
|
196
|
+
import: true,
|
|
197
|
+
approve: true,
|
|
198
|
+
reject: true,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const permissions = user.permissions || [];
|
|
203
|
+
const permissionKeys = new Set(
|
|
204
|
+
permissions.map((p) => `${p.resourceCode}:${p.actionCode}`),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const hasPermission = (action: string) => {
|
|
208
|
+
const key = `${entity}:${action}`;
|
|
209
|
+
return permissionKeys.has(key);
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
create: hasPermission(getActionCode("create")),
|
|
214
|
+
view: hasPermission(getActionCode("view")),
|
|
215
|
+
update: hasPermission(getActionCode("update")),
|
|
216
|
+
delete: hasPermission(getActionCode("delete")),
|
|
217
|
+
export: hasPermission(getActionCode("export")),
|
|
218
|
+
import: hasPermission(getActionCode("import")),
|
|
219
|
+
approve: hasPermission(getActionCode("approve")),
|
|
220
|
+
reject: hasPermission(getActionCode("reject")),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function checkPermission(
|
|
225
|
+
session: Session | null,
|
|
226
|
+
resourceCode: string,
|
|
227
|
+
actionCode: string,
|
|
228
|
+
): boolean {
|
|
229
|
+
if (BYPASS_AUTH) {
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!session?.user) return false;
|
|
234
|
+
const user = session.user as ExtendedUser;
|
|
235
|
+
|
|
236
|
+
if (!user.permissions || user.permissions.length === 0) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (user.roles?.some((role) => ADMIN_ROLE_CODES.includes(role))) {
|
|
241
|
+
return true;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const permissionKey = `${resourceCode}:${actionCode}`;
|
|
245
|
+
return user.permissions.some(
|
|
246
|
+
(p) => p.resourceCode === resourceCode && p.actionCode === actionCode,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function hasPermission(
|
|
251
|
+
session: Session | null,
|
|
252
|
+
resourceCode: string,
|
|
253
|
+
actionCode: string,
|
|
254
|
+
): boolean {
|
|
255
|
+
return checkPermission(session, resourceCode, actionCode);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function requirePermission(
|
|
259
|
+
session: Session | null,
|
|
260
|
+
resourceCode: string,
|
|
261
|
+
actionCode: string,
|
|
262
|
+
): void {
|
|
263
|
+
if (!checkPermission(session, resourceCode, actionCode)) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Unauthorized: User does not have permission ${actionCode} on ${resourceCode}`,
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function hasRole(session: Session | null, roleCode: string): boolean {
|
|
271
|
+
if (!session?.user) return false;
|
|
272
|
+
const user = session.user as ExtendedUser;
|
|
273
|
+
|
|
274
|
+
if (!user.roles) {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
return user.roles.includes(roleCode);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function hasAnyRole(
|
|
281
|
+
session: Session | null,
|
|
282
|
+
roleCodes: string[],
|
|
283
|
+
): boolean {
|
|
284
|
+
if (!session?.user) return false;
|
|
285
|
+
const user = session.user as ExtendedUser;
|
|
286
|
+
|
|
287
|
+
if (!user.roles) {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
return roleCodes.some((role) => user.roles!.includes(role));
|
|
291
|
+
}
|
|
292
|
+
export * from "./permission-service";
|
|
293
|
+
export * from "./role-service";
|
|
294
|
+
export * from "./resource-service";
|
|
295
|
+
export * from "./resource-validator";
|
|
296
|
+
export { RoleListPage } from "./pages/role-list-page";
|
|
297
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Role } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Helper function to get permission description
|
|
5
|
+
*/
|
|
6
|
+
export function getPermissionDescription(permission: string): string {
|
|
7
|
+
const descriptions: Record<string, string> = {
|
|
8
|
+
"dashboard:view": "Xem bảng điều khiển",
|
|
9
|
+
"users:view": "Xem danh sách người dùng",
|
|
10
|
+
"users:create": "Tạo người dùng mới",
|
|
11
|
+
"users:update": "Cập nhật thông tin người dùng",
|
|
12
|
+
"users:delete": "Xóa người dùng",
|
|
13
|
+
"roles:view": "Xem danh sách vai trò",
|
|
14
|
+
"roles:create": "Tạo vai trò mới",
|
|
15
|
+
"roles:update": "Cập nhật vai trò",
|
|
16
|
+
"roles:delete": "Xóa vai trò",
|
|
17
|
+
"reports:view": "Xem báo cáo",
|
|
18
|
+
"reconciliation:view": "Xem đối soát",
|
|
19
|
+
"reconciliation:manage": "Quản lý đối soát",
|
|
20
|
+
"payment:approve": "Duyệt thanh toán",
|
|
21
|
+
"supplier-pricing:view": "Xem giá nhà cung cấp",
|
|
22
|
+
"supplier-pricing:approve": "Duyệt giá nhà cung cấp",
|
|
23
|
+
"purchase-request:view": "Xem yêu cầu mua hàng",
|
|
24
|
+
"purchase-request:create": "Tạo yêu cầu mua hàng",
|
|
25
|
+
"purchase-request:update": "Cập nhật yêu cầu mua hàng",
|
|
26
|
+
"purchase-request:approve": "Duyệt yêu cầu mua hàng",
|
|
27
|
+
"purchase-order:view": "Xem đơn hàng mua",
|
|
28
|
+
"purchase-order:create": "Tạo đơn hàng mua",
|
|
29
|
+
"purchase-order:update": "Cập nhật đơn hàng mua",
|
|
30
|
+
"purchase-order:approve": "Duyệt đơn hàng mua",
|
|
31
|
+
"supplier:view": "Xem nhà cung cấp",
|
|
32
|
+
"supplier-pricing:update": "Cập nhật giá nhà cung cấp",
|
|
33
|
+
"material:view": "Xem nguyên vật liệu",
|
|
34
|
+
"material-category:view": "Xem nhóm nguyên vật liệu",
|
|
35
|
+
"warehouse:view": "Xem kho",
|
|
36
|
+
"goods-receipt:view": "Xem phiếu nhập kho",
|
|
37
|
+
"goods-receipt:create": "Tạo phiếu nhập kho",
|
|
38
|
+
"goods-receipt:update": "Cập nhật phiếu nhập kho",
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return descriptions[permission] || `Quyền ${permission}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get users who have a specific permission
|
|
46
|
+
*/
|
|
47
|
+
export function getUsersWithPermission(
|
|
48
|
+
permission: string,
|
|
49
|
+
roles: Role[],
|
|
50
|
+
): Array<{ id: string; [key: string]: any }> {
|
|
51
|
+
const usersWithPermission: Array<{ id: string; [key: string]: any }> = [];
|
|
52
|
+
|
|
53
|
+
roles.forEach((role) => {
|
|
54
|
+
if (role.permissions.includes(permission)) {
|
|
55
|
+
usersWithPermission.push(...role.users);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Remove duplicates by id
|
|
60
|
+
return Array.from(
|
|
61
|
+
new Map(usersWithPermission.map((u) => [u.id, u])).values(),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EntityCrudPage } from "../../crud/pages/entity-crud-page";
|
|
2
|
+
|
|
3
|
+
interface ActionListPageProps {
|
|
4
|
+
lang: string;
|
|
5
|
+
session: any;
|
|
6
|
+
dictionary: any;
|
|
7
|
+
config: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ActionListPage({
|
|
11
|
+
lang,
|
|
12
|
+
session,
|
|
13
|
+
dictionary,
|
|
14
|
+
config,
|
|
15
|
+
}: ActionListPageProps) {
|
|
16
|
+
return (
|
|
17
|
+
<EntityCrudPage
|
|
18
|
+
entity="actions"
|
|
19
|
+
lang={lang}
|
|
20
|
+
session={session}
|
|
21
|
+
dictionary={dictionary}
|
|
22
|
+
config={config}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { EntityCrudPage } from "../../crud/pages/entity-crud-page";
|
|
2
|
+
|
|
3
|
+
interface ResourceListPageProps {
|
|
4
|
+
lang: string;
|
|
5
|
+
session: any;
|
|
6
|
+
dictionary: any;
|
|
7
|
+
config: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ResourceListPage({
|
|
11
|
+
lang,
|
|
12
|
+
session,
|
|
13
|
+
dictionary,
|
|
14
|
+
config,
|
|
15
|
+
}: ResourceListPageProps) {
|
|
16
|
+
return (
|
|
17
|
+
<EntityCrudPage
|
|
18
|
+
entity="resources"
|
|
19
|
+
lang={lang}
|
|
20
|
+
session={session}
|
|
21
|
+
dictionary={dictionary}
|
|
22
|
+
config={config}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
}
|