@arch-cadre/panel 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/actions.cjs +18 -0
- package/dist/actions/actions.d.ts +4 -0
- package/dist/actions/actions.mjs +10 -0
- package/dist/actions/index.cjs +27 -0
- package/dist/actions/index.d.ts +2 -0
- package/dist/actions/index.mjs +2 -0
- package/dist/actions/manager.cjs +138 -0
- package/dist/actions/manager.d.ts +14 -0
- package/dist/actions/manager.mjs +135 -0
- package/dist/actions/profile.cjs +169 -0
- package/dist/actions/profile.d.ts +8 -0
- package/dist/actions/profile.mjs +135 -0
- package/dist/actions/settings.cjs +26 -0
- package/dist/actions/settings.d.ts +7 -0
- package/dist/actions/settings.mjs +20 -0
- package/dist/index.cjs +29 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +25 -0
- package/dist/intl.d.ts +9 -0
- package/dist/navigation.cjs +38 -0
- package/dist/navigation.d.ts +2 -0
- package/dist/navigation.mjs +39 -0
- package/dist/routes.cjs +71 -0
- package/dist/routes.d.ts +4 -0
- package/dist/routes.mjs +71 -0
- package/dist/types.cjs +1 -0
- package/dist/types.d.ts +13 -0
- package/dist/types.mjs +0 -0
- package/dist/ui/[...catchAll]/page.cjs +127 -0
- package/dist/ui/[...catchAll]/page.d.ts +13 -0
- package/dist/ui/[...catchAll]/page.mjs +88 -0
- package/dist/ui/components/app-content.cjs +49 -0
- package/dist/ui/components/app-content.d.ts +6 -0
- package/dist/ui/components/app-content.mjs +34 -0
- package/dist/ui/components/app-header.cjs +55 -0
- package/dist/ui/components/app-header.d.ts +6 -0
- package/dist/ui/components/app-header.mjs +45 -0
- package/dist/ui/components/app-sidebar.cjs +133 -0
- package/dist/ui/components/app-sidebar.d.ts +17 -0
- package/dist/ui/components/app-sidebar.mjs +142 -0
- package/dist/ui/components/app-user.cjs +102 -0
- package/dist/ui/components/app-user.d.ts +2 -0
- package/dist/ui/components/app-user.mjs +92 -0
- package/dist/ui/components/breadcrumb-slot.cjs +35 -0
- package/dist/ui/components/breadcrumb-slot.d.ts +2 -0
- package/dist/ui/components/breadcrumb-slot.mjs +31 -0
- package/dist/ui/components/manager/module-card.cjs +213 -0
- package/dist/ui/components/manager/module-card.d.ts +12 -0
- package/dist/ui/components/manager/module-card.mjs +197 -0
- package/dist/ui/components/manager/module-list.cjs +52 -0
- package/dist/ui/components/manager/module-list.d.ts +4 -0
- package/dist/ui/components/manager/module-list.mjs +16 -0
- package/dist/ui/components/manager/module-upload.cjs +81 -0
- package/dist/ui/components/manager/module-upload.d.ts +2 -0
- package/dist/ui/components/manager/module-upload.mjs +68 -0
- package/dist/ui/components/profile/components.cjs +239 -0
- package/dist/ui/components/profile/components.d.ts +8 -0
- package/dist/ui/components/profile/components.mjs +219 -0
- package/dist/ui/components/profile/link.cjs +25 -0
- package/dist/ui/components/profile/link.d.ts +1 -0
- package/dist/ui/components/profile/link.mjs +13 -0
- package/dist/ui/components/profile/page.cjs +41 -0
- package/dist/ui/components/profile/page.d.ts +1 -0
- package/dist/ui/components/profile/page.mjs +21 -0
- package/dist/ui/components/sidebar-slot.cjs +49 -0
- package/dist/ui/components/sidebar-slot.d.ts +2 -0
- package/dist/ui/components/sidebar-slot.mjs +33 -0
- package/dist/ui/dashboard/page.cjs +31 -0
- package/dist/ui/dashboard/page.d.ts +2 -0
- package/dist/ui/dashboard/page.mjs +9 -0
- package/dist/ui/error.cjs +50 -0
- package/dist/ui/error.d.ts +7 -0
- package/dist/ui/error.mjs +35 -0
- package/dist/ui/layout.cjs +48 -0
- package/dist/ui/layout.d.ts +4 -0
- package/dist/ui/layout.mjs +35 -0
- package/dist/ui/modules/docs/page.cjs +98 -0
- package/dist/ui/modules/docs/page.d.ts +6 -0
- package/dist/ui/modules/docs/page.mjs +46 -0
- package/dist/ui/modules/page.cjs +30 -0
- package/dist/ui/modules/page.d.ts +2 -0
- package/dist/ui/modules/page.mjs +10 -0
- package/dist/ui/page.cjs +18 -0
- package/dist/ui/page.d.ts +1 -0
- package/dist/ui/page.mjs +9 -0
- package/dist/ui/router.cjs +61 -0
- package/dist/ui/router.d.ts +5 -0
- package/dist/ui/router.mjs +51 -0
- package/dist/ui/settings/page.cjs +61 -0
- package/dist/ui/settings/page.d.ts +2 -0
- package/dist/ui/settings/page.mjs +22 -0
- package/dist/ui/settings-page.cjs +76 -0
- package/dist/ui/settings-page.d.ts +2 -0
- package/dist/ui/settings-page.mjs +57 -0
- package/locales/en/global.json +80 -0
- package/manifest.json +11 -0
- package/package.json +67 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use server";
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.logoutAction = logoutAction;
|
|
8
|
+
var _server = require("@arch-cadre/core/server");
|
|
9
|
+
var _server2 = require("@arch-cadre/intl/server");
|
|
10
|
+
var _cache = require("next/cache");
|
|
11
|
+
async function logoutAction() {
|
|
12
|
+
await (0, _server.signOut)();
|
|
13
|
+
const {
|
|
14
|
+
t
|
|
15
|
+
} = await (0, _server2.getTranslation)();
|
|
16
|
+
t("Pos");
|
|
17
|
+
(0, _cache.revalidatePath)("/", "layout");
|
|
18
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import { signOut as signOutAction } from "@arch-cadre/core/server";
|
|
3
|
+
import { getTranslation } from "@arch-cadre/intl/server";
|
|
4
|
+
import { revalidatePath } from "next/cache";
|
|
5
|
+
export async function logoutAction() {
|
|
6
|
+
await signOutAction();
|
|
7
|
+
const { t } = await getTranslation();
|
|
8
|
+
t("Pos");
|
|
9
|
+
revalidatePath("/", "layout");
|
|
10
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _actions = require("./actions.cjs");
|
|
7
|
+
Object.keys(_actions).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _actions[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _actions[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
var _settings = require("./settings.cjs");
|
|
18
|
+
Object.keys(_settings).forEach(function (key) {
|
|
19
|
+
if (key === "default" || key === "__esModule") return;
|
|
20
|
+
if (key in exports && exports[key] === _settings[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _settings[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use server";
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.checkDiskWriteAccess = checkDiskWriteAccess;
|
|
8
|
+
exports.getModuleDocumentation = getModuleDocumentation;
|
|
9
|
+
exports.getModuleStatusAction = getModuleStatusAction;
|
|
10
|
+
exports.getModulesAction = getModulesAction;
|
|
11
|
+
exports.toggleModuleAction = toggleModuleAction;
|
|
12
|
+
exports.uploadModuleAction = uploadModuleAction;
|
|
13
|
+
var _promises = _interopRequireWildcard(require("node:fs/promises"));
|
|
14
|
+
var _nodePath = _interopRequireDefault(require("node:path"));
|
|
15
|
+
var _server = require("@arch-cadre/core/server");
|
|
16
|
+
var _server2 = require("@arch-cadre/modules/server");
|
|
17
|
+
var _admZip = _interopRequireDefault(require("adm-zip"));
|
|
18
|
+
var _cache = require("next/cache");
|
|
19
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
20
|
+
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); }
|
|
21
|
+
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; }
|
|
22
|
+
async function toggleModuleAction(moduleId, isEnabled) {
|
|
23
|
+
try {
|
|
24
|
+
console.log(`[Module:ModuleManager] Toggle request for ${moduleId} -> ${isEnabled}`);
|
|
25
|
+
const result = await (0, _server2.toggleModuleState)(moduleId, isEnabled);
|
|
26
|
+
await _server.eventBus.publish("system:module:toggle", {
|
|
27
|
+
moduleId,
|
|
28
|
+
isEnabled
|
|
29
|
+
});
|
|
30
|
+
(0, _cache.revalidatePath)("/admin/modules");
|
|
31
|
+
(0, _cache.revalidatePath)("/module/module-manager");
|
|
32
|
+
return result;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
console.error(`[Module:ModuleManager] Error toggling module ${moduleId}:`, error);
|
|
35
|
+
throw error;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function uploadModuleAction(formData) {
|
|
39
|
+
try {
|
|
40
|
+
const file = formData.get("file");
|
|
41
|
+
if (!file) throw new Error("No file provided");
|
|
42
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
43
|
+
const zip = new _admZip.default(buffer);
|
|
44
|
+
const zipEntries = zip.getEntries();
|
|
45
|
+
const manifestEntry = zipEntries.find(e => e.entryName.endsWith("manifest.json"));
|
|
46
|
+
if (!manifestEntry) throw new Error("ZIP must contain manifest.json");
|
|
47
|
+
const moduleRootInZip = manifestEntry.entryName.replace("manifest.json", "");
|
|
48
|
+
const manifestRaw = JSON.parse(manifestEntry.getData().toString("utf8"));
|
|
49
|
+
const manifest = _server2.ModuleManifestSchema.parse(manifestRaw);
|
|
50
|
+
const modulesDir = await (0, _server.getModulesDir)();
|
|
51
|
+
const targetDir = _nodePath.default.join(modulesDir, manifest.id);
|
|
52
|
+
try {
|
|
53
|
+
await _promises.default.access(targetDir);
|
|
54
|
+
throw new Error(`Module with ID "${manifest.id}" already exists.`);
|
|
55
|
+
} catch (e) {
|
|
56
|
+
if (e.message.includes("already exists")) throw e;
|
|
57
|
+
}
|
|
58
|
+
await _promises.default.mkdir(targetDir, {
|
|
59
|
+
recursive: true
|
|
60
|
+
});
|
|
61
|
+
for (const entry of zipEntries) {
|
|
62
|
+
if (entry.entryName.startsWith(moduleRootInZip) && !entry.isDirectory) {
|
|
63
|
+
const relativePath = entry.entryName.slice(moduleRootInZip.length);
|
|
64
|
+
const fullPath = _nodePath.default.join(targetDir, relativePath);
|
|
65
|
+
await _promises.default.mkdir(_nodePath.default.dirname(fullPath), {
|
|
66
|
+
recursive: true
|
|
67
|
+
});
|
|
68
|
+
await _promises.default.writeFile(fullPath, entry.getData());
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
await _server.db.insert(_server.systemModulesTable).values({
|
|
72
|
+
id: manifest.id,
|
|
73
|
+
enabled: false,
|
|
74
|
+
installed: false,
|
|
75
|
+
system: manifest.system ?? false
|
|
76
|
+
}).onConflictDoNothing();
|
|
77
|
+
console.log(`[Module:ModuleManager] Module "${manifest.id}" uploaded and extracted.`);
|
|
78
|
+
await _server.eventBus.publish("system:module:upload", {
|
|
79
|
+
moduleId: manifest.id,
|
|
80
|
+
manifest
|
|
81
|
+
});
|
|
82
|
+
(0, _cache.revalidatePath)("/admin/modules");
|
|
83
|
+
(0, _cache.revalidatePath)("/module/module-manager");
|
|
84
|
+
return {
|
|
85
|
+
success: true
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
console.error(`[Module:ModuleManager] Upload error:`, error);
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
error: error.message
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function checkDiskWriteAccess() {
|
|
96
|
+
const modulesDir = await (0, _server.getModulesDir)();
|
|
97
|
+
try {
|
|
98
|
+
await _promises.default.access(modulesDir, _promises.constants.W_OK);
|
|
99
|
+
return {
|
|
100
|
+
canWrite: true
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error("[Module:ModuleManager] No write access to modules directory:", error);
|
|
104
|
+
return {
|
|
105
|
+
canWrite: false
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async function getModuleStatusAction(moduleId) {
|
|
110
|
+
(0, _cache.unstable_noStore)();
|
|
111
|
+
return await (0, _server2.getModuleStatus)(moduleId);
|
|
112
|
+
}
|
|
113
|
+
async function getModulesAction() {
|
|
114
|
+
(0, _cache.unstable_noStore)();
|
|
115
|
+
return await (0, _server2.getModules)();
|
|
116
|
+
}
|
|
117
|
+
async function getModuleDocumentation(moduleId) {
|
|
118
|
+
const modulesDir = await (0, _server.getModulesDir)();
|
|
119
|
+
const docsDir = _nodePath.default.join(modulesDir, moduleId, "docs");
|
|
120
|
+
try {
|
|
121
|
+
const exists = await _promises.default.access(docsDir).then(() => true).catch(() => false);
|
|
122
|
+
if (!exists) return null;
|
|
123
|
+
const files = await _promises.default.readdir(docsDir);
|
|
124
|
+
const mdFiles = files.filter(f => f.endsWith(".md"));
|
|
125
|
+
if (mdFiles.length === 0) return null;
|
|
126
|
+
const docs = await Promise.all(mdFiles.map(async file => {
|
|
127
|
+
const content = await _promises.default.readFile(_nodePath.default.join(docsDir, file), "utf-8");
|
|
128
|
+
return {
|
|
129
|
+
filename: file,
|
|
130
|
+
title: file.replace(".md", "").split("-").map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(" "),
|
|
131
|
+
content
|
|
132
|
+
};
|
|
133
|
+
}));
|
|
134
|
+
return docs;
|
|
135
|
+
} catch (_error) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare function toggleModuleAction(moduleId: string, isEnabled: boolean): Promise<any>;
|
|
2
|
+
export declare function uploadModuleAction(formData: FormData): Promise<{
|
|
3
|
+
success: boolean;
|
|
4
|
+
error?: undefined;
|
|
5
|
+
} | {
|
|
6
|
+
success: boolean;
|
|
7
|
+
error: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function checkDiskWriteAccess(): Promise<{
|
|
10
|
+
canWrite: boolean;
|
|
11
|
+
}>;
|
|
12
|
+
export declare function getModuleStatusAction(moduleId: string): Promise<any>;
|
|
13
|
+
export declare function getModulesAction(): Promise<any>;
|
|
14
|
+
export declare function getModuleDocumentation(moduleId: string): Promise<any>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import fs, { constants } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
db,
|
|
6
|
+
eventBus,
|
|
7
|
+
getModulesDir,
|
|
8
|
+
systemModulesTable
|
|
9
|
+
} from "@arch-cadre/core/server";
|
|
10
|
+
import {
|
|
11
|
+
getModuleStatus,
|
|
12
|
+
getModules,
|
|
13
|
+
ModuleManifestSchema,
|
|
14
|
+
toggleModuleState
|
|
15
|
+
} from "@arch-cadre/modules/server";
|
|
16
|
+
import AdmZip from "adm-zip";
|
|
17
|
+
import { revalidatePath, unstable_noStore } from "next/cache";
|
|
18
|
+
export async function toggleModuleAction(moduleId, isEnabled) {
|
|
19
|
+
try {
|
|
20
|
+
console.log(
|
|
21
|
+
`[Module:ModuleManager] Toggle request for ${moduleId} -> ${isEnabled}`
|
|
22
|
+
);
|
|
23
|
+
const result = await toggleModuleState(moduleId, isEnabled);
|
|
24
|
+
await eventBus.publish("system:module:toggle", { moduleId, isEnabled });
|
|
25
|
+
revalidatePath("/admin/modules");
|
|
26
|
+
revalidatePath("/module/module-manager");
|
|
27
|
+
return result;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(
|
|
30
|
+
`[Module:ModuleManager] Error toggling module ${moduleId}:`,
|
|
31
|
+
error
|
|
32
|
+
);
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function uploadModuleAction(formData) {
|
|
37
|
+
try {
|
|
38
|
+
const file = formData.get("file");
|
|
39
|
+
if (!file) throw new Error("No file provided");
|
|
40
|
+
const buffer = Buffer.from(await file.arrayBuffer());
|
|
41
|
+
const zip = new AdmZip(buffer);
|
|
42
|
+
const zipEntries = zip.getEntries();
|
|
43
|
+
const manifestEntry = zipEntries.find(
|
|
44
|
+
(e) => e.entryName.endsWith("manifest.json")
|
|
45
|
+
);
|
|
46
|
+
if (!manifestEntry) throw new Error("ZIP must contain manifest.json");
|
|
47
|
+
const moduleRootInZip = manifestEntry.entryName.replace(
|
|
48
|
+
"manifest.json",
|
|
49
|
+
""
|
|
50
|
+
);
|
|
51
|
+
const manifestRaw = JSON.parse(manifestEntry.getData().toString("utf8"));
|
|
52
|
+
const manifest = ModuleManifestSchema.parse(manifestRaw);
|
|
53
|
+
const modulesDir = await getModulesDir();
|
|
54
|
+
const targetDir = path.join(modulesDir, manifest.id);
|
|
55
|
+
try {
|
|
56
|
+
await fs.access(targetDir);
|
|
57
|
+
throw new Error(`Module with ID "${manifest.id}" already exists.`);
|
|
58
|
+
} catch (e) {
|
|
59
|
+
if (e.message.includes("already exists")) throw e;
|
|
60
|
+
}
|
|
61
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
62
|
+
for (const entry of zipEntries) {
|
|
63
|
+
if (entry.entryName.startsWith(moduleRootInZip) && !entry.isDirectory) {
|
|
64
|
+
const relativePath = entry.entryName.slice(moduleRootInZip.length);
|
|
65
|
+
const fullPath = path.join(targetDir, relativePath);
|
|
66
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
67
|
+
await fs.writeFile(fullPath, entry.getData());
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
await db.insert(systemModulesTable).values({
|
|
71
|
+
id: manifest.id,
|
|
72
|
+
enabled: false,
|
|
73
|
+
installed: false,
|
|
74
|
+
system: manifest.system ?? false
|
|
75
|
+
}).onConflictDoNothing();
|
|
76
|
+
console.log(
|
|
77
|
+
`[Module:ModuleManager] Module "${manifest.id}" uploaded and extracted.`
|
|
78
|
+
);
|
|
79
|
+
await eventBus.publish("system:module:upload", {
|
|
80
|
+
moduleId: manifest.id,
|
|
81
|
+
manifest
|
|
82
|
+
});
|
|
83
|
+
revalidatePath("/admin/modules");
|
|
84
|
+
revalidatePath("/module/module-manager");
|
|
85
|
+
return { success: true };
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`[Module:ModuleManager] Upload error:`, error);
|
|
88
|
+
return { success: false, error: error.message };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
export async function checkDiskWriteAccess() {
|
|
92
|
+
const modulesDir = await getModulesDir();
|
|
93
|
+
try {
|
|
94
|
+
await fs.access(modulesDir, constants.W_OK);
|
|
95
|
+
return { canWrite: true };
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error(
|
|
98
|
+
"[Module:ModuleManager] No write access to modules directory:",
|
|
99
|
+
error
|
|
100
|
+
);
|
|
101
|
+
return { canWrite: false };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export async function getModuleStatusAction(moduleId) {
|
|
105
|
+
unstable_noStore();
|
|
106
|
+
return await getModuleStatus(moduleId);
|
|
107
|
+
}
|
|
108
|
+
export async function getModulesAction() {
|
|
109
|
+
unstable_noStore();
|
|
110
|
+
return await getModules();
|
|
111
|
+
}
|
|
112
|
+
export async function getModuleDocumentation(moduleId) {
|
|
113
|
+
const modulesDir = await getModulesDir();
|
|
114
|
+
const docsDir = path.join(modulesDir, moduleId, "docs");
|
|
115
|
+
try {
|
|
116
|
+
const exists = await fs.access(docsDir).then(() => true).catch(() => false);
|
|
117
|
+
if (!exists) return null;
|
|
118
|
+
const files = await fs.readdir(docsDir);
|
|
119
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
120
|
+
if (mdFiles.length === 0) return null;
|
|
121
|
+
const docs = await Promise.all(
|
|
122
|
+
mdFiles.map(async (file) => {
|
|
123
|
+
const content = await fs.readFile(path.join(docsDir, file), "utf-8");
|
|
124
|
+
return {
|
|
125
|
+
filename: file,
|
|
126
|
+
title: file.replace(".md", "").split("-").map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(" "),
|
|
127
|
+
content
|
|
128
|
+
};
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
return docs;
|
|
132
|
+
} catch (_error) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
"use server";
|
|
3
|
+
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.updateEmailAction = updateEmailAction;
|
|
8
|
+
exports.updatePasswordAction = updatePasswordAction;
|
|
9
|
+
exports.updateProfileAction = updateProfileAction;
|
|
10
|
+
var _server = require("@arch-cadre/core/server");
|
|
11
|
+
var _server2 = require("@arch-cadre/intl/server");
|
|
12
|
+
var _cache = require("next/cache");
|
|
13
|
+
var _navigation = require("next/navigation");
|
|
14
|
+
async function updatePasswordAction(_prev, formData) {
|
|
15
|
+
const {
|
|
16
|
+
t
|
|
17
|
+
} = await (0, _server2.getTranslation)();
|
|
18
|
+
const {
|
|
19
|
+
session,
|
|
20
|
+
user
|
|
21
|
+
} = await (0, _server.getCurrentSession)();
|
|
22
|
+
if (session === null || user === null) {
|
|
23
|
+
return {
|
|
24
|
+
success: false,
|
|
25
|
+
message: t("Not authenticated")
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (user.registered2FA && !session.two_factor_verified) {
|
|
29
|
+
return {
|
|
30
|
+
success: false,
|
|
31
|
+
message: t("Forbidden")
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const password = formData.get("password");
|
|
35
|
+
const newPassword = formData.get("new_password");
|
|
36
|
+
if (typeof password !== "string" || typeof newPassword !== "string") {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
message: t("Invalid or missing fields")
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (!(0, _server.verifyPasswordStrength)(newPassword)) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
message: t("Weak password")
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const passwordHash = await (0, _server.getUserPasswordHash)(user.id);
|
|
49
|
+
if (!passwordHash) return {
|
|
50
|
+
success: false,
|
|
51
|
+
message: t("Internal error")
|
|
52
|
+
};
|
|
53
|
+
const validPassword = await (0, _server.verifyPasswordHash)(passwordHash, password);
|
|
54
|
+
if (!validPassword) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
message: t("Incorrect password")
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
await (0, _server.invalidateUserSessions)(user.id);
|
|
61
|
+
await (0, _server.updateUserPassword)(user.id, newPassword);
|
|
62
|
+
const sessionToken = await (0, _server.generateSessionToken)();
|
|
63
|
+
const sessionFlags = {
|
|
64
|
+
twoFactorVerified: session.two_factor_verified ?? false
|
|
65
|
+
};
|
|
66
|
+
const newSession = await (0, _server.createSession)(sessionToken, user.id, sessionFlags);
|
|
67
|
+
await (0, _server.setSessionTokenCookie)(sessionToken, newSession.expiresAt);
|
|
68
|
+
await _server.eventBus.publish("activity.create", {
|
|
69
|
+
action: "profile.updated.password",
|
|
70
|
+
description: `User ${user?.name} updated their password`,
|
|
71
|
+
userId: user?.id,
|
|
72
|
+
metadata: {}
|
|
73
|
+
}, "profile");
|
|
74
|
+
return {
|
|
75
|
+
success: true,
|
|
76
|
+
message: t("Updated password")
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function updateEmailAction(_prev, formData) {
|
|
80
|
+
const {
|
|
81
|
+
t
|
|
82
|
+
} = await (0, _server2.getTranslation)();
|
|
83
|
+
const {
|
|
84
|
+
session,
|
|
85
|
+
user
|
|
86
|
+
} = await (0, _server.getCurrentSession)();
|
|
87
|
+
if (session === null || user === null) return {
|
|
88
|
+
success: false,
|
|
89
|
+
message: t("Not authenticated")
|
|
90
|
+
};
|
|
91
|
+
if (user.registered2FA && !session.two_factor_verified) return {
|
|
92
|
+
success: false,
|
|
93
|
+
message: t("Forbidden")
|
|
94
|
+
};
|
|
95
|
+
const email = formData.get("email");
|
|
96
|
+
if (typeof email !== "string" || email === "") {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
message: t("Please enter your email")
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
if (!(0, _server.verifyEmailInput)(email)) {
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
message: t("Please enter a valid email")
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const emailAvailable = await (0, _server.checkEmailAvailability)(email);
|
|
109
|
+
if (!emailAvailable) {
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
message: t("This email is already used")
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const verificationRequest = await (0, _server.createEmailVerificationRequest)(user.id, email);
|
|
116
|
+
await (0, _server.sendVerificationEmail)(verificationRequest.email, verificationRequest.code);
|
|
117
|
+
await (0, _server.setEmailVerificationRequestCookie)(verificationRequest);
|
|
118
|
+
await _server.eventBus.publish("activity.create", {
|
|
119
|
+
action: "profile.updated.email",
|
|
120
|
+
description: `User ${user?.name} updated their email`,
|
|
121
|
+
userId: user?.id,
|
|
122
|
+
metadata: {
|
|
123
|
+
email
|
|
124
|
+
}
|
|
125
|
+
}, "profile");
|
|
126
|
+
return (0, _navigation.redirect)("/verify-email");
|
|
127
|
+
}
|
|
128
|
+
async function updateProfileAction(name, image) {
|
|
129
|
+
const {
|
|
130
|
+
session,
|
|
131
|
+
user
|
|
132
|
+
} = await (0, _server.getCurrentSession)();
|
|
133
|
+
const {
|
|
134
|
+
t
|
|
135
|
+
} = await (0, _server2.getTranslation)();
|
|
136
|
+
if (session === null || user === null) return {
|
|
137
|
+
error: t("Not authenticated")
|
|
138
|
+
};
|
|
139
|
+
if (!name || name.trim().length === 0) return {
|
|
140
|
+
error: t("Name is required")
|
|
141
|
+
};
|
|
142
|
+
if (name.trim().length > 100) return {
|
|
143
|
+
error: t("Name is too long")
|
|
144
|
+
};
|
|
145
|
+
if (image instanceof File) {
|
|
146
|
+
const uploaded = await _server.filesystemService.upload(image, "vercel-blob");
|
|
147
|
+
if ("error" in uploaded) {
|
|
148
|
+
return {
|
|
149
|
+
error: uploaded.error
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
await (0, _server.updateUserAwatar)(user.id, uploaded.url);
|
|
153
|
+
}
|
|
154
|
+
await (0, _server.updateUserName)(user.id, name.trim());
|
|
155
|
+
(0, _cache.revalidatePath)("/profile");
|
|
156
|
+
await _server.eventBus.publish("activity.create", {
|
|
157
|
+
action: "profile.updated",
|
|
158
|
+
description: `User ${user?.name} updated their profile`,
|
|
159
|
+
userId: user?.id,
|
|
160
|
+
metadata: {
|
|
161
|
+
name,
|
|
162
|
+
imageUrl: image instanceof File ? image.name : null
|
|
163
|
+
}
|
|
164
|
+
}, "profile");
|
|
165
|
+
return {
|
|
166
|
+
success: true,
|
|
167
|
+
message: t("Profile updated successfully")
|
|
168
|
+
};
|
|
169
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ActionResult } from "../types";
|
|
2
|
+
export declare function updatePasswordAction(_prev: ActionResult, formData: FormData): Promise<ActionResult>;
|
|
3
|
+
export declare function updateEmailAction(_prev: ActionResult, formData: FormData): Promise<ActionResult>;
|
|
4
|
+
export declare function updateProfileAction(name: string, image?: File): Promise<{
|
|
5
|
+
error?: string;
|
|
6
|
+
success?: boolean;
|
|
7
|
+
message?: string;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use server";
|
|
2
|
+
import {
|
|
3
|
+
checkEmailAvailability,
|
|
4
|
+
createEmailVerificationRequest,
|
|
5
|
+
createSession,
|
|
6
|
+
eventBus,
|
|
7
|
+
filesystemService,
|
|
8
|
+
generateSessionToken,
|
|
9
|
+
getCurrentSession,
|
|
10
|
+
getUserPasswordHash,
|
|
11
|
+
invalidateUserSessions,
|
|
12
|
+
sendVerificationEmail,
|
|
13
|
+
setEmailVerificationRequestCookie,
|
|
14
|
+
setSessionTokenCookie,
|
|
15
|
+
updateUserAwatar,
|
|
16
|
+
updateUserName,
|
|
17
|
+
updateUserPassword,
|
|
18
|
+
verifyEmailInput,
|
|
19
|
+
verifyPasswordHash,
|
|
20
|
+
verifyPasswordStrength
|
|
21
|
+
} from "@arch-cadre/core/server";
|
|
22
|
+
import { getTranslation } from "@arch-cadre/intl/server";
|
|
23
|
+
import { revalidatePath } from "next/cache";
|
|
24
|
+
import { redirect } from "next/navigation";
|
|
25
|
+
export async function updatePasswordAction(_prev, formData) {
|
|
26
|
+
const { t } = await getTranslation();
|
|
27
|
+
const { session, user } = await getCurrentSession();
|
|
28
|
+
if (session === null || user === null) {
|
|
29
|
+
return { success: false, message: t("Not authenticated") };
|
|
30
|
+
}
|
|
31
|
+
if (user.registered2FA && !session.two_factor_verified) {
|
|
32
|
+
return { success: false, message: t("Forbidden") };
|
|
33
|
+
}
|
|
34
|
+
const password = formData.get("password");
|
|
35
|
+
const newPassword = formData.get("new_password");
|
|
36
|
+
if (typeof password !== "string" || typeof newPassword !== "string") {
|
|
37
|
+
return { success: false, message: t("Invalid or missing fields") };
|
|
38
|
+
}
|
|
39
|
+
if (!verifyPasswordStrength(newPassword)) {
|
|
40
|
+
return { success: false, message: t("Weak password") };
|
|
41
|
+
}
|
|
42
|
+
const passwordHash = await getUserPasswordHash(user.id);
|
|
43
|
+
if (!passwordHash) return { success: false, message: t("Internal error") };
|
|
44
|
+
const validPassword = await verifyPasswordHash(passwordHash, password);
|
|
45
|
+
if (!validPassword) {
|
|
46
|
+
return { success: false, message: t("Incorrect password") };
|
|
47
|
+
}
|
|
48
|
+
await invalidateUserSessions(user.id);
|
|
49
|
+
await updateUserPassword(user.id, newPassword);
|
|
50
|
+
const sessionToken = await generateSessionToken();
|
|
51
|
+
const sessionFlags = {
|
|
52
|
+
twoFactorVerified: session.two_factor_verified ?? false
|
|
53
|
+
};
|
|
54
|
+
const newSession = await createSession(sessionToken, user.id, sessionFlags);
|
|
55
|
+
await setSessionTokenCookie(sessionToken, newSession.expiresAt);
|
|
56
|
+
await eventBus.publish(
|
|
57
|
+
"activity.create",
|
|
58
|
+
{
|
|
59
|
+
action: "profile.updated.password",
|
|
60
|
+
description: `User ${user?.name} updated their password`,
|
|
61
|
+
userId: user?.id,
|
|
62
|
+
metadata: {}
|
|
63
|
+
},
|
|
64
|
+
"profile"
|
|
65
|
+
);
|
|
66
|
+
return { success: true, message: t("Updated password") };
|
|
67
|
+
}
|
|
68
|
+
export async function updateEmailAction(_prev, formData) {
|
|
69
|
+
const { t } = await getTranslation();
|
|
70
|
+
const { session, user } = await getCurrentSession();
|
|
71
|
+
if (session === null || user === null)
|
|
72
|
+
return { success: false, message: t("Not authenticated") };
|
|
73
|
+
if (user.registered2FA && !session.two_factor_verified)
|
|
74
|
+
return { success: false, message: t("Forbidden") };
|
|
75
|
+
const email = formData.get("email");
|
|
76
|
+
if (typeof email !== "string" || email === "") {
|
|
77
|
+
return { success: false, message: t("Please enter your email") };
|
|
78
|
+
}
|
|
79
|
+
if (!verifyEmailInput(email)) {
|
|
80
|
+
return { success: false, message: t("Please enter a valid email") };
|
|
81
|
+
}
|
|
82
|
+
const emailAvailable = await checkEmailAvailability(email);
|
|
83
|
+
if (!emailAvailable) {
|
|
84
|
+
return { success: false, message: t("This email is already used") };
|
|
85
|
+
}
|
|
86
|
+
const verificationRequest = await createEmailVerificationRequest(
|
|
87
|
+
user.id,
|
|
88
|
+
email
|
|
89
|
+
);
|
|
90
|
+
await sendVerificationEmail(
|
|
91
|
+
verificationRequest.email,
|
|
92
|
+
verificationRequest.code
|
|
93
|
+
);
|
|
94
|
+
await setEmailVerificationRequestCookie(verificationRequest);
|
|
95
|
+
await eventBus.publish(
|
|
96
|
+
"activity.create",
|
|
97
|
+
{
|
|
98
|
+
action: "profile.updated.email",
|
|
99
|
+
description: `User ${user?.name} updated their email`,
|
|
100
|
+
userId: user?.id,
|
|
101
|
+
metadata: { email }
|
|
102
|
+
},
|
|
103
|
+
"profile"
|
|
104
|
+
);
|
|
105
|
+
return redirect("/verify-email");
|
|
106
|
+
}
|
|
107
|
+
export async function updateProfileAction(name, image) {
|
|
108
|
+
const { session, user } = await getCurrentSession();
|
|
109
|
+
const { t } = await getTranslation();
|
|
110
|
+
if (session === null || user === null)
|
|
111
|
+
return { error: t("Not authenticated") };
|
|
112
|
+
if (!name || name.trim().length === 0)
|
|
113
|
+
return { error: t("Name is required") };
|
|
114
|
+
if (name.trim().length > 100) return { error: t("Name is too long") };
|
|
115
|
+
if (image instanceof File) {
|
|
116
|
+
const uploaded = await filesystemService.upload(image, "vercel-blob");
|
|
117
|
+
if ("error" in uploaded) {
|
|
118
|
+
return { error: uploaded.error };
|
|
119
|
+
}
|
|
120
|
+
await updateUserAwatar(user.id, uploaded.url);
|
|
121
|
+
}
|
|
122
|
+
await updateUserName(user.id, name.trim());
|
|
123
|
+
revalidatePath("/profile");
|
|
124
|
+
await eventBus.publish(
|
|
125
|
+
"activity.create",
|
|
126
|
+
{
|
|
127
|
+
action: "profile.updated",
|
|
128
|
+
description: `User ${user?.name} updated their profile`,
|
|
129
|
+
userId: user?.id,
|
|
130
|
+
metadata: { name, imageUrl: image instanceof File ? image.name : null }
|
|
131
|
+
},
|
|
132
|
+
"profile"
|
|
133
|
+
);
|
|
134
|
+
return { success: true, message: t("Profile updated successfully") };
|
|
135
|
+
}
|