@carlonicora/nextjs-jsonapi 1.0.3 → 1.0.4
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/atoms/index.ts +1 -0
- package/src/atoms/recentPagesAtom.ts +10 -0
- package/src/client/context/JsonApiContext.ts +61 -0
- package/src/client/context/JsonApiProvider.tsx +27 -0
- package/src/client/context/index.ts +2 -0
- package/src/client/hooks/index.ts +3 -0
- package/src/client/hooks/useJsonApiGet.ts +188 -0
- package/src/client/hooks/useJsonApiMutation.ts +193 -0
- package/src/client/hooks/useRehydration.ts +47 -0
- package/src/client/index.ts +11 -0
- package/src/client/request.ts +97 -0
- package/src/client/token.ts +10 -0
- package/src/components/containers/PageContainer.tsx +15 -0
- package/src/components/containers/ReactMarkdownContainer.tsx +119 -0
- package/src/components/containers/TabsContainer.tsx +93 -0
- package/src/components/containers/index.ts +3 -0
- package/src/components/contents/AttributeElement.tsx +20 -0
- package/src/components/contents/index.ts +1 -0
- package/src/components/details/AllowedUsersDetails.tsx +23 -0
- package/src/components/details/index.ts +1 -0
- package/src/components/editors/BlockNoteDiffInlineContent.tsx +152 -0
- package/src/components/editors/BlockNoteEditor.tsx +404 -0
- package/src/components/editors/BlockNoteEditorContainer.tsx +13 -0
- package/src/components/editors/BlockNoteEditorFormattingToolbar.tsx +38 -0
- package/src/components/editors/index.ts +1 -0
- package/src/components/errors/ErrorDetails.tsx +41 -0
- package/src/components/errors/errorToast.ts +9 -0
- package/src/components/errors/index.ts +2 -0
- package/src/components/forms/CommonAssociationForm.tsx +162 -0
- package/src/components/forms/CommonDeleter.tsx +94 -0
- package/src/components/forms/CommonEditorButtons.tsx +30 -0
- package/src/components/forms/CommonEditorHeader.tsx +35 -0
- package/src/components/forms/CommonEditorTrigger.tsx +26 -0
- package/src/components/forms/DatePickerPopover.tsx +219 -0
- package/src/components/forms/DateRangeSelector.tsx +110 -0
- package/src/components/forms/FileUploader.tsx +324 -0
- package/src/components/forms/FormCheckbox.tsx +66 -0
- package/src/components/forms/FormContainerGeneric.tsx +39 -0
- package/src/components/forms/FormDate.tsx +247 -0
- package/src/components/forms/FormDateTime.tsx +231 -0
- package/src/components/forms/FormInput.tsx +110 -0
- package/src/components/forms/FormPassword.tsx +54 -0
- package/src/components/forms/FormPlaceAutocomplete.tsx +286 -0
- package/src/components/forms/FormSelect.tsx +72 -0
- package/src/components/forms/FormSlider.tsx +51 -0
- package/src/components/forms/FormSwitch.tsx +25 -0
- package/src/components/forms/FormTextarea.tsx +44 -0
- package/src/components/forms/MultiFileUploader.tsx +107 -0
- package/src/components/forms/PasswordInput.tsx +47 -0
- package/src/components/forms/index.ts +21 -0
- package/src/components/index.ts +11 -0
- package/src/components/navigations/Breadcrumb.tsx +83 -0
- package/src/components/navigations/ContentTitle.tsx +39 -0
- package/src/components/navigations/Header.tsx +27 -0
- package/src/components/navigations/ModeToggleSwitch.tsx +25 -0
- package/src/components/navigations/PageSection.tsx +64 -0
- package/src/components/navigations/RecentPagesNavigator.tsx +52 -0
- package/src/components/navigations/index.ts +6 -0
- package/src/components/pages/PageContainerContentDetails.tsx +76 -0
- package/src/components/pages/PageContentContainer.tsx +31 -0
- package/src/components/pages/index.ts +2 -0
- package/src/components/tables/ContentListTable.tsx +165 -0
- package/src/components/tables/ContentTableSearch.tsx +105 -0
- package/src/components/tables/cells/cell.component.tsx +18 -0
- package/src/components/tables/cells/cell.date.tsx +16 -0
- package/src/components/tables/cells/cell.id.tsx +27 -0
- package/src/components/tables/cells/cell.link.tsx +18 -0
- package/src/components/tables/cells/cell.text.tsx +12 -0
- package/src/components/tables/cells/cell.url.tsx +13 -0
- package/src/components/tables/cells/index.ts +5 -0
- package/src/components/tables/index.ts +3 -0
- package/src/contexts/SharedContext.tsx +35 -0
- package/src/contexts/index.ts +2 -0
- package/src/core/abstracts/AbstractApiData.ts +138 -0
- package/src/core/abstracts/AbstractService.ts +263 -0
- package/src/core/abstracts/index.ts +2 -0
- package/src/core/endpoint/EndpointCreator.ts +97 -0
- package/src/core/endpoint/index.ts +1 -0
- package/src/core/factories/JsonApiDataFactory.ts +12 -0
- package/src/core/factories/RehydrationFactory.ts +30 -0
- package/src/core/factories/index.ts +2 -0
- package/src/core/fields/FieldSelector.ts +15 -0
- package/src/core/fields/index.ts +1 -0
- package/src/core/index.ts +20 -0
- package/src/core/interfaces/ApiData.ts +8 -0
- package/src/core/interfaces/ApiDataInterface.ts +15 -0
- package/src/core/interfaces/ApiRequestDataTypeInterface.ts +14 -0
- package/src/core/interfaces/ApiResponseInterface.ts +17 -0
- package/src/core/interfaces/JsonApiHydratedDataInterface.ts +5 -0
- package/src/core/interfaces/index.ts +5 -0
- package/src/core/registry/DataClassRegistry.ts +51 -0
- package/src/core/registry/ModuleRegistrar.ts +43 -0
- package/src/core/registry/ModuleRegistry.ts +64 -0
- package/src/core/registry/index.ts +3 -0
- package/src/core/utils/index.ts +2 -0
- package/src/core/utils/rehydrate.ts +24 -0
- package/src/core/utils/translateResponse.ts +125 -0
- package/src/features/auth/auth.module.ts +9 -0
- package/src/features/auth/config.ts +57 -0
- package/src/features/auth/data/auth.interface.ts +31 -0
- package/src/features/auth/data/auth.service.ts +159 -0
- package/src/features/auth/data/auth.ts +54 -0
- package/src/features/auth/data/index.ts +3 -0
- package/src/features/auth/index.ts +3 -0
- package/src/features/company/company.module.ts +10 -0
- package/src/features/company/data/company.fields.ts +6 -0
- package/src/features/company/data/company.interface.ts +28 -0
- package/src/features/company/data/company.service.ts +73 -0
- package/src/features/company/data/company.ts +104 -0
- package/src/features/company/data/index.ts +4 -0
- package/src/features/company/index.ts +2 -0
- package/src/features/content/content.module.ts +20 -0
- package/src/features/content/data/content.fields.ts +13 -0
- package/src/features/content/data/content.interface.ts +23 -0
- package/src/features/content/data/content.service.ts +75 -0
- package/src/features/content/data/content.ts +85 -0
- package/src/features/content/data/index.ts +4 -0
- package/src/features/content/index.ts +2 -0
- package/src/features/feature/components/forms/FormFeatures.tsx +149 -0
- package/src/features/feature/components/index.ts +1 -0
- package/src/features/feature/data/feature.interface.ts +9 -0
- package/src/features/feature/data/feature.service.ts +19 -0
- package/src/features/feature/data/feature.ts +33 -0
- package/src/features/feature/data/index.ts +3 -0
- package/src/features/feature/feature.module.ts +10 -0
- package/src/features/feature/index.ts +3 -0
- package/src/features/index.ts +12 -0
- package/src/features/module/data/index.ts +2 -0
- package/src/features/module/data/module.interface.ts +12 -0
- package/src/features/module/data/module.ts +42 -0
- package/src/features/module/index.ts +2 -0
- package/src/features/module/module.module.ts +10 -0
- package/src/features/notification/data/index.ts +4 -0
- package/src/features/notification/data/notification.fields.ts +8 -0
- package/src/features/notification/data/notification.interface.ts +14 -0
- package/src/features/notification/data/notification.service.ts +34 -0
- package/src/features/notification/data/notification.ts +51 -0
- package/src/features/notification/index.ts +2 -0
- package/src/features/notification/notification.module.ts +10 -0
- package/src/features/push/data/index.ts +3 -0
- package/src/features/push/data/push.interface.ts +8 -0
- package/src/features/push/data/push.service.ts +17 -0
- package/src/features/push/data/push.ts +18 -0
- package/src/features/push/index.ts +2 -0
- package/src/features/push/push.module.ts +10 -0
- package/src/features/role/data/index.ts +4 -0
- package/src/features/role/data/role.fields.ts +8 -0
- package/src/features/role/data/role.interface.ts +16 -0
- package/src/features/role/data/role.service.ts +117 -0
- package/src/features/role/data/role.ts +62 -0
- package/src/features/role/index.ts +2 -0
- package/src/features/role/role.module.ts +10 -0
- package/src/features/s3/data/index.ts +3 -0
- package/src/features/s3/data/s3.interface.ts +11 -0
- package/src/features/s3/data/s3.service.ts +30 -0
- package/src/features/s3/data/s3.ts +60 -0
- package/src/features/s3/index.ts +2 -0
- package/src/features/s3/s3.module.ts +10 -0
- package/src/features/search/index.ts +1 -0
- package/src/features/search/interfaces/index.ts +1 -0
- package/src/features/search/interfaces/search.result.interface.ts +3 -0
- package/src/features/user/author.module.ts +10 -0
- package/src/features/user/components/index.ts +2 -0
- package/src/features/user/components/lists/ContributorsList.tsx +41 -0
- package/src/features/user/components/lists/index.ts +1 -0
- package/src/features/user/components/widgets/UserAvatar.tsx +86 -0
- package/src/features/user/components/widgets/index.ts +1 -0
- package/src/features/user/contexts/CurrentUserContext.tsx +156 -0
- package/src/features/user/contexts/index.ts +1 -0
- package/src/features/user/data/index.ts +4 -0
- package/src/features/user/data/user.fields.ts +8 -0
- package/src/features/user/data/user.interface.ts +41 -0
- package/src/features/user/data/user.service.ts +246 -0
- package/src/features/user/data/user.ts +162 -0
- package/src/features/user/index.ts +4 -0
- package/src/features/user/user.module.ts +21 -0
- package/src/hooks/TableGeneratorRegistry.ts +53 -0
- package/src/hooks/index.ts +33 -0
- package/src/hooks/types.ts +35 -0
- package/src/hooks/url.rewriter.ts +22 -0
- package/src/hooks/useCustomD3Graph.tsx +705 -0
- package/src/hooks/useDataListRetriever.ts +349 -0
- package/src/hooks/useDebounce.ts +33 -0
- package/src/hooks/usePageUrlGenerator.ts +50 -0
- package/src/hooks/useTableGenerator.ts +16 -0
- package/src/i18n/config.ts +73 -0
- package/src/i18n/index.ts +18 -0
- package/src/index.ts +16 -0
- package/src/interfaces/breadcrumb.item.data.interface.ts +4 -0
- package/src/interfaces/d3.link.interface.ts +7 -0
- package/src/interfaces/d3.node.interface.ts +12 -0
- package/src/interfaces/index.ts +3 -0
- package/src/permissions/check.ts +127 -0
- package/src/permissions/index.ts +2 -0
- package/src/permissions/types.ts +109 -0
- package/src/roles/config.ts +46 -0
- package/src/roles/index.ts +1 -0
- package/src/server/cache.ts +28 -0
- package/src/server/index.ts +3 -0
- package/src/server/request.ts +113 -0
- package/src/server/token.ts +10 -0
- package/src/shadcnui/custom/kanban.tsx +1001 -0
- package/src/shadcnui/custom/link.tsx +18 -0
- package/src/shadcnui/custom/multi-select.tsx +382 -0
- package/src/shadcnui/index.ts +49 -0
- package/src/shadcnui/ui/accordion.tsx +52 -0
- package/src/shadcnui/ui/alert-dialog.tsx +141 -0
- package/src/shadcnui/ui/alert.tsx +43 -0
- package/src/shadcnui/ui/avatar.tsx +50 -0
- package/src/shadcnui/ui/badge.tsx +40 -0
- package/src/shadcnui/ui/breadcrumb.tsx +115 -0
- package/src/shadcnui/ui/button.tsx +51 -0
- package/src/shadcnui/ui/calendar.tsx +73 -0
- package/src/shadcnui/ui/card.tsx +43 -0
- package/src/shadcnui/ui/carousel.tsx +225 -0
- package/src/shadcnui/ui/chart.tsx +320 -0
- package/src/shadcnui/ui/checkbox.tsx +29 -0
- package/src/shadcnui/ui/collapsible.tsx +11 -0
- package/src/shadcnui/ui/command.tsx +155 -0
- package/src/shadcnui/ui/context-menu.tsx +179 -0
- package/src/shadcnui/ui/dialog.tsx +96 -0
- package/src/shadcnui/ui/drawer.tsx +89 -0
- package/src/shadcnui/ui/dropdown-menu.tsx +205 -0
- package/src/shadcnui/ui/form.tsx +138 -0
- package/src/shadcnui/ui/hover-card.tsx +29 -0
- package/src/shadcnui/ui/input.tsx +21 -0
- package/src/shadcnui/ui/label.tsx +26 -0
- package/src/shadcnui/ui/navigation-menu.tsx +168 -0
- package/src/shadcnui/ui/popover.tsx +33 -0
- package/src/shadcnui/ui/progress.tsx +25 -0
- package/src/shadcnui/ui/radio-group.tsx +37 -0
- package/src/shadcnui/ui/resizable.tsx +47 -0
- package/src/shadcnui/ui/scroll-area.tsx +40 -0
- package/src/shadcnui/ui/select.tsx +164 -0
- package/src/shadcnui/ui/separator.tsx +28 -0
- package/src/shadcnui/ui/sheet.tsx +139 -0
- package/src/shadcnui/ui/sidebar.tsx +677 -0
- package/src/shadcnui/ui/skeleton.tsx +13 -0
- package/src/shadcnui/ui/slider.tsx +25 -0
- package/src/shadcnui/ui/sonner.tsx +25 -0
- package/src/shadcnui/ui/switch.tsx +31 -0
- package/src/shadcnui/ui/table.tsx +120 -0
- package/src/shadcnui/ui/tabs.tsx +55 -0
- package/src/shadcnui/ui/textarea.tsx +24 -0
- package/src/shadcnui/ui/toggle.tsx +39 -0
- package/src/shadcnui/ui/tooltip.tsx +61 -0
- package/src/unified/JsonApiRequest.ts +325 -0
- package/src/unified/index.ts +1 -0
- package/src/utils/blocknote-diff.util.ts +815 -0
- package/src/utils/blocknote-word-diff-renderer.util.ts +413 -0
- package/src/utils/cn.ts +6 -0
- package/src/utils/compose-refs.ts +61 -0
- package/src/utils/date-formatter.ts +53 -0
- package/src/utils/exists.ts +7 -0
- package/src/utils/index.ts +15 -0
- package/src/utils/schemas/entity.object.schema.ts +8 -0
- package/src/utils/schemas/index.ts +2 -0
- package/src/utils/schemas/user.object.schema.ts +9 -0
- package/src/utils/table-options.ts +67 -0
- package/src/utils/use-mobile.tsx +21 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { Action, ModuleWithPermissions, PermissionModule, PermissionUser } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Check if a user has permission to perform an action on a module.
|
|
5
|
+
*
|
|
6
|
+
* @param module - The module to check permissions for
|
|
7
|
+
* @param action - The action to check (read, create, update, delete)
|
|
8
|
+
* @param user - The user with their modules and permissions
|
|
9
|
+
* @param data - Optional data object for path-based permission checks
|
|
10
|
+
*/
|
|
11
|
+
export function checkPermissions<T extends PermissionUser>(params: {
|
|
12
|
+
module: ModuleWithPermissions;
|
|
13
|
+
action: Action;
|
|
14
|
+
user: T;
|
|
15
|
+
data?: any;
|
|
16
|
+
}): boolean {
|
|
17
|
+
const selectedModule = params.user.modules.find((module: PermissionModule) => module.id === params.module.moduleId);
|
|
18
|
+
|
|
19
|
+
if (!selectedModule) return false;
|
|
20
|
+
const permissionConfig = selectedModule.permissions[params.action];
|
|
21
|
+
|
|
22
|
+
if (!permissionConfig) return false;
|
|
23
|
+
if (typeof permissionConfig === "boolean") return permissionConfig as boolean;
|
|
24
|
+
|
|
25
|
+
if (!params.data) return true;
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const singlePermissionConfig = permissionConfig.split("|").map((p) => p.trim());
|
|
29
|
+
|
|
30
|
+
for (const path of singlePermissionConfig) {
|
|
31
|
+
if (getValueFromPath(params.data, path, params.user.id)) return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
} catch {
|
|
35
|
+
if (typeof permissionConfig === "string") {
|
|
36
|
+
return getValueFromPath(params.data, permissionConfig, params.user.id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Check permissions from server context where user object is not fully available.
|
|
45
|
+
*
|
|
46
|
+
* @param module - The module to check permissions for
|
|
47
|
+
* @param action - The action to check
|
|
48
|
+
* @param userId - The user's ID
|
|
49
|
+
* @param selectedModule - The selected module with its permissions
|
|
50
|
+
* @param data - Optional data object for path-based permission checks
|
|
51
|
+
*/
|
|
52
|
+
export function checkPermissionsFromServer(params: {
|
|
53
|
+
module: ModuleWithPermissions;
|
|
54
|
+
action: Action;
|
|
55
|
+
userId: string;
|
|
56
|
+
selectedModule?: PermissionModule;
|
|
57
|
+
data?: any;
|
|
58
|
+
}): boolean {
|
|
59
|
+
if (!params.selectedModule) return false;
|
|
60
|
+
const permissionConfig = params.selectedModule.permissions[params.action];
|
|
61
|
+
|
|
62
|
+
if (!permissionConfig) return false;
|
|
63
|
+
if (typeof permissionConfig === "boolean") return permissionConfig as boolean;
|
|
64
|
+
|
|
65
|
+
if (!params.data) return true;
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const singlePermissionConfig = permissionConfig.split("|").map((p) => p.trim());
|
|
69
|
+
|
|
70
|
+
for (const path of singlePermissionConfig) {
|
|
71
|
+
if (getValueFromPath(params.data, path, params.userId)) return true;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
} catch {
|
|
75
|
+
if (typeof permissionConfig === "string") {
|
|
76
|
+
return getValueFromPath(params.data, permissionConfig, params.userId);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Traverse an object path and check if the value matches the user ID.
|
|
85
|
+
* Handles nested objects, arrays, and various data structures.
|
|
86
|
+
*/
|
|
87
|
+
export function getValueFromPath(obj: any, path: string, userId: string): any {
|
|
88
|
+
const parts = path.split(".");
|
|
89
|
+
let current = obj;
|
|
90
|
+
|
|
91
|
+
for (const part of parts) {
|
|
92
|
+
if (!current) return false;
|
|
93
|
+
|
|
94
|
+
if (Array.isArray(current)) {
|
|
95
|
+
let found = false;
|
|
96
|
+
for (const item of current) {
|
|
97
|
+
const result = getValueFromPath(item, parts.slice(parts.indexOf(part)).join("."), userId);
|
|
98
|
+
if (result === userId || result === true) {
|
|
99
|
+
found = true;
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return found;
|
|
104
|
+
} else if (current[part] !== undefined) {
|
|
105
|
+
current = current[part];
|
|
106
|
+
} else {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (Array.isArray(current)) {
|
|
112
|
+
// If final value is an array, check if any element has id matching userId
|
|
113
|
+
return current.some((item: any) => {
|
|
114
|
+
if (typeof item === "object" && item.id !== undefined) {
|
|
115
|
+
return item.id.toString() === userId;
|
|
116
|
+
}
|
|
117
|
+
return item.toString() === userId;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Direct comparison for primitive values or objects with id
|
|
122
|
+
if (typeof current === "object" && current.id !== undefined) {
|
|
123
|
+
return current.id.toString() === userId;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return current.toString() === userId;
|
|
127
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { ApiRequestDataTypeInterface } from "../core/interfaces/ApiRequestDataTypeInterface";
|
|
2
|
+
import { FieldSelector } from "../core/fields/FieldSelector";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Permission actions
|
|
6
|
+
*/
|
|
7
|
+
export enum Action {
|
|
8
|
+
Read = "read",
|
|
9
|
+
Create = "create",
|
|
10
|
+
Update = "update",
|
|
11
|
+
Delete = "delete",
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generic permission check type.
|
|
16
|
+
* Can be a boolean or a function that checks permissions dynamically.
|
|
17
|
+
* @template T - The data type being checked
|
|
18
|
+
* @template U - The user type (defaults to PermissionUser)
|
|
19
|
+
*/
|
|
20
|
+
export type PermissionCheck<T, U = PermissionUser> = boolean | ((user?: U | string, data?: T) => boolean);
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Page URL configuration for modules
|
|
24
|
+
*/
|
|
25
|
+
export type PageUrl = {
|
|
26
|
+
pageUrl?: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Module permission definition wrapper
|
|
31
|
+
*/
|
|
32
|
+
export type ModulePermissionDefinition<T> = {
|
|
33
|
+
interface: T;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Base module definition
|
|
38
|
+
*/
|
|
39
|
+
export type ModuleDefinition = {
|
|
40
|
+
pageUrl?: string;
|
|
41
|
+
name: string;
|
|
42
|
+
model: any;
|
|
43
|
+
feature?: string;
|
|
44
|
+
moduleId?: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Permission configuration for a module.
|
|
49
|
+
* Can be a boolean (allow/deny all) or a string path for dynamic checks.
|
|
50
|
+
*/
|
|
51
|
+
export interface PermissionConfig {
|
|
52
|
+
create: boolean | string;
|
|
53
|
+
read: boolean | string;
|
|
54
|
+
update: boolean | string;
|
|
55
|
+
delete: boolean | string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generic interface for a module that has permissions.
|
|
60
|
+
* Apps should ensure their Module class implements this.
|
|
61
|
+
*/
|
|
62
|
+
export interface PermissionModule {
|
|
63
|
+
id: string;
|
|
64
|
+
permissions: PermissionConfig;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Generic interface for a user that has modules with permissions.
|
|
69
|
+
* Apps should ensure their User class implements this.
|
|
70
|
+
*/
|
|
71
|
+
export interface PermissionUser {
|
|
72
|
+
id: string;
|
|
73
|
+
modules: PermissionModule[];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Module definition with permissions - extends ApiRequestDataTypeInterface
|
|
78
|
+
*/
|
|
79
|
+
export type ModuleWithPermissions = ApiRequestDataTypeInterface & {
|
|
80
|
+
pageUrl?: string;
|
|
81
|
+
feature?: string;
|
|
82
|
+
moduleId?: string;
|
|
83
|
+
inclusions?: Record<
|
|
84
|
+
string,
|
|
85
|
+
{
|
|
86
|
+
types?: string[];
|
|
87
|
+
fields?: FieldSelector<any>[];
|
|
88
|
+
}
|
|
89
|
+
>;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Factory type for creating module definitions
|
|
94
|
+
*/
|
|
95
|
+
export type ModuleFactory = (params: {
|
|
96
|
+
pageUrl?: string;
|
|
97
|
+
name: string;
|
|
98
|
+
cache?: string | "days" | "default" | "hours" | "max" | "minutes" | "seconds" | "weeks";
|
|
99
|
+
model: any;
|
|
100
|
+
feature?: string;
|
|
101
|
+
moduleId?: string;
|
|
102
|
+
inclusions?: Record<
|
|
103
|
+
string,
|
|
104
|
+
{
|
|
105
|
+
types?: string[];
|
|
106
|
+
fields?: FieldSelector<any>[];
|
|
107
|
+
}
|
|
108
|
+
>;
|
|
109
|
+
}) => ModuleWithPermissions;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role ID configuration interface
|
|
3
|
+
* Apps provide their role IDs via configureRoles()
|
|
4
|
+
*/
|
|
5
|
+
export interface RoleIdConfig {
|
|
6
|
+
Administrator: string;
|
|
7
|
+
CompanyAdministrator: string;
|
|
8
|
+
[key: string]: string; // Allow additional roles
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Private storage for the injected role IDs
|
|
12
|
+
let _roleId: RoleIdConfig | null = null;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Configure role IDs for the library
|
|
16
|
+
* Call this at app startup to provide role ID constants
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* import { configureRoles } from "@carlonicora/nextjs-jsonapi";
|
|
21
|
+
* import { RoleId } from "@phlow/shared";
|
|
22
|
+
*
|
|
23
|
+
* configureRoles(RoleId);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export function configureRoles(roleId: RoleIdConfig): void {
|
|
27
|
+
_roleId = roleId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get configured role IDs
|
|
32
|
+
* @throws Error if roles not configured
|
|
33
|
+
*/
|
|
34
|
+
export function getRoleId(): RoleIdConfig {
|
|
35
|
+
if (!_roleId) {
|
|
36
|
+
throw new Error("Roles not configured. Call configureRoles() at app startup.");
|
|
37
|
+
}
|
|
38
|
+
return _roleId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Check if roles have been configured
|
|
43
|
+
*/
|
|
44
|
+
export function isRolesConfigured(): boolean {
|
|
45
|
+
return _roleId !== null;
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { configureRoles, getRoleId, isRolesConfigured, type RoleIdConfig } from "./config";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { revalidateTag } from "next/cache";
|
|
4
|
+
|
|
5
|
+
export type CacheProfile = "seconds" | "minutes" | "hours" | "days" | "weeks" | "max" | "default";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Revalidate a cache tag to invalidate cached data.
|
|
9
|
+
* Next.js 16+ requires a profile parameter.
|
|
10
|
+
*
|
|
11
|
+
* @param tag - The cache tag to invalidate
|
|
12
|
+
* @param profile - The cache profile (defaults to "max" for immediate invalidation)
|
|
13
|
+
*/
|
|
14
|
+
export async function invalidateCacheTag(tag: string, profile: CacheProfile = "max"): Promise<void> {
|
|
15
|
+
revalidateTag(tag, profile);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Revalidate multiple cache tags.
|
|
20
|
+
*
|
|
21
|
+
* @param tags - Array of cache tags to invalidate
|
|
22
|
+
* @param profile - The cache profile (defaults to "max" for immediate invalidation)
|
|
23
|
+
*/
|
|
24
|
+
export async function invalidateCacheTags(tags: string[], profile: CacheProfile = "max"): Promise<void> {
|
|
25
|
+
for (const tag of tags) {
|
|
26
|
+
revalidateTag(tag, profile);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { ApiData } from "../core/interfaces/ApiData";
|
|
4
|
+
import { cacheLife } from "next/dist/server/use-cache/cache-life";
|
|
5
|
+
import { cacheTag } from "next/dist/server/use-cache/cache-tag";
|
|
6
|
+
|
|
7
|
+
export interface ServerRequestParams {
|
|
8
|
+
method: string;
|
|
9
|
+
url: string;
|
|
10
|
+
token?: string;
|
|
11
|
+
cache?: string;
|
|
12
|
+
body?: any;
|
|
13
|
+
files?: { [key: string]: File | Blob } | File | Blob;
|
|
14
|
+
companyId?: string;
|
|
15
|
+
language: string;
|
|
16
|
+
additionalHeaders?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Server-side request with Next.js caching support.
|
|
21
|
+
* Uses "use cache" directive for automatic caching.
|
|
22
|
+
*/
|
|
23
|
+
export async function serverRequest(params: ServerRequestParams): Promise<ApiData> {
|
|
24
|
+
"use cache";
|
|
25
|
+
|
|
26
|
+
const response: ApiData = {
|
|
27
|
+
data: undefined,
|
|
28
|
+
ok: false,
|
|
29
|
+
status: 0,
|
|
30
|
+
statusText: "",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Apply caching configuration
|
|
34
|
+
if (params.cache) {
|
|
35
|
+
if (["days", "default", "hours", "max", "minutes", "seconds", "weeks"].includes(params.cache)) {
|
|
36
|
+
cacheLife(params.cache as any);
|
|
37
|
+
} else {
|
|
38
|
+
cacheTag(params.cache);
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
cacheLife("seconds");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const additionalHeaders: Record<string, string> = { ...params.additionalHeaders };
|
|
45
|
+
|
|
46
|
+
if (params.companyId) {
|
|
47
|
+
additionalHeaders["x-companyid"] = params.companyId;
|
|
48
|
+
}
|
|
49
|
+
additionalHeaders["x-language"] = params.language;
|
|
50
|
+
|
|
51
|
+
let requestBody: BodyInit | undefined = undefined;
|
|
52
|
+
|
|
53
|
+
if (params.files) {
|
|
54
|
+
const formData = new FormData();
|
|
55
|
+
if (params.body && typeof params.body === "object") {
|
|
56
|
+
for (const key in params.body) {
|
|
57
|
+
if (Object.prototype.hasOwnProperty.call(params.body, key)) {
|
|
58
|
+
formData.append(
|
|
59
|
+
key,
|
|
60
|
+
typeof params.body[key] === "object" ? JSON.stringify(params.body[key]) : params.body[key],
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (params.files instanceof Blob) {
|
|
67
|
+
formData.append("file", params.files);
|
|
68
|
+
} else if (typeof params.files === "object" && params.files !== null) {
|
|
69
|
+
for (const key in params.files) {
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(params.files, key)) {
|
|
71
|
+
formData.append(key, params.files[key]);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
requestBody = formData;
|
|
77
|
+
} else if (params.body !== undefined) {
|
|
78
|
+
requestBody = JSON.stringify(params.body);
|
|
79
|
+
additionalHeaders["Content-Type"] = "application/json";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const options: RequestInit = {
|
|
83
|
+
method: params.method,
|
|
84
|
+
headers: { Accept: "application/json", ...additionalHeaders },
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
if (requestBody !== undefined) {
|
|
88
|
+
options.body = requestBody;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (params.token) {
|
|
92
|
+
options.headers = { ...options.headers, Authorization: `Bearer ${params.token}` };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const apiResponse = await fetch(params.url, options);
|
|
97
|
+
|
|
98
|
+
response.ok = apiResponse.ok;
|
|
99
|
+
response.status = apiResponse.status;
|
|
100
|
+
response.statusText = apiResponse.statusText;
|
|
101
|
+
try {
|
|
102
|
+
response.data = await apiResponse.json();
|
|
103
|
+
} catch {
|
|
104
|
+
response.data = undefined;
|
|
105
|
+
}
|
|
106
|
+
} catch {
|
|
107
|
+
response.ok = false;
|
|
108
|
+
response.status = 500;
|
|
109
|
+
response.data = undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return response;
|
|
113
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the authentication token from cookies (server-side)
|
|
5
|
+
*/
|
|
6
|
+
export async function getServerToken(): Promise<string | undefined> {
|
|
7
|
+
const { cookies } = await import("next/headers");
|
|
8
|
+
const cookieStore = await cookies();
|
|
9
|
+
return cookieStore.get("token")?.value;
|
|
10
|
+
}
|