@goplusvn/core 0.1.71 → 0.1.72
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/auth/index.ts +5 -1
- package/src/configs/entities/departments.config.ts +1 -0
- package/src/configs/entities/material-categories.config.ts +1 -0
- package/src/crud/components/crud-export-button.tsx +3 -2
- package/src/crud/components/crud-page.tsx +56 -25
- package/src/crud/components/crud-row-actions.tsx +16 -4
- package/src/crud/crud-route-handlers.test.ts +352 -0
- package/src/crud/crud-route-handlers.ts +260 -13
- package/src/crud/index.ts +3 -0
- package/src/crud/lib/coerce.test.ts +118 -0
- package/src/crud/lib/coerce.ts +95 -0
- package/src/crud/lib/crud-utils.ts +4 -0
- package/src/crud/lib/entity-endpoints.test.ts +41 -0
- package/src/crud/lib/entity-endpoints.ts +23 -0
- package/src/crud/lib/errors.ts +14 -0
- package/src/crud/lib/import-request.ts +99 -0
- package/src/crud/lib/mutation-builder.test.ts +114 -0
- package/src/crud/lib/mutation-builder.ts +27 -34
- package/src/crud/lib/permissions.test.ts +57 -0
- package/src/crud/lib/permissions.ts +25 -13
- package/src/crud/lib/query-builder.ts +13 -6
- package/src/crud/pages/entity-crud-page.tsx +11 -8
- package/src/crud/server-service.test.ts +112 -0
- package/src/crud/server-service.ts +52 -27
- package/src/crud/server.ts +11 -1
- package/src/guardrails/rules/design.ts +3 -2
- package/src/types/index.ts +75 -10
- package/src/ui/data-display/__tests__/use-client-pagination.test.ts +59 -0
- package/src/ui/data-display/data-table/__tests__/data-table-row-memo.test.tsx +96 -0
- package/src/ui/data-display/data-table/data-table-context.tsx +5 -0
- package/src/ui/data-display/data-table/data-table.tsx +68 -23
- package/src/ui/data-display/data-table-pagination.tsx +25 -8
- package/src/ui/data-display/index.tsx +1 -0
- package/src/ui/data-display/use-client-pagination.ts +48 -0
- package/templates/starter-app/AGENTS.md +1 -1
- package/templates/starter-app/prisma/migrations/20260807115347_platform_pages/migration.sql +87 -0
- package/templates/starter-app/prisma/migrations/20260808003811_company_profile/migration.sql +25 -0
- package/templates/starter-app/scripts/rbac-sync.ts +49 -21
- package/templates/starter-app/src/__tests__/architecture.test.ts +3 -2
- package/templates/starter-app/src/app/[lang]/(main)/actions/page.tsx +4 -4
- package/templates/starter-app/src/app/[lang]/(main)/admin/system/cache/page.tsx +1 -2
- package/templates/starter-app/src/app/[lang]/(main)/admin/system/company/actions.ts +7 -5
- package/templates/starter-app/src/app/[lang]/(main)/admin/system/company/company-profile-form.tsx +20 -9
- package/templates/starter-app/src/app/[lang]/(main)/admin/system/company/page.tsx +2 -1
- package/templates/starter-app/src/app/[lang]/(main)/crud/[entity]/page.tsx +4 -2
- package/templates/starter-app/src/app/[lang]/(main)/layout.tsx +2 -1
- package/templates/starter-app/src/app/[lang]/(main)/page.tsx +4 -4
- package/templates/starter-app/src/app/[lang]/(main)/resources/page.tsx +5 -5
- package/templates/starter-app/src/app/[lang]/(main)/roles/[id]/edit/page.tsx +5 -2
- package/templates/starter-app/src/app/[lang]/(main)/roles/new/page.tsx +1 -0
- package/templates/starter-app/src/app/[lang]/(main)/roles/page.tsx +1 -0
- package/templates/starter-app/src/app/[lang]/(main)/user/profile/actions.ts +4 -2
- package/templates/starter-app/src/app/[lang]/(main)/user/profile/page.tsx +3 -1
- package/templates/starter-app/src/app/[lang]/(main)/user/profile/profile-client-page.tsx +13 -5
- package/templates/starter-app/src/app/[lang]/(main)/users/page.tsx +3 -2
- package/templates/starter-app/src/app/[lang]/layout.tsx +2 -2
- package/templates/starter-app/src/app/api/admin/system/audit/route.ts +4 -3
- package/templates/starter-app/src/app/api/admin/system/jobs/[name]/history/route.ts +1 -1
- package/templates/starter-app/src/app/api/admin/system/jobs/route.ts +31 -12
- package/templates/starter-app/src/app/api/admin/system/settings/all/route.ts +2 -2
- package/templates/starter-app/src/app/api/admin/system/settings/create/route.ts +10 -3
- package/templates/starter-app/src/app/api/admin/system/settings/delete/route.ts +10 -3
- package/templates/starter-app/src/app/api/admin/system/settings/route.ts +14 -5
- package/templates/starter-app/src/app/api/admin/system/settings/toggle-status/route.ts +16 -7
- package/templates/starter-app/src/app/api/admin/system/settings/update/route.ts +12 -6
- package/templates/starter-app/src/app/api/admin/system/settings/update-full/route.ts +12 -6
- package/templates/starter-app/src/app/api/better-auth/[...all]/route.ts +1 -1
- package/templates/starter-app/src/app/api/crud/[entity]/[id]/route.ts +2 -2
- package/templates/starter-app/src/app/api/crud/[entity]/export/route.ts +16 -0
- package/templates/starter-app/src/app/api/crud/[entity]/import/route.ts +17 -0
- package/templates/starter-app/src/app/api/crud/[entity]/route.ts +2 -2
- package/templates/starter-app/src/app/api/error-logs/[id]/route.ts +4 -2
- package/templates/starter-app/src/app/api/error-logs/route.ts +26 -19
- package/templates/starter-app/src/app/api/files/[...key]/route.ts +1 -2
- package/templates/starter-app/src/app/api/job-titles/route.ts +1 -1
- package/templates/starter-app/src/app/api/notifications/read/route.ts +1 -1
- package/templates/starter-app/src/app/api/notifications/route.ts +1 -1
- package/templates/starter-app/src/app/api/notifications/unread-count/route.ts +1 -1
- package/templates/starter-app/src/app/api/rbac/permissions-version/route.ts +4 -1
- package/templates/starter-app/src/app/api/roles/[id]/route.ts +2 -1
- package/templates/starter-app/src/app/api/roles/route.ts +2 -1
- package/templates/starter-app/src/app/api/security/sessions/[id]/route.ts +5 -5
- package/templates/starter-app/src/app/api/security/sessions/revoke-user/route.ts +1 -1
- package/templates/starter-app/src/app/api/security/sessions/route.ts +2 -2
- package/templates/starter-app/src/app/api/system-categories/route.ts +39 -14
- package/templates/starter-app/src/app/api/system-category-groups/route.ts +32 -14
- package/templates/starter-app/src/app/api/tasks/[id]/download/route.ts +7 -3
- package/templates/starter-app/src/app/api/tasks/route.ts +5 -2
- package/templates/starter-app/src/app/api/upload/route.ts +1 -2
- package/templates/starter-app/src/app/api/users/[id]/route.ts +20 -12
- package/templates/starter-app/src/app/api/users/route.ts +11 -9
- package/templates/starter-app/src/app/globals.css +0 -1
- package/templates/starter-app/src/components/layout/main-layout-wrapper.tsx +5 -4
- package/templates/starter-app/src/configs/entities/department.config.ts +7 -4
- package/templates/starter-app/src/configs/entities/index.ts +1 -1
- package/templates/starter-app/src/configs/entities/job-titles.config.ts +2 -0
- package/templates/starter-app/src/configs/entities/system-alerts.config.ts +2 -0
- package/templates/starter-app/src/configs/entities/users.config.ts +15 -2
- package/templates/starter-app/src/configs/i18n.ts +6 -6
- package/templates/starter-app/src/configs/permissions/menu-tree.ts +1 -1
- package/templates/starter-app/src/data/dictionary.ts +2 -1
- package/templates/starter-app/src/data/navigations.ts +12 -2
- package/templates/starter-app/src/lib/action-guard.ts +3 -4
- package/templates/starter-app/src/lib/api-handler.ts +27 -19
- package/templates/starter-app/src/lib/auth.ts +3 -1
- package/templates/starter-app/src/lib/branch-scope.ts +1 -1
- package/templates/starter-app/src/lib/crud/index.ts +5 -12
- package/templates/starter-app/src/lib/errors/log-server-error.ts +5 -4
- package/templates/starter-app/src/lib/logger.ts +11 -4
- package/templates/starter-app/src/lib/page-guard.ts +2 -3
- package/templates/starter-app/src/lib/prisma.ts +13 -5
- package/templates/starter-app/src/lib/rbac/access.ts +2 -2
- package/templates/starter-app/src/lib/storage.ts +3 -1
- package/templates/starter-app/src/providers/index.tsx +2 -1
- package/templates/starter-app/src/providers/theme-provider.tsx +4 -3
- package/templates/starter-app/src/server/services/company-service.ts +4 -2
- package/templates/starter-app/src/server/services/notification-service.ts +6 -6
- package/templates/starter-app/src/server/services/system-config-service.ts +21 -9
- package/templates/starter-app/src/server/services/user-service.ts +2 -2
- package/templates/starter-app/src/server/tasks/handlers/export-departments.ts +15 -9
- package/templates/starter-app/src/server/tasks/task-runner.ts +6 -7
- package/templates/starter-app/tsconfig.json +8 -0
- package/templates/starter-app/vitest.config.ts +17 -1
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import type { Metadata } from "next"
|
|
10
10
|
|
|
11
11
|
import { dictionary } from "@/data/dictionary"
|
|
12
|
+
|
|
12
13
|
import { requirePageAccess } from "@/lib/page-guard"
|
|
13
14
|
import { db } from "@/lib/prisma"
|
|
14
15
|
|
|
@@ -29,7 +30,9 @@ export default async function EditRolePage({
|
|
|
29
30
|
getActionsForPermissionMatrix(db),
|
|
30
31
|
db.role.findUnique({
|
|
31
32
|
where: { id },
|
|
32
|
-
include: {
|
|
33
|
+
include: {
|
|
34
|
+
rolePermissions: { select: { resourceCode: true, actionCode: true } },
|
|
35
|
+
},
|
|
33
36
|
}),
|
|
34
37
|
])
|
|
35
38
|
|
|
@@ -52,7 +55,7 @@ export default async function EditRolePage({
|
|
|
52
55
|
// Ma trận quyền của core đọc dạng "action:resource" — đảo thứ tự là
|
|
53
56
|
// không ô nào tick, mà cũng chẳng có lỗi nào hiện ra.
|
|
54
57
|
permissions: role.rolePermissions.map(
|
|
55
|
-
(rp) => `${rp.actionCode}:${rp.resourceCode}
|
|
58
|
+
(rp) => `${rp.actionCode}:${rp.resourceCode}`
|
|
56
59
|
),
|
|
57
60
|
}}
|
|
58
61
|
/>
|
|
@@ -28,7 +28,7 @@ export interface ChangePasswordState {
|
|
|
28
28
|
*/
|
|
29
29
|
export async function changePassword(
|
|
30
30
|
_prev: ChangePasswordState,
|
|
31
|
-
formData: FormData
|
|
31
|
+
formData: FormData
|
|
32
32
|
): Promise<ChangePasswordState> {
|
|
33
33
|
// Không getSession() trần ở đây (guardrail server-action-bare-session):
|
|
34
34
|
// auth.api.changePassword tự validate phiên từ headers — không phiên là ném.
|
|
@@ -38,7 +38,9 @@ export async function changePassword(
|
|
|
38
38
|
confirmPassword: formData.get("confirmPassword"),
|
|
39
39
|
})
|
|
40
40
|
if (!parsed.success) {
|
|
41
|
-
return {
|
|
41
|
+
return {
|
|
42
|
+
message: parsed.error.issues[0]?.message ?? "Dữ liệu không hợp lệ",
|
|
43
|
+
}
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
try {
|
|
@@ -27,7 +27,9 @@ export default async function UserProfilePage() {
|
|
|
27
27
|
email: true,
|
|
28
28
|
image: true,
|
|
29
29
|
createdAt: true,
|
|
30
|
-
userRoles: {
|
|
30
|
+
userRoles: {
|
|
31
|
+
select: { roleCode: true, role: { select: { name: true } } },
|
|
32
|
+
},
|
|
31
33
|
userBranches: {
|
|
32
34
|
select: { isDefault: true, branch: { select: { name: true } } },
|
|
33
35
|
},
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { useActionState } from "react"
|
|
5
|
-
import { toast } from "sonner"
|
|
6
|
-
|
|
7
5
|
import {
|
|
8
6
|
Avatar,
|
|
9
7
|
AvatarFallback,
|
|
@@ -17,8 +15,11 @@ import {
|
|
|
17
15
|
import { Button } from "@goerp/core/ui/primitives/button"
|
|
18
16
|
import { Input } from "@goerp/core/ui/primitives/input"
|
|
19
17
|
import { Label } from "@goerp/core/ui/primitives/label"
|
|
18
|
+
import { toast } from "sonner"
|
|
20
19
|
|
|
21
|
-
import {
|
|
20
|
+
import type { ChangePasswordState } from "./actions"
|
|
21
|
+
|
|
22
|
+
import { changePassword } from "./actions"
|
|
22
23
|
|
|
23
24
|
interface ProfileUser {
|
|
24
25
|
id: string
|
|
@@ -58,7 +59,10 @@ export function ProfileClientPage({ user }: { user: ProfileUser }) {
|
|
|
58
59
|
<CardContent className="flex flex-col gap-6">
|
|
59
60
|
<div className="flex items-center gap-4">
|
|
60
61
|
<Avatar className="size-14">
|
|
61
|
-
<AvatarImage
|
|
62
|
+
<AvatarImage
|
|
63
|
+
src={user.image ?? undefined}
|
|
64
|
+
alt={user.name ?? ""}
|
|
65
|
+
/>
|
|
62
66
|
<AvatarFallback>
|
|
63
67
|
{(user.name ?? user.email).slice(0, 2).toUpperCase()}
|
|
64
68
|
</AvatarFallback>
|
|
@@ -107,7 +111,11 @@ export function ProfileClientPage({ user }: { user: ProfileUser }) {
|
|
|
107
111
|
<CardTitle>Đổi mật khẩu</CardTitle>
|
|
108
112
|
</CardHeader>
|
|
109
113
|
<CardContent>
|
|
110
|
-
<form
|
|
114
|
+
<form
|
|
115
|
+
ref={formRef}
|
|
116
|
+
action={formAction}
|
|
117
|
+
className="grid gap-4 md:max-w-md"
|
|
118
|
+
>
|
|
111
119
|
<div className="space-y-2">
|
|
112
120
|
<Label htmlFor="currentPassword">Mật khẩu hiện tại</Label>
|
|
113
121
|
<Input
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { getUsersList } from "@/server/services/user-service"
|
|
1
2
|
import { getCrudPermissions } from "@goerp/core/crud/server"
|
|
2
3
|
import { getActiveRoles, getUserStats } from "@goerp/core/user"
|
|
3
4
|
import { UsersClientPage } from "@goerp/core/user/client-page"
|
|
4
5
|
|
|
5
6
|
import type { Metadata } from "next"
|
|
6
7
|
|
|
8
|
+
import { dictionary } from "@/data/dictionary"
|
|
9
|
+
|
|
7
10
|
import { usersConfig } from "@/configs/entities/users.config"
|
|
8
11
|
import { getActionMetaMap } from "@/configs/permissions"
|
|
9
12
|
import { MENU_TREE } from "@/configs/permissions/menu-tree"
|
|
10
|
-
import { dictionary } from "@/data/dictionary"
|
|
11
13
|
import { requirePageAccess } from "@/lib/page-guard"
|
|
12
14
|
import { db } from "@/lib/prisma"
|
|
13
|
-
import { getUsersList } from "@/server/services/user-service"
|
|
14
15
|
|
|
15
16
|
export const metadata: Metadata = { title: "Người dùng" }
|
|
16
17
|
export const dynamic = "force-dynamic"
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { Providers } from "@/providers"
|
|
2
|
+
|
|
1
3
|
import type { LocaleType } from "@goerp/core/types"
|
|
2
4
|
import type { ReactNode } from "react"
|
|
3
5
|
|
|
4
|
-
import { Providers } from "@/providers"
|
|
5
|
-
|
|
6
6
|
/**
|
|
7
7
|
* Không truyền session xuống Providers: Better Auth lấy session ở CLIENT qua
|
|
8
8
|
* authClient.useSession() (bridge). Server component nào cần session thì tự gọi
|
|
@@ -18,7 +18,6 @@ export const GET = apiHandler(
|
|
|
18
18
|
const skip = parseInt(sp.get("skip") || "0", 10)
|
|
19
19
|
const take = Math.min(parseInt(sp.get("take") || "50", 10), 100)
|
|
20
20
|
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
21
|
const where: Record<string, any> = {}
|
|
23
22
|
if (action && action !== "all") where.action = action
|
|
24
23
|
if (resource && resource !== "all") where.resource = resource
|
|
@@ -53,8 +52,10 @@ export const GET = apiHandler(
|
|
|
53
52
|
meta: { total, skip, take, hasMore: skip + take < total },
|
|
54
53
|
})
|
|
55
54
|
} catch (error) {
|
|
56
|
-
return serverError(error, req, {
|
|
55
|
+
return serverError(error, req, {
|
|
56
|
+
message: "Không tải được nhật ký thao tác",
|
|
57
|
+
})
|
|
57
58
|
}
|
|
58
59
|
},
|
|
59
|
-
{ resource: "audit-log", action: "view" }
|
|
60
|
+
{ resource: "audit-log", action: "view" }
|
|
60
61
|
)
|
|
@@ -17,13 +17,18 @@ import { db } from "@/lib/prisma"
|
|
|
17
17
|
export const GET = apiHandler(
|
|
18
18
|
async (req) => {
|
|
19
19
|
try {
|
|
20
|
-
const memoryJobs = new Map(
|
|
20
|
+
const memoryJobs = new Map(
|
|
21
|
+
cronManager.getJobsInfo().map((j) => [j.name, j])
|
|
22
|
+
)
|
|
21
23
|
const dbJobs = new Map(
|
|
22
|
-
(await db.systemJob.findMany({ orderBy: { name: "asc" } })).map((j) => [
|
|
24
|
+
(await db.systemJob.findMany({ orderBy: { name: "asc" } })).map((j) => [
|
|
25
|
+
j.name,
|
|
26
|
+
j,
|
|
27
|
+
])
|
|
23
28
|
)
|
|
24
29
|
|
|
25
|
-
const names = [...new Set([...dbJobs.keys(), ...memoryJobs.keys()])].sort(
|
|
26
|
-
a.localeCompare(b)
|
|
30
|
+
const names = [...new Set([...dbJobs.keys(), ...memoryJobs.keys()])].sort(
|
|
31
|
+
(a, b) => a.localeCompare(b)
|
|
27
32
|
)
|
|
28
33
|
|
|
29
34
|
const jobs = names.map((name) => {
|
|
@@ -45,10 +50,12 @@ export const GET = apiHandler(
|
|
|
45
50
|
|
|
46
51
|
return NextResponse.json({ data: jobs })
|
|
47
52
|
} catch (error) {
|
|
48
|
-
return serverError(error, req, {
|
|
53
|
+
return serverError(error, req, {
|
|
54
|
+
message: "Không tải được danh sách job",
|
|
55
|
+
})
|
|
49
56
|
}
|
|
50
57
|
},
|
|
51
|
-
{ resource: "system-job", action: "view" }
|
|
58
|
+
{ resource: "system-job", action: "view" }
|
|
52
59
|
)
|
|
53
60
|
|
|
54
61
|
/** POST { name, action: "toggle" | "run" | "remove" }. */
|
|
@@ -62,7 +69,10 @@ export const POST = apiHandler(
|
|
|
62
69
|
|
|
63
70
|
if (action === "toggle") {
|
|
64
71
|
if (!(await cronManager.toggleJob(name))) {
|
|
65
|
-
return NextResponse.json(
|
|
72
|
+
return NextResponse.json(
|
|
73
|
+
{ error: `Không tìm thấy job "${name}"` },
|
|
74
|
+
{ status: 404 }
|
|
75
|
+
)
|
|
66
76
|
}
|
|
67
77
|
const info = cronManager.getJobsInfo().find((j) => j.name === name)
|
|
68
78
|
return NextResponse.json({
|
|
@@ -74,23 +84,32 @@ export const POST = apiHandler(
|
|
|
74
84
|
|
|
75
85
|
if (action === "run") {
|
|
76
86
|
if (!(await cronManager.runJobNow(name))) {
|
|
77
|
-
return NextResponse.json(
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: `Không tìm thấy job "${name}"` },
|
|
89
|
+
{ status: 404 }
|
|
90
|
+
)
|
|
78
91
|
}
|
|
79
|
-
return NextResponse.json({
|
|
92
|
+
return NextResponse.json({
|
|
93
|
+
success: true,
|
|
94
|
+
message: `Đã chạy tay job "${name}"`,
|
|
95
|
+
})
|
|
80
96
|
}
|
|
81
97
|
|
|
82
98
|
if (action === "remove") {
|
|
83
99
|
cronManager.removeJob(name)
|
|
84
|
-
return NextResponse.json({
|
|
100
|
+
return NextResponse.json({
|
|
101
|
+
success: true,
|
|
102
|
+
message: `Đã gỡ job "${name}"`,
|
|
103
|
+
})
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
return NextResponse.json(
|
|
88
107
|
{ error: "action phải là 'toggle', 'run' hoặc 'remove'" },
|
|
89
|
-
{ status: 400 }
|
|
108
|
+
{ status: 400 }
|
|
90
109
|
)
|
|
91
110
|
} catch (error) {
|
|
92
111
|
return serverError(error, req, { message: "Không thao tác được với job" })
|
|
93
112
|
}
|
|
94
113
|
},
|
|
95
|
-
{ resource: "system-job", action: "update" }
|
|
114
|
+
{ resource: "system-job", action: "update" }
|
|
96
115
|
)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
+
import { listConfigs } from "@/server/services/system-config-service"
|
|
2
3
|
|
|
3
4
|
import { apiHandler } from "@/lib/api-handler"
|
|
4
5
|
import { serverError } from "@/lib/errors/server-error"
|
|
5
|
-
import { listConfigs } from "@/server/services/system-config-service"
|
|
6
6
|
|
|
7
7
|
/** Toàn bộ cấu hình đang bật — bảng chính của trang cài đặt hệ thống. */
|
|
8
8
|
export const GET = apiHandler(
|
|
@@ -13,5 +13,5 @@ export const GET = apiHandler(
|
|
|
13
13
|
return serverError(error, req, { message: "Không tải được cấu hình" })
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
|
-
{ resource: "system-setting", action: "view" }
|
|
16
|
+
{ resource: "system-setting", action: "view" }
|
|
17
17
|
)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
+
import {
|
|
3
|
+
configErrorInfo,
|
|
4
|
+
createConfig,
|
|
5
|
+
} from "@/server/services/system-config-service"
|
|
2
6
|
|
|
3
7
|
import { apiHandler } from "@/lib/api-handler"
|
|
4
8
|
import { serverError } from "@/lib/errors/server-error"
|
|
5
|
-
import { configErrorInfo, createConfig } from "@/server/services/system-config-service"
|
|
6
9
|
|
|
7
10
|
export const POST = apiHandler(
|
|
8
11
|
async (req, { session }) => {
|
|
@@ -11,9 +14,13 @@ export const POST = apiHandler(
|
|
|
11
14
|
return NextResponse.json({ success: true, data })
|
|
12
15
|
} catch (error) {
|
|
13
16
|
const known = configErrorInfo(error)
|
|
14
|
-
if (known)
|
|
17
|
+
if (known)
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{ error: known.message },
|
|
20
|
+
{ status: known.status }
|
|
21
|
+
)
|
|
15
22
|
return serverError(error, req, { message: "Không tạo được cấu hình" })
|
|
16
23
|
}
|
|
17
24
|
},
|
|
18
|
-
{ resource: "system-setting", action: "create" }
|
|
25
|
+
{ resource: "system-setting", action: "create" }
|
|
19
26
|
)
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
+
import {
|
|
3
|
+
configErrorInfo,
|
|
4
|
+
deleteConfig,
|
|
5
|
+
} from "@/server/services/system-config-service"
|
|
2
6
|
|
|
3
7
|
import { apiHandler } from "@/lib/api-handler"
|
|
4
8
|
import { serverError } from "@/lib/errors/server-error"
|
|
5
|
-
import { configErrorInfo, deleteConfig } from "@/server/services/system-config-service"
|
|
6
9
|
|
|
7
10
|
export const POST = apiHandler(
|
|
8
11
|
async (req) => {
|
|
@@ -12,9 +15,13 @@ export const POST = apiHandler(
|
|
|
12
15
|
return NextResponse.json({ success: true, message: "Đã xóa cấu hình" })
|
|
13
16
|
} catch (error) {
|
|
14
17
|
const known = configErrorInfo(error)
|
|
15
|
-
if (known)
|
|
18
|
+
if (known)
|
|
19
|
+
return NextResponse.json(
|
|
20
|
+
{ error: known.message },
|
|
21
|
+
{ status: known.status }
|
|
22
|
+
)
|
|
16
23
|
return serverError(error, req, { message: "Không xóa được cấu hình" })
|
|
17
24
|
}
|
|
18
25
|
},
|
|
19
|
-
{ resource: "system-setting", action: "delete" }
|
|
26
|
+
{ resource: "system-setting", action: "delete" }
|
|
20
27
|
)
|
|
@@ -23,7 +23,7 @@ export const GET = apiHandler(
|
|
|
23
23
|
return serverError(error, req, { message: "Không tải được cấu hình" })
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
|
-
{ resource: "system-setting", action: "view" }
|
|
26
|
+
{ resource: "system-setting", action: "view" }
|
|
27
27
|
)
|
|
28
28
|
|
|
29
29
|
export const POST = apiHandler(
|
|
@@ -33,17 +33,26 @@ export const POST = apiHandler(
|
|
|
33
33
|
|
|
34
34
|
if (type === "audit") {
|
|
35
35
|
await SettingsService.saveAuditSettings(settings)
|
|
36
|
-
return NextResponse.json({
|
|
36
|
+
return NextResponse.json({
|
|
37
|
+
success: true,
|
|
38
|
+
message: "Đã lưu cấu hình nhật ký",
|
|
39
|
+
})
|
|
37
40
|
}
|
|
38
41
|
if (type === "notification") {
|
|
39
42
|
await SettingsService.saveNotificationSettings(settings)
|
|
40
|
-
return NextResponse.json({
|
|
43
|
+
return NextResponse.json({
|
|
44
|
+
success: true,
|
|
45
|
+
message: "Đã lưu cấu hình thông báo",
|
|
46
|
+
})
|
|
41
47
|
}
|
|
42
48
|
|
|
43
|
-
return NextResponse.json(
|
|
49
|
+
return NextResponse.json(
|
|
50
|
+
{ error: "Loại cấu hình không hợp lệ" },
|
|
51
|
+
{ status: 400 }
|
|
52
|
+
)
|
|
44
53
|
} catch (error) {
|
|
45
54
|
return serverError(error, req, { message: "Không lưu được cấu hình" })
|
|
46
55
|
}
|
|
47
56
|
},
|
|
48
|
-
{ resource: "system-setting", action: "update" }
|
|
57
|
+
{ resource: "system-setting", action: "update" }
|
|
49
58
|
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
-
|
|
3
|
-
import { apiHandler } from "@/lib/api-handler"
|
|
4
|
-
import { serverError } from "@/lib/errors/server-error"
|
|
5
2
|
import {
|
|
6
3
|
configErrorInfo,
|
|
7
4
|
toggleConfigStatus,
|
|
8
5
|
} from "@/server/services/system-config-service"
|
|
9
6
|
|
|
7
|
+
import { apiHandler } from "@/lib/api-handler"
|
|
8
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
9
|
+
|
|
10
10
|
/** Bật/tạm ngưng một cấu hình mà không xóa nó. */
|
|
11
11
|
export const POST = apiHandler(
|
|
12
12
|
async (req, { session }) => {
|
|
@@ -16,13 +16,22 @@ export const POST = apiHandler(
|
|
|
16
16
|
return NextResponse.json({
|
|
17
17
|
success: true,
|
|
18
18
|
data,
|
|
19
|
-
message:
|
|
19
|
+
message:
|
|
20
|
+
status === "active"
|
|
21
|
+
? "Đã kích hoạt cấu hình"
|
|
22
|
+
: "Đã tạm ngưng cấu hình",
|
|
20
23
|
})
|
|
21
24
|
} catch (error) {
|
|
22
25
|
const known = configErrorInfo(error)
|
|
23
|
-
if (known)
|
|
24
|
-
|
|
26
|
+
if (known)
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ error: known.message },
|
|
29
|
+
{ status: known.status }
|
|
30
|
+
)
|
|
31
|
+
return serverError(error, req, {
|
|
32
|
+
message: "Không cập nhật được trạng thái",
|
|
33
|
+
})
|
|
25
34
|
}
|
|
26
35
|
},
|
|
27
|
-
{ resource: "system-setting", action: "update" }
|
|
36
|
+
{ resource: "system-setting", action: "update" }
|
|
28
37
|
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
-
|
|
3
|
-
import { apiHandler } from "@/lib/api-handler"
|
|
4
|
-
import { serverError } from "@/lib/errors/server-error"
|
|
5
2
|
import {
|
|
6
3
|
configErrorInfo,
|
|
7
4
|
updateConfigValue,
|
|
8
5
|
} from "@/server/services/system-config-service"
|
|
9
6
|
|
|
7
|
+
import { apiHandler } from "@/lib/api-handler"
|
|
8
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
9
|
+
|
|
10
10
|
/** Đổi mỗi giá trị của một cấu hình theo key. */
|
|
11
11
|
export const POST = apiHandler(
|
|
12
12
|
async (req, { session }) => {
|
|
@@ -16,9 +16,15 @@ export const POST = apiHandler(
|
|
|
16
16
|
return NextResponse.json({ success: true, data })
|
|
17
17
|
} catch (error) {
|
|
18
18
|
const known = configErrorInfo(error)
|
|
19
|
-
if (known)
|
|
20
|
-
|
|
19
|
+
if (known)
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: known.message },
|
|
22
|
+
{ status: known.status }
|
|
23
|
+
)
|
|
24
|
+
return serverError(error, req, {
|
|
25
|
+
message: "Không cập nhật được cấu hình",
|
|
26
|
+
})
|
|
21
27
|
}
|
|
22
28
|
},
|
|
23
|
-
{ resource: "system-setting", action: "update" }
|
|
29
|
+
{ resource: "system-setting", action: "update" }
|
|
24
30
|
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { NextResponse } from "next/server"
|
|
2
|
-
|
|
3
|
-
import { apiHandler } from "@/lib/api-handler"
|
|
4
|
-
import { serverError } from "@/lib/errors/server-error"
|
|
5
2
|
import {
|
|
6
3
|
configErrorInfo,
|
|
7
4
|
updateConfigFull,
|
|
8
5
|
} from "@/server/services/system-config-service"
|
|
9
6
|
|
|
7
|
+
import { apiHandler } from "@/lib/api-handler"
|
|
8
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
9
|
+
|
|
10
10
|
/** Sửa toàn bộ thuộc tính của một cấu hình (dialog "Sửa"). */
|
|
11
11
|
export const POST = apiHandler(
|
|
12
12
|
async (req, { session }) => {
|
|
@@ -15,9 +15,15 @@ export const POST = apiHandler(
|
|
|
15
15
|
return NextResponse.json({ success: true, data })
|
|
16
16
|
} catch (error) {
|
|
17
17
|
const known = configErrorInfo(error)
|
|
18
|
-
if (known)
|
|
19
|
-
|
|
18
|
+
if (known)
|
|
19
|
+
return NextResponse.json(
|
|
20
|
+
{ error: known.message },
|
|
21
|
+
{ status: known.status }
|
|
22
|
+
)
|
|
23
|
+
return serverError(error, req, {
|
|
24
|
+
message: "Không cập nhật được cấu hình",
|
|
25
|
+
})
|
|
20
26
|
}
|
|
21
27
|
},
|
|
22
|
-
{ resource: "system-setting", action: "update" }
|
|
28
|
+
{ resource: "system-setting", action: "update" }
|
|
23
29
|
)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createCrudItemHandlers } from "@goerp/core/crud/server"
|
|
2
2
|
|
|
3
|
-
import { getSession } from "@/lib/auth"
|
|
4
3
|
import { getEntityConfig } from "@/configs/entities"
|
|
5
|
-
import {
|
|
4
|
+
import { getSession } from "@/lib/auth"
|
|
6
5
|
import { crudService } from "@/lib/crud"
|
|
6
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
7
7
|
|
|
8
8
|
export const { GET, PUT, PATCH, DELETE } = createCrudItemHandlers({
|
|
9
9
|
getSession,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createCrudExportHandler } from "@goerp/core/crud/server"
|
|
2
|
+
|
|
3
|
+
import { getEntityConfig } from "@/configs/entities"
|
|
4
|
+
import { getSession } from "@/lib/auth"
|
|
5
|
+
import { crudService } from "@/lib/crud"
|
|
6
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
7
|
+
|
|
8
|
+
// Xuất xlsx/csv/json theo đúng search/sort/filter đang áp trên bảng. Nút
|
|
9
|
+
// Export của CrudPage mặc định trỏ về đây (`${apiEndpoint}/export`); gác
|
|
10
|
+
// quyền `<resource>:export` ở core.
|
|
11
|
+
export const { GET } = createCrudExportHandler({
|
|
12
|
+
getSession,
|
|
13
|
+
getEntityConfig,
|
|
14
|
+
service: crudService,
|
|
15
|
+
onError: serverError,
|
|
16
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createCrudImportHandler } from "@goerp/core/crud/server"
|
|
2
|
+
|
|
3
|
+
import { getEntityConfig } from "@/configs/entities"
|
|
4
|
+
import { getSession } from "@/lib/auth"
|
|
5
|
+
import { crudService } from "@/lib/crud"
|
|
6
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
7
|
+
|
|
8
|
+
// Nhập từ file (multipart của ImportDialog) hoặc JSON array. Mỗi dòng đi qua
|
|
9
|
+
// service.create như tạo tay — coercion/validate/audit của engine áp đủ; dòng
|
|
10
|
+
// lỗi gom vào ImportResult.errors, không chặn dòng sau. Gác quyền
|
|
11
|
+
// `<resource>:import` ở core.
|
|
12
|
+
export const { POST } = createCrudImportHandler({
|
|
13
|
+
getSession,
|
|
14
|
+
getEntityConfig,
|
|
15
|
+
service: crudService,
|
|
16
|
+
onError: serverError,
|
|
17
|
+
})
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createCrudCollectionHandlers } from "@goerp/core/crud/server"
|
|
2
2
|
|
|
3
|
-
import { getSession } from "@/lib/auth"
|
|
4
3
|
import { getEntityConfig } from "@/configs/entities"
|
|
5
|
-
import {
|
|
4
|
+
import { getSession } from "@/lib/auth"
|
|
6
5
|
import { crudService } from "@/lib/crud"
|
|
6
|
+
import { serverError } from "@/lib/errors/server-error"
|
|
7
7
|
|
|
8
8
|
// GET danh sách + POST tạo. Gác quyền + query đều ở core.
|
|
9
9
|
export const { GET, POST } = createCrudCollectionHandlers({
|
|
@@ -16,8 +16,10 @@ export const PATCH = apiHandler(
|
|
|
16
16
|
})
|
|
17
17
|
return NextResponse.json({ success: true, log })
|
|
18
18
|
} catch (error) {
|
|
19
|
-
return serverError(error, req, {
|
|
19
|
+
return serverError(error, req, {
|
|
20
|
+
message: "Không cập nhật được trạng thái lỗi",
|
|
21
|
+
})
|
|
20
22
|
}
|
|
21
23
|
},
|
|
22
|
-
{ resource: "error-log", action: "update" }
|
|
24
|
+
{ resource: "error-log", action: "update" }
|
|
23
25
|
)
|