@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.
Files changed (97) hide show
  1. package/dist/actions/actions.cjs +18 -0
  2. package/dist/actions/actions.d.ts +4 -0
  3. package/dist/actions/actions.mjs +10 -0
  4. package/dist/actions/index.cjs +27 -0
  5. package/dist/actions/index.d.ts +2 -0
  6. package/dist/actions/index.mjs +2 -0
  7. package/dist/actions/manager.cjs +138 -0
  8. package/dist/actions/manager.d.ts +14 -0
  9. package/dist/actions/manager.mjs +135 -0
  10. package/dist/actions/profile.cjs +169 -0
  11. package/dist/actions/profile.d.ts +8 -0
  12. package/dist/actions/profile.mjs +135 -0
  13. package/dist/actions/settings.cjs +26 -0
  14. package/dist/actions/settings.d.ts +7 -0
  15. package/dist/actions/settings.mjs +20 -0
  16. package/dist/index.cjs +29 -0
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.mjs +25 -0
  19. package/dist/intl.d.ts +9 -0
  20. package/dist/navigation.cjs +38 -0
  21. package/dist/navigation.d.ts +2 -0
  22. package/dist/navigation.mjs +39 -0
  23. package/dist/routes.cjs +71 -0
  24. package/dist/routes.d.ts +4 -0
  25. package/dist/routes.mjs +71 -0
  26. package/dist/types.cjs +1 -0
  27. package/dist/types.d.ts +13 -0
  28. package/dist/types.mjs +0 -0
  29. package/dist/ui/[...catchAll]/page.cjs +127 -0
  30. package/dist/ui/[...catchAll]/page.d.ts +13 -0
  31. package/dist/ui/[...catchAll]/page.mjs +88 -0
  32. package/dist/ui/components/app-content.cjs +49 -0
  33. package/dist/ui/components/app-content.d.ts +6 -0
  34. package/dist/ui/components/app-content.mjs +34 -0
  35. package/dist/ui/components/app-header.cjs +55 -0
  36. package/dist/ui/components/app-header.d.ts +6 -0
  37. package/dist/ui/components/app-header.mjs +45 -0
  38. package/dist/ui/components/app-sidebar.cjs +133 -0
  39. package/dist/ui/components/app-sidebar.d.ts +17 -0
  40. package/dist/ui/components/app-sidebar.mjs +142 -0
  41. package/dist/ui/components/app-user.cjs +102 -0
  42. package/dist/ui/components/app-user.d.ts +2 -0
  43. package/dist/ui/components/app-user.mjs +92 -0
  44. package/dist/ui/components/breadcrumb-slot.cjs +35 -0
  45. package/dist/ui/components/breadcrumb-slot.d.ts +2 -0
  46. package/dist/ui/components/breadcrumb-slot.mjs +31 -0
  47. package/dist/ui/components/manager/module-card.cjs +213 -0
  48. package/dist/ui/components/manager/module-card.d.ts +12 -0
  49. package/dist/ui/components/manager/module-card.mjs +197 -0
  50. package/dist/ui/components/manager/module-list.cjs +52 -0
  51. package/dist/ui/components/manager/module-list.d.ts +4 -0
  52. package/dist/ui/components/manager/module-list.mjs +16 -0
  53. package/dist/ui/components/manager/module-upload.cjs +81 -0
  54. package/dist/ui/components/manager/module-upload.d.ts +2 -0
  55. package/dist/ui/components/manager/module-upload.mjs +68 -0
  56. package/dist/ui/components/profile/components.cjs +239 -0
  57. package/dist/ui/components/profile/components.d.ts +8 -0
  58. package/dist/ui/components/profile/components.mjs +219 -0
  59. package/dist/ui/components/profile/link.cjs +25 -0
  60. package/dist/ui/components/profile/link.d.ts +1 -0
  61. package/dist/ui/components/profile/link.mjs +13 -0
  62. package/dist/ui/components/profile/page.cjs +41 -0
  63. package/dist/ui/components/profile/page.d.ts +1 -0
  64. package/dist/ui/components/profile/page.mjs +21 -0
  65. package/dist/ui/components/sidebar-slot.cjs +49 -0
  66. package/dist/ui/components/sidebar-slot.d.ts +2 -0
  67. package/dist/ui/components/sidebar-slot.mjs +33 -0
  68. package/dist/ui/dashboard/page.cjs +31 -0
  69. package/dist/ui/dashboard/page.d.ts +2 -0
  70. package/dist/ui/dashboard/page.mjs +9 -0
  71. package/dist/ui/error.cjs +50 -0
  72. package/dist/ui/error.d.ts +7 -0
  73. package/dist/ui/error.mjs +35 -0
  74. package/dist/ui/layout.cjs +48 -0
  75. package/dist/ui/layout.d.ts +4 -0
  76. package/dist/ui/layout.mjs +35 -0
  77. package/dist/ui/modules/docs/page.cjs +98 -0
  78. package/dist/ui/modules/docs/page.d.ts +6 -0
  79. package/dist/ui/modules/docs/page.mjs +46 -0
  80. package/dist/ui/modules/page.cjs +30 -0
  81. package/dist/ui/modules/page.d.ts +2 -0
  82. package/dist/ui/modules/page.mjs +10 -0
  83. package/dist/ui/page.cjs +18 -0
  84. package/dist/ui/page.d.ts +1 -0
  85. package/dist/ui/page.mjs +9 -0
  86. package/dist/ui/router.cjs +61 -0
  87. package/dist/ui/router.d.ts +5 -0
  88. package/dist/ui/router.mjs +51 -0
  89. package/dist/ui/settings/page.cjs +61 -0
  90. package/dist/ui/settings/page.d.ts +2 -0
  91. package/dist/ui/settings/page.mjs +22 -0
  92. package/dist/ui/settings-page.cjs +76 -0
  93. package/dist/ui/settings-page.d.ts +2 -0
  94. package/dist/ui/settings-page.mjs +57 -0
  95. package/locales/en/global.json +80 -0
  96. package/manifest.json +11 -0
  97. 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,4 @@
1
+ /**
2
+ * Modern Logout Action delegating to Core Auth
3
+ */
4
+ export declare function logoutAction(): Promise<void>;
@@ -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,2 @@
1
+ export * from "./actions";
2
+ export * from "./settings";
@@ -0,0 +1,2 @@
1
+ export * from "./actions.mjs";
2
+ export * from "./settings.mjs";
@@ -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
+ }