@arch-cadre/panel 1.0.7 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/activity-log/pages/log-list.cjs +2 -2
- package/dist/ui/components/profile/components.cjs +2 -2
- package/dist/ui/components/profile/components.d.ts +1 -1
- package/dist/ui/components/profile/components.mjs +1 -1
- package/dist/ui/rbac/pages/rbac-admin.cjs +14 -14
- package/dist/ui/session-manager/components/sessions-list.cjs +5 -5
- package/dist/ui/session-manager/components/sessions-list.mjs +1 -1
- package/dist/ui/session-manager/pages/sessions-page.cjs +3 -3
- package/dist/ui/session-manager/pages/sessions-page.mjs +1 -1
- package/package.json +7 -6
- package/src/actions/actions.ts +17 -0
- package/src/actions/activity-log/index.ts +17 -0
- package/src/actions/index.ts +2 -0
- package/src/actions/manager.ts +168 -0
- package/src/actions/profile.ts +173 -0
- package/src/actions/rbac/index.ts +131 -0
- package/src/actions/session-manager/index.ts +87 -0
- package/src/actions/settings.ts +34 -0
- package/src/index.ts +135 -0
- package/src/intl.d.ts +9 -0
- package/src/navigation.ts +57 -0
- package/src/routes.ts +107 -0
- package/src/schema/activity-log.ts +16 -0
- package/src/schema.ts +1 -0
- package/src/types.ts +18 -0
- package/src/ui/activity-log/components/ActivityStatsWidget.tsx +37 -0
- package/src/ui/activity-log/components/RecentLogsWidget.tsx +74 -0
- package/src/ui/activity-log/pages/log-list.tsx +91 -0
- package/src/ui/components/app-content.tsx +51 -0
- package/src/ui/components/app-header.tsx +65 -0
- package/src/ui/components/app-sidebar.tsx +249 -0
- package/src/ui/components/app-user.tsx +126 -0
- package/src/ui/components/breadcrumb-slot.tsx +52 -0
- package/src/ui/components/manager/module-card.tsx +327 -0
- package/src/ui/components/manager/module-list.tsx +59 -0
- package/src/ui/components/manager/module-upload.tsx +84 -0
- package/src/ui/components/profile/components.tsx +311 -0
- package/src/ui/components/profile/link.tsx +36 -0
- package/src/ui/components/profile/page.tsx +45 -0
- package/src/ui/components/sidebar-slot.tsx +47 -0
- package/src/ui/dashboard/page.tsx +17 -0
- package/src/ui/dashboard/widgets/WelcomeBackUserWidget.tsx +47 -0
- package/src/ui/error.tsx +82 -0
- package/src/ui/layout.tsx +54 -0
- package/src/ui/modules/docs/page.tsx +105 -0
- package/src/ui/modules/page.tsx +30 -0
- package/src/ui/page.tsx +15 -0
- package/src/ui/rbac/pages/rbac-admin.tsx +551 -0
- package/src/ui/router.tsx +69 -0
- package/src/ui/session-manager/components/sessions-list.tsx +303 -0
- package/src/ui/session-manager/pages/sessions-page.tsx +22 -0
- package/src/ui/settings/page.tsx +73 -0
- package/src/ui/settings-page.tsx +97 -0
|
@@ -8,11 +8,11 @@ var _server = require("@arch-cadre/intl/server");
|
|
|
8
8
|
var _ui = require("@arch-cadre/ui");
|
|
9
9
|
var _table = require("@arch-cadre/ui/components/table");
|
|
10
10
|
var React = _interopRequireWildcard(require("react"));
|
|
11
|
-
var
|
|
11
|
+
var _index = require("../../../actions/activity-log/index.cjs");
|
|
12
12
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
13
13
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
14
14
|
async function ActivityLogPage() {
|
|
15
|
-
const logs = await (0,
|
|
15
|
+
const logs = await (0, _index.getActivityLogs)();
|
|
16
16
|
const {
|
|
17
17
|
t
|
|
18
18
|
} = await (0, _server.getTranslation)();
|
|
@@ -7,14 +7,14 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.UpdateEmailForm = UpdateEmailForm;
|
|
8
8
|
exports.UpdatePasswordForm = UpdatePasswordForm;
|
|
9
9
|
exports.UpdateProfileForm = UpdateProfileForm;
|
|
10
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
11
|
-
var React = _react;
|
|
12
10
|
var _intl = require("@arch-cadre/intl");
|
|
13
11
|
var _ui = require("@arch-cadre/ui");
|
|
14
12
|
var _avatar = require("@arch-cadre/ui/components/avatar");
|
|
15
13
|
var _button = require("@arch-cadre/ui/components/button");
|
|
16
14
|
var _input = require("@arch-cadre/ui/components/input");
|
|
17
15
|
var _label = require("@arch-cadre/ui/components/label");
|
|
16
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
17
|
+
var React = _react;
|
|
18
18
|
var _profile = require("../../../actions/profile.cjs");
|
|
19
19
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
20
20
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import * as React from "react";
|
|
3
2
|
import { useTranslation } from "@arch-cadre/intl";
|
|
4
3
|
import { cn, Icon, toast } from "@arch-cadre/ui";
|
|
5
4
|
import {
|
|
@@ -10,6 +9,7 @@ import {
|
|
|
10
9
|
import { Button } from "@arch-cadre/ui/components/button";
|
|
11
10
|
import { Input } from "@arch-cadre/ui/components/input";
|
|
12
11
|
import { Label } from "@arch-cadre/ui/components/label";
|
|
12
|
+
import * as React from "react";
|
|
13
13
|
import { useActionState, useRef, useState, useTransition } from "react";
|
|
14
14
|
import {
|
|
15
15
|
updateEmailAction,
|
|
@@ -14,7 +14,7 @@ var _input = require("@arch-cadre/ui/components/input");
|
|
|
14
14
|
var _table = require("@arch-cadre/ui/components/table");
|
|
15
15
|
var _react = _interopRequireWildcard(require("react"));
|
|
16
16
|
var React = _react;
|
|
17
|
-
var
|
|
17
|
+
var _index = require("../../../actions/rbac/index.cjs");
|
|
18
18
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
19
19
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
20
20
|
function RbacAdminPage() {
|
|
@@ -38,7 +38,7 @@ function RbacAdminPage() {
|
|
|
38
38
|
const loadData = (0, _react.useCallback)(async () => {
|
|
39
39
|
setLoading(true);
|
|
40
40
|
try {
|
|
41
|
-
const [r, p, u] = await Promise.all([(0,
|
|
41
|
+
const [r, p, u] = await Promise.all([(0, _index.getRoles)(), (0, _index.getPermissions)(), (0, _index.getUsers)()]);
|
|
42
42
|
setRoles(r);
|
|
43
43
|
setPermissions(p);
|
|
44
44
|
setUsers(u);
|
|
@@ -51,7 +51,7 @@ function RbacAdminPage() {
|
|
|
51
51
|
}, []);
|
|
52
52
|
const loadRolePermissions = (0, _react.useCallback)(async roleId => {
|
|
53
53
|
try {
|
|
54
|
-
const p = await (0,
|
|
54
|
+
const p = await (0, _index.getRolePermissions)(roleId);
|
|
55
55
|
setRolePermissions(p);
|
|
56
56
|
} catch (e) {
|
|
57
57
|
console.error(`[RBAC] Failed to load permissions for role ${roleId}:`, e);
|
|
@@ -60,7 +60,7 @@ function RbacAdminPage() {
|
|
|
60
60
|
}, []);
|
|
61
61
|
const loadUserRbacData = (0, _react.useCallback)(async userId => {
|
|
62
62
|
try {
|
|
63
|
-
const data = await (0,
|
|
63
|
+
const data = await (0, _index.getUserRbacData)(userId);
|
|
64
64
|
setUserRbacData(data);
|
|
65
65
|
} catch (e) {
|
|
66
66
|
console.error(`[RBAC] Failed to load RBAC data for user ${userId}:`, e);
|
|
@@ -83,7 +83,7 @@ function RbacAdminPage() {
|
|
|
83
83
|
const handleCreateRole = (0, _react.useCallback)(async () => {
|
|
84
84
|
if (!newRoleName) return;
|
|
85
85
|
try {
|
|
86
|
-
await (0,
|
|
86
|
+
await (0, _index.createRole)(newRoleName);
|
|
87
87
|
setNewRoleName("");
|
|
88
88
|
_ui.toast.success(t("Role created."));
|
|
89
89
|
loadData();
|
|
@@ -94,7 +94,7 @@ function RbacAdminPage() {
|
|
|
94
94
|
const handleCreatePermission = (0, _react.useCallback)(async () => {
|
|
95
95
|
if (!newPermissionName) return;
|
|
96
96
|
try {
|
|
97
|
-
await (0,
|
|
97
|
+
await (0, _index.createPermission)(newPermissionName);
|
|
98
98
|
setNewPermissionName("");
|
|
99
99
|
_ui.toast.success(t("Permission created."));
|
|
100
100
|
loadData();
|
|
@@ -104,7 +104,7 @@ function RbacAdminPage() {
|
|
|
104
104
|
}, [newPermissionName, loadData]);
|
|
105
105
|
const handleDeleteRole = (0, _react.useCallback)(async id => {
|
|
106
106
|
try {
|
|
107
|
-
await (0,
|
|
107
|
+
await (0, _index.deleteRole)(id);
|
|
108
108
|
_ui.toast.success(t("Role deleted."));
|
|
109
109
|
if (selectedRole?.id === id) setSelectedRole(null);
|
|
110
110
|
loadData();
|
|
@@ -114,7 +114,7 @@ function RbacAdminPage() {
|
|
|
114
114
|
}, [selectedRole, loadData]);
|
|
115
115
|
const handleDeletePermission = (0, _react.useCallback)(async id => {
|
|
116
116
|
try {
|
|
117
|
-
await (0,
|
|
117
|
+
await (0, _index.deletePermission)(id);
|
|
118
118
|
_ui.toast.success(t("Permission deleted."));
|
|
119
119
|
loadData();
|
|
120
120
|
} catch (_e) {
|
|
@@ -125,9 +125,9 @@ function RbacAdminPage() {
|
|
|
125
125
|
if (!selectedRole) return;
|
|
126
126
|
try {
|
|
127
127
|
if (hasIt) {
|
|
128
|
-
await (0,
|
|
128
|
+
await (0, _index.revokePermissionFromRole)(selectedRole.id, permId);
|
|
129
129
|
} else {
|
|
130
|
-
await (0,
|
|
130
|
+
await (0, _index.assignPermissionToRole)(selectedRole.id, permId);
|
|
131
131
|
}
|
|
132
132
|
loadRolePermissions(selectedRole.id);
|
|
133
133
|
} catch (_e) {
|
|
@@ -138,9 +138,9 @@ function RbacAdminPage() {
|
|
|
138
138
|
if (!selectedUser) return;
|
|
139
139
|
try {
|
|
140
140
|
if (hasIt) {
|
|
141
|
-
await (0,
|
|
141
|
+
await (0, _index.revokeRoleFromUser)(selectedUser.id, roleId);
|
|
142
142
|
} else {
|
|
143
|
-
await (0,
|
|
143
|
+
await (0, _index.assignRoleToUser)(selectedUser.id, roleId);
|
|
144
144
|
}
|
|
145
145
|
loadUserRbacData(selectedUser.id);
|
|
146
146
|
} catch (_e) {
|
|
@@ -151,9 +151,9 @@ function RbacAdminPage() {
|
|
|
151
151
|
if (!selectedUser) return;
|
|
152
152
|
try {
|
|
153
153
|
if (hasIt) {
|
|
154
|
-
await (0,
|
|
154
|
+
await (0, _index.revokePermissionFromUser)(selectedUser.id, permId);
|
|
155
155
|
} else {
|
|
156
|
-
await (0,
|
|
156
|
+
await (0, _index.assignPermissionToUser)(selectedUser.id, permId);
|
|
157
157
|
}
|
|
158
158
|
loadUserRbacData(selectedUser.id);
|
|
159
159
|
} catch (_e) {
|
|
@@ -5,15 +5,15 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.SessionsList = SessionsList;
|
|
8
|
-
var _react = _interopRequireWildcard(require("react"));
|
|
9
|
-
var React = _react;
|
|
10
8
|
var _intl = require("@arch-cadre/intl");
|
|
11
9
|
var _ui = require("@arch-cadre/ui");
|
|
12
10
|
var _alertDialog = require("@arch-cadre/ui/components/alert-dialog");
|
|
13
11
|
var _badge = require("@arch-cadre/ui/components/badge");
|
|
14
12
|
var _button = require("@arch-cadre/ui/components/button");
|
|
15
13
|
var _navigation = require("next/navigation");
|
|
16
|
-
var
|
|
14
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
15
|
+
var React = _react;
|
|
16
|
+
var _index = require("../../../actions/session-manager/index.cjs");
|
|
17
17
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
18
18
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
19
19
|
const CORE_SESSION_KEYS = ["id", "createdAt", "expiresAt", "isCurrent", "userId"];
|
|
@@ -49,7 +49,7 @@ function SessionsList({
|
|
|
49
49
|
const handleRevokeSession = sessionId => {
|
|
50
50
|
setRevokingSessionId(sessionId);
|
|
51
51
|
startTransition(async () => {
|
|
52
|
-
const result = await (0,
|
|
52
|
+
const result = await (0, _index.revokeSessionAction)(sessionId);
|
|
53
53
|
if (result.error) {
|
|
54
54
|
_ui.toast.error(result.error);
|
|
55
55
|
} else {
|
|
@@ -61,7 +61,7 @@ function SessionsList({
|
|
|
61
61
|
};
|
|
62
62
|
const handleRevokeAllOther = () => {
|
|
63
63
|
startTransition(async () => {
|
|
64
|
-
const result = await (0,
|
|
64
|
+
const result = await (0, _index.revokeAllOtherSessionsAction)();
|
|
65
65
|
if (result.error) {
|
|
66
66
|
_ui.toast.error(result.error);
|
|
67
67
|
} else {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import * as React from "react";
|
|
3
2
|
import { useTranslation } from "@arch-cadre/intl";
|
|
4
3
|
import { cn, Icon, toast } from "@arch-cadre/ui";
|
|
5
4
|
import {
|
|
@@ -16,6 +15,7 @@ import {
|
|
|
16
15
|
import { Badge } from "@arch-cadre/ui/components/badge";
|
|
17
16
|
import { Button } from "@arch-cadre/ui/components/button";
|
|
18
17
|
import { useRouter } from "next/navigation";
|
|
18
|
+
import * as React from "react";
|
|
19
19
|
import { useState, useTransition } from "react";
|
|
20
20
|
import {
|
|
21
21
|
revokeAllOtherSessionsAction,
|
|
@@ -4,9 +4,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
module.exports = SessionsSettingsPage;
|
|
7
|
-
var React = _interopRequireWildcard(require("react"));
|
|
8
7
|
var _server = require("@arch-cadre/intl/server");
|
|
9
|
-
var
|
|
8
|
+
var React = _interopRequireWildcard(require("react"));
|
|
9
|
+
var _index = require("../../../actions/session-manager/index.cjs");
|
|
10
10
|
var _sessionsList = require("../components/sessions-list.cjs");
|
|
11
11
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
12
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -16,7 +16,7 @@ async function SessionsSettingsPage() {
|
|
|
16
16
|
} = await (0, _server.getTranslation)();
|
|
17
17
|
const {
|
|
18
18
|
sessions
|
|
19
|
-
} = await (0,
|
|
19
|
+
} = await (0, _index.getSessionsAction)();
|
|
20
20
|
return /* @__PURE__ */React.createElement("div", {
|
|
21
21
|
className: "space-y-6"
|
|
22
22
|
}, /* @__PURE__ */React.createElement("div", null, /* @__PURE__ */React.createElement("h2", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
1
|
import { getTranslation } from "@arch-cadre/intl/server";
|
|
2
|
+
import * as React from "react";
|
|
3
3
|
import { getSessionsAction } from "../../../actions/session-manager/index.mjs";
|
|
4
4
|
import { SessionsList } from "../components/sessions-list.mjs";
|
|
5
5
|
export default async function SessionsSettingsPage() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arch-cadre/panel",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Panel module for Kryo framework",
|
|
6
6
|
"exports": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
|
+
"src",
|
|
26
27
|
"locales",
|
|
27
28
|
"manifest.json"
|
|
28
29
|
],
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"lint": "biome check --write"
|
|
37
38
|
},
|
|
38
39
|
"dependencies": {
|
|
39
|
-
"@arch-cadre/modules": "^0.0.
|
|
40
|
+
"@arch-cadre/modules": "^0.0.80",
|
|
40
41
|
"@hookform/resolvers": "^3.10.0",
|
|
41
42
|
"react-hook-form": "^7.54.2",
|
|
42
43
|
"@iconify-json/solar": "^1.2.2",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"zod": "^3.24.1"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@arch-cadre/core": "^0.0.
|
|
52
|
+
"@arch-cadre/core": "^0.0.54",
|
|
52
53
|
"@types/adm-zip": "^0.5.7",
|
|
53
54
|
"@types/node": "^20.19.9",
|
|
54
55
|
"@types/react": "^19",
|
|
@@ -57,9 +58,9 @@
|
|
|
57
58
|
"unbuild": "^3.6.1"
|
|
58
59
|
},
|
|
59
60
|
"peerDependencies": {
|
|
60
|
-
"@arch-cadre/core": "^0.0.
|
|
61
|
-
"@arch-cadre/intl": "^0.0.
|
|
62
|
-
"@arch-cadre/ui": "^0.0.
|
|
61
|
+
"@arch-cadre/core": "^0.0.54",
|
|
62
|
+
"@arch-cadre/intl": "^0.0.54",
|
|
63
|
+
"@arch-cadre/ui": "^0.0.54",
|
|
63
64
|
"next": ">=13.0.0",
|
|
64
65
|
"react": "^19.0.0",
|
|
65
66
|
"react-dom": "^19.0.0"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { signOut as signOutAction } from "@arch-cadre/core/server";
|
|
4
|
+
import { getTranslation } from "@arch-cadre/intl/server";
|
|
5
|
+
import { revalidatePath } from "next/cache";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Modern Logout Action delegating to Core Auth
|
|
9
|
+
*/
|
|
10
|
+
export async function logoutAction() {
|
|
11
|
+
await signOutAction();
|
|
12
|
+
const { t } = await getTranslation();
|
|
13
|
+
|
|
14
|
+
t("Pos");
|
|
15
|
+
|
|
16
|
+
revalidatePath("/", "layout");
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { db, userTable } from "@arch-cadre/core/server";
|
|
4
|
+
import { desc, eq } from "drizzle-orm";
|
|
5
|
+
import { activityLogsTable } from "../../schema/activity-log";
|
|
6
|
+
|
|
7
|
+
export async function getActivityLogs() {
|
|
8
|
+
return await db
|
|
9
|
+
.select({
|
|
10
|
+
log: activityLogsTable,
|
|
11
|
+
user: { name: userTable.name, email: userTable.email },
|
|
12
|
+
})
|
|
13
|
+
.from(activityLogsTable)
|
|
14
|
+
.leftJoin(userTable, eq(activityLogsTable.userId, userTable.id))
|
|
15
|
+
.orderBy(desc(activityLogsTable.createdAt))
|
|
16
|
+
.limit(100);
|
|
17
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import { exec } from "node:child_process";
|
|
4
|
+
import fs, { constants } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
import {
|
|
8
|
+
db,
|
|
9
|
+
eventBus,
|
|
10
|
+
getModulesDir,
|
|
11
|
+
systemModulesTable,
|
|
12
|
+
} from "@arch-cadre/core/server";
|
|
13
|
+
import {
|
|
14
|
+
getModuleStatus,
|
|
15
|
+
getModules,
|
|
16
|
+
ModuleManifestSchema,
|
|
17
|
+
toggleModuleState,
|
|
18
|
+
} from "@arch-cadre/modules/server";
|
|
19
|
+
import AdmZip from "adm-zip";
|
|
20
|
+
import { revalidatePath, unstable_noStore } from "next/cache";
|
|
21
|
+
|
|
22
|
+
export async function toggleModuleAction(moduleId: string, isEnabled: boolean) {
|
|
23
|
+
try {
|
|
24
|
+
console.log(
|
|
25
|
+
`[Module:ModuleManager] Toggle request for ${moduleId} -> ${isEnabled}`,
|
|
26
|
+
);
|
|
27
|
+
const result = await toggleModuleState(moduleId, isEnabled);
|
|
28
|
+
await eventBus.publish("system:module:toggle", { moduleId, isEnabled });
|
|
29
|
+
revalidatePath("/admin/modules");
|
|
30
|
+
revalidatePath("/module/module-manager");
|
|
31
|
+
return result;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(
|
|
34
|
+
`[Module:ModuleManager] Error toggling module ${moduleId}:`,
|
|
35
|
+
error,
|
|
36
|
+
);
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function uploadModuleAction(formData: FormData) {
|
|
42
|
+
try {
|
|
43
|
+
const file = formData.get("file") as File;
|
|
44
|
+
if (!file) throw new Error("No file provided");
|
|
45
|
+
|
|
46
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
47
|
+
const zip = new AdmZip(buffer);
|
|
48
|
+
const zipEntries = zip.getEntries();
|
|
49
|
+
|
|
50
|
+
const manifestEntry = zipEntries.find((e) =>
|
|
51
|
+
e.entryName.endsWith("manifest.json"),
|
|
52
|
+
);
|
|
53
|
+
if (!manifestEntry) throw new Error("ZIP must contain manifest.json");
|
|
54
|
+
|
|
55
|
+
const moduleRootInZip = manifestEntry.entryName.replace(
|
|
56
|
+
"manifest.json",
|
|
57
|
+
"",
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const manifestRaw = JSON.parse(manifestEntry.getData().toString("utf8"));
|
|
61
|
+
const manifest = ModuleManifestSchema.parse(manifestRaw);
|
|
62
|
+
|
|
63
|
+
const modulesDir = await getModulesDir();
|
|
64
|
+
const targetDir = path.join(modulesDir, manifest.id);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await fs.access(targetDir);
|
|
68
|
+
throw new Error(`Module with ID "${manifest.id}" already exists.`);
|
|
69
|
+
} catch (e: any) {
|
|
70
|
+
if (e.message.includes("already exists")) throw e;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
74
|
+
|
|
75
|
+
for (const entry of zipEntries) {
|
|
76
|
+
if (entry.entryName.startsWith(moduleRootInZip) && !entry.isDirectory) {
|
|
77
|
+
const relativePath = entry.entryName.slice(moduleRootInZip.length);
|
|
78
|
+
const fullPath = path.join(targetDir, relativePath);
|
|
79
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
80
|
+
await fs.writeFile(fullPath, entry.getData());
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
await db
|
|
85
|
+
.insert(systemModulesTable)
|
|
86
|
+
.values({
|
|
87
|
+
id: manifest.id,
|
|
88
|
+
enabled: false,
|
|
89
|
+
installed: false,
|
|
90
|
+
system: manifest.system ?? false,
|
|
91
|
+
})
|
|
92
|
+
.onConflictDoNothing();
|
|
93
|
+
|
|
94
|
+
console.log(
|
|
95
|
+
`[Module:ModuleManager] Module "${manifest.id}" uploaded and extracted.`,
|
|
96
|
+
);
|
|
97
|
+
await eventBus.publish("system:module:upload", {
|
|
98
|
+
moduleId: manifest.id,
|
|
99
|
+
manifest,
|
|
100
|
+
});
|
|
101
|
+
revalidatePath("/admin/modules");
|
|
102
|
+
revalidatePath("/module/module-manager");
|
|
103
|
+
return { success: true };
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(`[Module:ModuleManager] Upload error:`, error);
|
|
106
|
+
return { success: false, error: (error as Error).message };
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export async function checkDiskWriteAccess() {
|
|
111
|
+
const modulesDir = await getModulesDir();
|
|
112
|
+
try {
|
|
113
|
+
await fs.access(modulesDir, constants.W_OK);
|
|
114
|
+
return { canWrite: true };
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error(
|
|
117
|
+
"[Module:ModuleManager] No write access to modules directory:",
|
|
118
|
+
error,
|
|
119
|
+
);
|
|
120
|
+
return { canWrite: false };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function getModuleStatusAction(moduleId: string) {
|
|
125
|
+
unstable_noStore();
|
|
126
|
+
return await getModuleStatus(moduleId);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function getModulesAction() {
|
|
130
|
+
unstable_noStore();
|
|
131
|
+
return await getModules();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function getModuleDocumentation(moduleId: string) {
|
|
135
|
+
const modulesDir = await getModulesDir();
|
|
136
|
+
const docsDir = path.join(modulesDir, moduleId, "docs");
|
|
137
|
+
try {
|
|
138
|
+
const exists = await fs
|
|
139
|
+
.access(docsDir)
|
|
140
|
+
.then(() => true)
|
|
141
|
+
.catch(() => false);
|
|
142
|
+
if (!exists) return null;
|
|
143
|
+
|
|
144
|
+
const files = await fs.readdir(docsDir);
|
|
145
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
146
|
+
|
|
147
|
+
if (mdFiles.length === 0) return null;
|
|
148
|
+
|
|
149
|
+
const docs = await Promise.all(
|
|
150
|
+
mdFiles.map(async (file) => {
|
|
151
|
+
const content = await fs.readFile(path.join(docsDir, file), "utf-8");
|
|
152
|
+
return {
|
|
153
|
+
filename: file,
|
|
154
|
+
title: file
|
|
155
|
+
.replace(".md", "")
|
|
156
|
+
.split("-")
|
|
157
|
+
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
|
|
158
|
+
.join(" "),
|
|
159
|
+
content,
|
|
160
|
+
};
|
|
161
|
+
}),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
return docs;
|
|
165
|
+
} catch (_error) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
|
|
3
|
+
import type { SessionFlags } from "@arch-cadre/core";
|
|
4
|
+
import {
|
|
5
|
+
checkEmailAvailability,
|
|
6
|
+
createEmailVerificationRequest,
|
|
7
|
+
createSession,
|
|
8
|
+
eventBus,
|
|
9
|
+
filesystemService,
|
|
10
|
+
generateSessionToken,
|
|
11
|
+
getCurrentSession,
|
|
12
|
+
getUserPasswordHash,
|
|
13
|
+
invalidateUserSessions,
|
|
14
|
+
sendVerificationEmail,
|
|
15
|
+
setEmailVerificationRequestCookie,
|
|
16
|
+
setSessionTokenCookie,
|
|
17
|
+
updateUserAwatar,
|
|
18
|
+
updateUserName,
|
|
19
|
+
updateUserPassword,
|
|
20
|
+
verifyEmailInput,
|
|
21
|
+
verifyPasswordHash,
|
|
22
|
+
verifyPasswordStrength,
|
|
23
|
+
} from "@arch-cadre/core/server";
|
|
24
|
+
import { getTranslation } from "@arch-cadre/intl/server";
|
|
25
|
+
import { revalidatePath } from "next/cache";
|
|
26
|
+
import { redirect } from "next/navigation";
|
|
27
|
+
import type { ActionResult } from "../types";
|
|
28
|
+
|
|
29
|
+
export async function updatePasswordAction(
|
|
30
|
+
_prev: ActionResult,
|
|
31
|
+
formData: FormData,
|
|
32
|
+
): Promise<ActionResult> {
|
|
33
|
+
const { t } = await getTranslation();
|
|
34
|
+
const { session, user } = await getCurrentSession();
|
|
35
|
+
if (session === null || user === null) {
|
|
36
|
+
return { success: false, message: t("Not authenticated") };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (user.registered2FA && !session.two_factor_verified) {
|
|
40
|
+
return { success: false, message: t("Forbidden") };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const password = formData.get("password");
|
|
44
|
+
const newPassword = formData.get("new_password");
|
|
45
|
+
|
|
46
|
+
if (typeof password !== "string" || typeof newPassword !== "string") {
|
|
47
|
+
return { success: false, message: t("Invalid or missing fields") };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!verifyPasswordStrength(newPassword)) {
|
|
51
|
+
return { success: false, message: t("Weak password") };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const passwordHash = await getUserPasswordHash(user.id);
|
|
55
|
+
if (!passwordHash) return { success: false, message: t("Internal error") };
|
|
56
|
+
|
|
57
|
+
const validPassword = await verifyPasswordHash(passwordHash, password);
|
|
58
|
+
if (!validPassword) {
|
|
59
|
+
return { success: false, message: t("Incorrect password") };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await invalidateUserSessions(user.id);
|
|
63
|
+
await updateUserPassword(user.id, newPassword);
|
|
64
|
+
|
|
65
|
+
const sessionToken = await generateSessionToken();
|
|
66
|
+
const sessionFlags: SessionFlags = {
|
|
67
|
+
twoFactorVerified: session.two_factor_verified ?? false,
|
|
68
|
+
};
|
|
69
|
+
const newSession = await createSession(sessionToken, user.id, sessionFlags);
|
|
70
|
+
await setSessionTokenCookie(sessionToken, newSession.expiresAt);
|
|
71
|
+
|
|
72
|
+
await eventBus.publish(
|
|
73
|
+
"activity.create",
|
|
74
|
+
{
|
|
75
|
+
action: "profile.updated.password",
|
|
76
|
+
description: `User ${user?.name} updated their password`,
|
|
77
|
+
userId: user?.id,
|
|
78
|
+
metadata: {},
|
|
79
|
+
},
|
|
80
|
+
"profile",
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return { success: true, message: t("Updated password") };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function updateEmailAction(
|
|
87
|
+
_prev: ActionResult,
|
|
88
|
+
formData: FormData,
|
|
89
|
+
): Promise<ActionResult> {
|
|
90
|
+
const { t } = await getTranslation();
|
|
91
|
+
const { session, user } = await getCurrentSession();
|
|
92
|
+
if (session === null || user === null)
|
|
93
|
+
return { success: false, message: t("Not authenticated") };
|
|
94
|
+
if (user.registered2FA && !session.two_factor_verified)
|
|
95
|
+
return { success: false, message: t("Forbidden") };
|
|
96
|
+
|
|
97
|
+
const email = formData.get("email");
|
|
98
|
+
if (typeof email !== "string" || email === "") {
|
|
99
|
+
return { success: false, message: t("Please enter your email") };
|
|
100
|
+
}
|
|
101
|
+
if (!verifyEmailInput(email)) {
|
|
102
|
+
return { success: false, message: t("Please enter a valid email") };
|
|
103
|
+
}
|
|
104
|
+
const emailAvailable = await checkEmailAvailability(email);
|
|
105
|
+
if (!emailAvailable) {
|
|
106
|
+
return { success: false, message: t("This email is already used") };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const verificationRequest = await createEmailVerificationRequest(
|
|
110
|
+
user.id,
|
|
111
|
+
email,
|
|
112
|
+
);
|
|
113
|
+
await sendVerificationEmail(
|
|
114
|
+
verificationRequest.email,
|
|
115
|
+
verificationRequest.code,
|
|
116
|
+
);
|
|
117
|
+
await setEmailVerificationRequestCookie(verificationRequest);
|
|
118
|
+
|
|
119
|
+
await eventBus.publish(
|
|
120
|
+
"activity.create",
|
|
121
|
+
{
|
|
122
|
+
action: "profile.updated.email",
|
|
123
|
+
description: `User ${user?.name} updated their email`,
|
|
124
|
+
userId: user?.id,
|
|
125
|
+
metadata: { email },
|
|
126
|
+
},
|
|
127
|
+
"profile",
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return redirect("/verify-email");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function updateProfileAction(
|
|
134
|
+
name: string,
|
|
135
|
+
image?: File,
|
|
136
|
+
): Promise<{ error?: string; success?: boolean; message?: string }> {
|
|
137
|
+
const { session, user } = await getCurrentSession();
|
|
138
|
+
|
|
139
|
+
const { t } = await getTranslation();
|
|
140
|
+
|
|
141
|
+
if (session === null || user === null)
|
|
142
|
+
return { error: t("Not authenticated") };
|
|
143
|
+
|
|
144
|
+
if (!name || name.trim().length === 0)
|
|
145
|
+
return { error: t("Name is required") };
|
|
146
|
+
if (name.trim().length > 100) return { error: t("Name is too long") };
|
|
147
|
+
|
|
148
|
+
if (image instanceof File) {
|
|
149
|
+
const uploaded = await filesystemService.upload(image, "vercel-blob");
|
|
150
|
+
|
|
151
|
+
if ("error" in uploaded) {
|
|
152
|
+
return { error: uploaded.error };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await updateUserAwatar(user.id, uploaded.url);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await updateUserName(user.id, name.trim());
|
|
159
|
+
revalidatePath("/profile"); // Updated path
|
|
160
|
+
|
|
161
|
+
await eventBus.publish(
|
|
162
|
+
"activity.create",
|
|
163
|
+
{
|
|
164
|
+
action: "profile.updated",
|
|
165
|
+
description: `User ${user?.name} updated their profile`,
|
|
166
|
+
userId: user?.id,
|
|
167
|
+
metadata: { name, imageUrl: image instanceof File ? image.name : null },
|
|
168
|
+
},
|
|
169
|
+
"profile",
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return { success: true, message: t("Profile updated successfully") };
|
|
173
|
+
}
|