@naisys/erp 3.0.0-beta.3

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.

Potentially problematic release.


This version of @naisys/erp might be problematic. Click here for more details.

Files changed (201) hide show
  1. package/bin/naisys-erp +2 -0
  2. package/client-dist/android-chrome-192x192.png +0 -0
  3. package/client-dist/android-chrome-512x512.png +0 -0
  4. package/client-dist/apple-touch-icon.png +0 -0
  5. package/client-dist/assets/index-45dVo30p.css +1 -0
  6. package/client-dist/assets/index-Dffms7F_.js +168 -0
  7. package/client-dist/assets/naisys-logo-CzoPnn5I.webp +0 -0
  8. package/client-dist/favicon.ico +0 -0
  9. package/client-dist/index.html +42 -0
  10. package/client-dist/site.webmanifest +22 -0
  11. package/dist/api-reference.d.ts +10 -0
  12. package/dist/api-reference.js +101 -0
  13. package/dist/audit.d.ts +5 -0
  14. package/dist/audit.js +14 -0
  15. package/dist/auth-middleware.d.ts +18 -0
  16. package/dist/auth-middleware.js +203 -0
  17. package/dist/dbConfig.d.ts +5 -0
  18. package/dist/dbConfig.js +10 -0
  19. package/dist/erpDb.d.ts +10 -0
  20. package/dist/erpDb.js +34 -0
  21. package/dist/erpServer.d.ts +10 -0
  22. package/dist/erpServer.js +321 -0
  23. package/dist/error-handler.d.ts +7 -0
  24. package/dist/error-handler.js +17 -0
  25. package/dist/generated/prisma/client.d.ts +154 -0
  26. package/dist/generated/prisma/client.js +35 -0
  27. package/dist/generated/prisma/commonInputTypes.d.ts +637 -0
  28. package/dist/generated/prisma/commonInputTypes.js +11 -0
  29. package/dist/generated/prisma/enums.d.ts +59 -0
  30. package/dist/generated/prisma/enums.js +60 -0
  31. package/dist/generated/prisma/internal/class.d.ts +406 -0
  32. package/dist/generated/prisma/internal/class.js +50 -0
  33. package/dist/generated/prisma/internal/prismaNamespace.d.ts +2722 -0
  34. package/dist/generated/prisma/internal/prismaNamespace.js +366 -0
  35. package/dist/generated/prisma/models/Attachment.d.ts +1455 -0
  36. package/dist/generated/prisma/models/Attachment.js +2 -0
  37. package/dist/generated/prisma/models/AuditLog.d.ts +1359 -0
  38. package/dist/generated/prisma/models/AuditLog.js +2 -0
  39. package/dist/generated/prisma/models/Field.d.ts +1880 -0
  40. package/dist/generated/prisma/models/Field.js +2 -0
  41. package/dist/generated/prisma/models/FieldAttachment.d.ts +1245 -0
  42. package/dist/generated/prisma/models/FieldAttachment.js +2 -0
  43. package/dist/generated/prisma/models/FieldRecord.d.ts +1625 -0
  44. package/dist/generated/prisma/models/FieldRecord.js +2 -0
  45. package/dist/generated/prisma/models/FieldSet.d.ts +1577 -0
  46. package/dist/generated/prisma/models/FieldSet.js +2 -0
  47. package/dist/generated/prisma/models/FieldValue.d.ts +1908 -0
  48. package/dist/generated/prisma/models/FieldValue.js +2 -0
  49. package/dist/generated/prisma/models/Item.d.ts +1858 -0
  50. package/dist/generated/prisma/models/Item.js +2 -0
  51. package/dist/generated/prisma/models/ItemInstance.d.ts +1987 -0
  52. package/dist/generated/prisma/models/ItemInstance.js +2 -0
  53. package/dist/generated/prisma/models/LaborTicket.d.ts +1867 -0
  54. package/dist/generated/prisma/models/LaborTicket.js +2 -0
  55. package/dist/generated/prisma/models/Operation.d.ts +2578 -0
  56. package/dist/generated/prisma/models/Operation.js +2 -0
  57. package/dist/generated/prisma/models/OperationDependency.d.ts +1434 -0
  58. package/dist/generated/prisma/models/OperationDependency.js +2 -0
  59. package/dist/generated/prisma/models/OperationFieldRef.d.ts +1539 -0
  60. package/dist/generated/prisma/models/OperationFieldRef.js +2 -0
  61. package/dist/generated/prisma/models/OperationRun.d.ts +2563 -0
  62. package/dist/generated/prisma/models/OperationRun.js +2 -0
  63. package/dist/generated/prisma/models/OperationRunComment.d.ts +1366 -0
  64. package/dist/generated/prisma/models/OperationRunComment.js +2 -0
  65. package/dist/generated/prisma/models/Order.d.ts +1931 -0
  66. package/dist/generated/prisma/models/Order.js +2 -0
  67. package/dist/generated/prisma/models/OrderRevision.d.ts +1962 -0
  68. package/dist/generated/prisma/models/OrderRevision.js +2 -0
  69. package/dist/generated/prisma/models/OrderRun.d.ts +2310 -0
  70. package/dist/generated/prisma/models/OrderRun.js +2 -0
  71. package/dist/generated/prisma/models/SchemaVersion.d.ts +985 -0
  72. package/dist/generated/prisma/models/SchemaVersion.js +2 -0
  73. package/dist/generated/prisma/models/Session.d.ts +1213 -0
  74. package/dist/generated/prisma/models/Session.js +2 -0
  75. package/dist/generated/prisma/models/Step.d.ts +2180 -0
  76. package/dist/generated/prisma/models/Step.js +2 -0
  77. package/dist/generated/prisma/models/StepRun.d.ts +1963 -0
  78. package/dist/generated/prisma/models/StepRun.js +2 -0
  79. package/dist/generated/prisma/models/User.d.ts +11819 -0
  80. package/dist/generated/prisma/models/User.js +2 -0
  81. package/dist/generated/prisma/models/UserPermission.d.ts +1348 -0
  82. package/dist/generated/prisma/models/UserPermission.js +2 -0
  83. package/dist/generated/prisma/models/WorkCenter.d.ts +1657 -0
  84. package/dist/generated/prisma/models/WorkCenter.js +2 -0
  85. package/dist/generated/prisma/models/WorkCenterUser.d.ts +1390 -0
  86. package/dist/generated/prisma/models/WorkCenterUser.js +2 -0
  87. package/dist/generated/prisma/models.d.ts +28 -0
  88. package/dist/generated/prisma/models.js +2 -0
  89. package/dist/hateoas.d.ts +7 -0
  90. package/dist/hateoas.js +61 -0
  91. package/dist/route-helpers.d.ts +318 -0
  92. package/dist/route-helpers.js +220 -0
  93. package/dist/routes/admin.d.ts +3 -0
  94. package/dist/routes/admin.js +147 -0
  95. package/dist/routes/audit.d.ts +3 -0
  96. package/dist/routes/audit.js +36 -0
  97. package/dist/routes/auth.d.ts +3 -0
  98. package/dist/routes/auth.js +112 -0
  99. package/dist/routes/dispatch.d.ts +3 -0
  100. package/dist/routes/dispatch.js +174 -0
  101. package/dist/routes/inventory.d.ts +3 -0
  102. package/dist/routes/inventory.js +70 -0
  103. package/dist/routes/item-fields.d.ts +3 -0
  104. package/dist/routes/item-fields.js +220 -0
  105. package/dist/routes/item-instances.d.ts +3 -0
  106. package/dist/routes/item-instances.js +426 -0
  107. package/dist/routes/items.d.ts +3 -0
  108. package/dist/routes/items.js +252 -0
  109. package/dist/routes/labor-tickets.d.ts +3 -0
  110. package/dist/routes/labor-tickets.js +268 -0
  111. package/dist/routes/operation-dependencies.d.ts +3 -0
  112. package/dist/routes/operation-dependencies.js +170 -0
  113. package/dist/routes/operation-field-refs.d.ts +3 -0
  114. package/dist/routes/operation-field-refs.js +263 -0
  115. package/dist/routes/operation-run-comments.d.ts +3 -0
  116. package/dist/routes/operation-run-comments.js +108 -0
  117. package/dist/routes/operation-run-transitions.d.ts +3 -0
  118. package/dist/routes/operation-run-transitions.js +249 -0
  119. package/dist/routes/operation-runs.d.ts +112 -0
  120. package/dist/routes/operation-runs.js +299 -0
  121. package/dist/routes/operations.d.ts +3 -0
  122. package/dist/routes/operations.js +283 -0
  123. package/dist/routes/order-revision-transitions.d.ts +3 -0
  124. package/dist/routes/order-revision-transitions.js +86 -0
  125. package/dist/routes/order-revisions.d.ts +51 -0
  126. package/dist/routes/order-revisions.js +327 -0
  127. package/dist/routes/order-run-transitions.d.ts +3 -0
  128. package/dist/routes/order-run-transitions.js +215 -0
  129. package/dist/routes/order-runs.d.ts +58 -0
  130. package/dist/routes/order-runs.js +335 -0
  131. package/dist/routes/orders.d.ts +3 -0
  132. package/dist/routes/orders.js +262 -0
  133. package/dist/routes/root.d.ts +3 -0
  134. package/dist/routes/root.js +123 -0
  135. package/dist/routes/schemas.d.ts +3 -0
  136. package/dist/routes/schemas.js +31 -0
  137. package/dist/routes/step-field-attachments.d.ts +3 -0
  138. package/dist/routes/step-field-attachments.js +231 -0
  139. package/dist/routes/step-fields.d.ts +100 -0
  140. package/dist/routes/step-fields.js +315 -0
  141. package/dist/routes/step-run-fields.d.ts +3 -0
  142. package/dist/routes/step-run-fields.js +438 -0
  143. package/dist/routes/step-run-transitions.d.ts +3 -0
  144. package/dist/routes/step-run-transitions.js +113 -0
  145. package/dist/routes/step-runs.d.ts +332 -0
  146. package/dist/routes/step-runs.js +324 -0
  147. package/dist/routes/steps.d.ts +3 -0
  148. package/dist/routes/steps.js +283 -0
  149. package/dist/routes/user-permissions.d.ts +3 -0
  150. package/dist/routes/user-permissions.js +100 -0
  151. package/dist/routes/users.d.ts +57 -0
  152. package/dist/routes/users.js +381 -0
  153. package/dist/routes/work-centers.d.ts +3 -0
  154. package/dist/routes/work-centers.js +280 -0
  155. package/dist/schema-registry.d.ts +3 -0
  156. package/dist/schema-registry.js +45 -0
  157. package/dist/services/attachment-service.d.ts +33 -0
  158. package/dist/services/attachment-service.js +118 -0
  159. package/dist/services/field-ref-service.d.ts +96 -0
  160. package/dist/services/field-ref-service.js +74 -0
  161. package/dist/services/field-service.d.ts +49 -0
  162. package/dist/services/field-service.js +114 -0
  163. package/dist/services/field-value-service.d.ts +61 -0
  164. package/dist/services/field-value-service.js +256 -0
  165. package/dist/services/item-instance-service.d.ts +152 -0
  166. package/dist/services/item-instance-service.js +155 -0
  167. package/dist/services/item-service.d.ts +47 -0
  168. package/dist/services/item-service.js +56 -0
  169. package/dist/services/labor-ticket-service.d.ts +40 -0
  170. package/dist/services/labor-ticket-service.js +148 -0
  171. package/dist/services/log-file-service.d.ts +4 -0
  172. package/dist/services/log-file-service.js +11 -0
  173. package/dist/services/operation-dependency-service.d.ts +33 -0
  174. package/dist/services/operation-dependency-service.js +30 -0
  175. package/dist/services/operation-run-comment-service.d.ts +17 -0
  176. package/dist/services/operation-run-comment-service.js +26 -0
  177. package/dist/services/operation-run-service.d.ts +126 -0
  178. package/dist/services/operation-run-service.js +347 -0
  179. package/dist/services/operation-service.d.ts +47 -0
  180. package/dist/services/operation-service.js +132 -0
  181. package/dist/services/order-revision-service.d.ts +53 -0
  182. package/dist/services/order-revision-service.js +264 -0
  183. package/dist/services/order-run-service.d.ts +138 -0
  184. package/dist/services/order-run-service.js +356 -0
  185. package/dist/services/order-service.d.ts +15 -0
  186. package/dist/services/order-service.js +68 -0
  187. package/dist/services/revision-diff-service.d.ts +3 -0
  188. package/dist/services/revision-diff-service.js +194 -0
  189. package/dist/services/step-run-service.d.ts +172 -0
  190. package/dist/services/step-run-service.js +106 -0
  191. package/dist/services/step-service.d.ts +104 -0
  192. package/dist/services/step-service.js +89 -0
  193. package/dist/services/user-service.d.ts +185 -0
  194. package/dist/services/user-service.js +132 -0
  195. package/dist/services/work-center-service.d.ts +29 -0
  196. package/dist/services/work-center-service.js +106 -0
  197. package/dist/supervisorAuth.d.ts +3 -0
  198. package/dist/supervisorAuth.js +16 -0
  199. package/dist/userService.d.ts +20 -0
  200. package/dist/userService.js +118 -0
  201. package/package.json +69 -0
Binary file
@@ -0,0 +1,42 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+
6
+ <link rel="icon" type="image/x-icon" href="/erp/favicon.ico" />
7
+ <link
8
+ rel="apple-touch-icon"
9
+ sizes="180x180"
10
+ href="/erp/apple-touch-icon.png"
11
+ />
12
+
13
+ <link
14
+ rel="manifest"
15
+ crossorigin="use-credentials"
16
+ href="/erp/site.webmanifest"
17
+ />
18
+ <meta
19
+ name="viewport"
20
+ content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
21
+ />
22
+
23
+ <meta name="theme-color" content="#1a1b1e" />
24
+ <meta name="mobile-web-app-capable" content="yes" />
25
+ <meta
26
+ name="apple-mobile-web-app-status-bar-style"
27
+ content="black-translucent"
28
+ />
29
+ <meta name="apple-mobile-web-app-title" content="NAISYS ERP" />
30
+
31
+ <meta name="description" content="NAISYS ERP" />
32
+ <meta name="robots" content="noindex, nofollow" />
33
+ <meta name="format-detection" content="telephone=no" />
34
+
35
+ <title>NAISYS ERP</title>
36
+ <script type="module" crossorigin src="/erp/assets/index-Dffms7F_.js"></script>
37
+ <link rel="stylesheet" crossorigin href="/erp/assets/index-45dVo30p.css">
38
+ </head>
39
+ <body>
40
+ <div id="root"></div>
41
+ </body>
42
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "NAISYS ERP",
3
+ "short_name": "ERP",
4
+ "icons": [
5
+ {
6
+ "src": "/erp/android-chrome-192x192.png",
7
+ "sizes": "192x192",
8
+ "type": "image/png"
9
+ },
10
+ {
11
+ "src": "/erp/android-chrome-512x512.png",
12
+ "sizes": "512x512",
13
+ "type": "image/png"
14
+ }
15
+ ],
16
+ "theme_color": "#1a1b1e",
17
+ "background_color": "#1a1b1e",
18
+ "display": "standalone",
19
+ "start_url": "/erp/",
20
+ "scope": "/erp/",
21
+ "orientation": "portrait-primary"
22
+ }
@@ -0,0 +1,10 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ /**
3
+ * Registers Scalar API reference routes for the ERP service.
4
+ *
5
+ * Can't register @scalar/fastify-api-reference twice in the same process
6
+ * (Fastify deduplicates fp() plugins by name), so we serve the Scalar
7
+ * standalone JS bundle and a minimal HTML page manually.
8
+ */
9
+ export declare function registerApiReference(fastify: FastifyInstance): void;
10
+ //# sourceMappingURL=api-reference.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { createRequire } from "module";
3
+ import path from "path";
4
+ /**
5
+ * Registers Scalar API reference routes for the ERP service.
6
+ *
7
+ * Can't register @scalar/fastify-api-reference twice in the same process
8
+ * (Fastify deduplicates fp() plugins by name), so we serve the Scalar
9
+ * standalone JS bundle and a minimal HTML page manually.
10
+ */
11
+ export function registerApiReference(fastify) {
12
+ // Serve ERP OpenAPI spec filtered to ERP paths only
13
+ fastify.get("/api/erp/openapi.json", () => {
14
+ const spec = fastify.swagger();
15
+ const filteredPaths = {};
16
+ for (const [p, value] of Object.entries(spec.paths || {})) {
17
+ if (p.startsWith("/api/erp/")) {
18
+ filteredPaths[p] = value;
19
+ }
20
+ }
21
+ return {
22
+ ...spec,
23
+ info: {
24
+ title: "NAISYS ERP API",
25
+ description: "AI-first ERP system - Order management and definitions",
26
+ version: "1.0.0",
27
+ },
28
+ paths: filteredPaths,
29
+ "x-tagGroups": [
30
+ { name: "General", tags: ["Discovery", "Auth", "Users"] },
31
+ {
32
+ name: "Planning",
33
+ tags: [
34
+ "Orders",
35
+ "Order Revisions",
36
+ "Operations",
37
+ "Operation Dependencies",
38
+ "Operation Field Refs",
39
+ "Steps",
40
+ "Step Fields",
41
+ ],
42
+ },
43
+ {
44
+ name: "Execution",
45
+ tags: [
46
+ "Dispatch",
47
+ "Order Runs",
48
+ "Operation Runs",
49
+ "Operation Run Comments",
50
+ "Labor Tickets",
51
+ "Step Runs",
52
+ "Attachments",
53
+ "Work Centers",
54
+ ],
55
+ },
56
+ {
57
+ name: "Inventory",
58
+ tags: ["Items", "Item Fields", "Item Instances", "Inventory"],
59
+ },
60
+ {
61
+ name: "Administration",
62
+ tags: ["Admin", "Audit"],
63
+ },
64
+ ],
65
+ };
66
+ });
67
+ // Serve the Scalar standalone JS bundle from the installed package
68
+ const erpRequire = createRequire(import.meta.url);
69
+ const scalarDistDir = path.dirname(erpRequire.resolve("@scalar/fastify-api-reference"));
70
+ const scalarJs = readFileSync(path.join(scalarDistDir, "js/standalone.js"), "utf8");
71
+ fastify.get("/erp/api-reference", { schema: { hide: true } }, async (_request, reply) => {
72
+ return reply.redirect("/erp/api-reference/");
73
+ });
74
+ fastify.get("/erp/api-reference/js/scalar.js", { schema: { hide: true } }, async (_request, reply) => {
75
+ return reply
76
+ .header("Content-Type", "application/javascript; charset=utf-8")
77
+ .send(scalarJs);
78
+ });
79
+ fastify.get("/erp/api-reference/", { schema: { hide: true } }, async (_request, reply) => {
80
+ return reply.header("Content-Type", "text/html; charset=utf-8")
81
+ .send(`<!doctype html>
82
+ <html>
83
+ <head>
84
+ <title>NAISYS ERP API Reference</title>
85
+ <meta charset="utf-8" />
86
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
87
+ </head>
88
+ <body>
89
+ <div id="app"></div>
90
+ <script src="/erp/api-reference/js/scalar.js"><\/script>
91
+ <script type="text/javascript">
92
+ Scalar.createApiReference('#app', {
93
+ url: "/api/erp/openapi.json",
94
+ theme: "kepler"
95
+ })
96
+ <\/script>
97
+ </body>
98
+ </html>`);
99
+ });
100
+ }
101
+ //# sourceMappingURL=api-reference.js.map
@@ -0,0 +1,5 @@
1
+ import type { PrismaClient } from "./generated/prisma/client.js";
2
+ type PrismaTransaction = Parameters<Parameters<PrismaClient["$transaction"]>[0]>[0];
3
+ export declare function writeAuditEntry(erpTx: PrismaTransaction, entityType: string, entityId: number, action: string, field: string, oldValue: string | null, newValue: string | null, userId: number): Promise<void>;
4
+ export {};
5
+ //# sourceMappingURL=audit.d.ts.map
package/dist/audit.js ADDED
@@ -0,0 +1,14 @@
1
+ export async function writeAuditEntry(erpTx, entityType, entityId, action, field, oldValue, newValue, userId) {
2
+ await erpTx.auditLog.create({
3
+ data: {
4
+ entityType,
5
+ entityId,
6
+ action,
7
+ field,
8
+ oldValue,
9
+ newValue,
10
+ userId,
11
+ },
12
+ });
13
+ }
14
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1,18 @@
1
+ import { AuthCache } from "@naisys/common";
2
+ import type { ErpPermission } from "@naisys/erp-shared";
3
+ import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify";
4
+ export interface ErpUser {
5
+ id: number;
6
+ username: string;
7
+ permissions: ErpPermission[];
8
+ }
9
+ declare module "fastify" {
10
+ interface FastifyRequest {
11
+ erpUser?: ErpUser;
12
+ }
13
+ }
14
+ export declare const authCache: AuthCache<ErpUser>;
15
+ export declare function hasPermission(user: ErpUser | undefined, permission: ErpPermission): boolean;
16
+ export declare function requirePermission(permission: ErpPermission): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
17
+ export declare function registerAuthMiddleware(fastify: FastifyInstance): void;
18
+ //# sourceMappingURL=auth-middleware.d.ts.map
@@ -0,0 +1,203 @@
1
+ import { AuthCache } from "@naisys/common";
2
+ import { extractBearerToken, hashToken, SESSION_COOKIE_NAME, } from "@naisys/common-node";
3
+ import { findAgentByApiKey } from "@naisys/hub-database";
4
+ import { findSession, findUserByApiKey } from "@naisys/supervisor-database";
5
+ import erpDb from "./erpDb.js";
6
+ import { isSupervisorAuth } from "./supervisorAuth.js";
7
+ const PUBLIC_PREFIXES = ["/api/erp/auth/login", "/api/erp/client-config"];
8
+ export const authCache = new AuthCache();
9
+ async function loadPermissions(userId) {
10
+ const perms = await erpDb.userPermission.findMany({
11
+ where: { userId },
12
+ select: { permission: true },
13
+ });
14
+ return perms.map((p) => p.permission);
15
+ }
16
+ export function hasPermission(user, permission) {
17
+ if (!user)
18
+ return false;
19
+ return (user.permissions.includes(permission) ||
20
+ user.permissions.includes("erp_admin"));
21
+ }
22
+ export function requirePermission(permission) {
23
+ return async (request, reply) => {
24
+ if (!request.erpUser) {
25
+ reply.status(401).send({
26
+ statusCode: 401,
27
+ error: "Unauthorized",
28
+ message: "Authentication required",
29
+ });
30
+ return;
31
+ }
32
+ if (!hasPermission(request.erpUser, permission)) {
33
+ reply.status(403).send({
34
+ statusCode: 403,
35
+ error: "Forbidden",
36
+ message: `Permission '${permission}' required`,
37
+ });
38
+ return;
39
+ }
40
+ };
41
+ }
42
+ function isPublicRoute(url) {
43
+ // Exact match: API root
44
+ if (url === "/api/erp/" || url === "/api/erp")
45
+ return true;
46
+ // Prefix matches
47
+ for (const prefix of PUBLIC_PREFIXES) {
48
+ if (url.startsWith(prefix))
49
+ return true;
50
+ }
51
+ // Schema routes
52
+ if (url.startsWith("/api/erp/schemas"))
53
+ return true;
54
+ // Non-ERP-API paths (static files, supervisor routes, etc.)
55
+ if (!url.startsWith("/api/erp"))
56
+ return true;
57
+ return false;
58
+ }
59
+ export function registerAuthMiddleware(fastify) {
60
+ const publicRead = process.env.PUBLIC_READ === "true";
61
+ fastify.decorateRequest("erpUser", undefined);
62
+ fastify.addHook("onRequest", async (request, reply) => {
63
+ const token = request.cookies?.[SESSION_COOKIE_NAME];
64
+ if (token) {
65
+ const tokenHash = hashToken(token);
66
+ const cacheKey = `cookie:${tokenHash}`;
67
+ const cached = authCache.get(cacheKey);
68
+ if (cached !== undefined) {
69
+ // Cache hit (valid or negative)
70
+ if (cached)
71
+ request.erpUser = cached;
72
+ }
73
+ else if (isSupervisorAuth()) {
74
+ // SSO mode: supervisor DB is source of truth for sessions
75
+ const session = await findSession(tokenHash);
76
+ if (session) {
77
+ let localUser = await erpDb.user.findUnique({
78
+ where: { uuid: session.uuid },
79
+ });
80
+ if (!localUser) {
81
+ localUser = await erpDb.user.create({
82
+ data: {
83
+ uuid: session.uuid,
84
+ username: session.username,
85
+ passwordHash: session.passwordHash,
86
+ },
87
+ });
88
+ }
89
+ const permissions = await loadPermissions(localUser.id);
90
+ const erpUser = {
91
+ id: localUser.id,
92
+ username: localUser.username,
93
+ permissions,
94
+ };
95
+ authCache.set(cacheKey, erpUser);
96
+ request.erpUser = erpUser;
97
+ }
98
+ else {
99
+ authCache.set(cacheKey, null);
100
+ }
101
+ }
102
+ else {
103
+ // Standalone mode: local session only
104
+ const session = await erpDb.session.findUnique({
105
+ where: {
106
+ tokenHash,
107
+ expiresAt: { gt: new Date() },
108
+ },
109
+ include: { user: true },
110
+ });
111
+ if (session) {
112
+ const permissions = await loadPermissions(session.user.id);
113
+ const erpUser = {
114
+ id: session.user.id,
115
+ username: session.user.username,
116
+ permissions,
117
+ };
118
+ authCache.set(cacheKey, erpUser);
119
+ request.erpUser = erpUser;
120
+ }
121
+ else {
122
+ authCache.set(cacheKey, null);
123
+ }
124
+ }
125
+ }
126
+ // API key auth (for agents / machine-to-machine)
127
+ if (!request.erpUser) {
128
+ const apiKey = extractBearerToken(request.headers.authorization);
129
+ if (apiKey) {
130
+ const apiKeyHash = hashToken(apiKey);
131
+ const cacheKey = `apikey:${apiKeyHash}`;
132
+ const cached = authCache.get(cacheKey);
133
+ if (cached !== undefined) {
134
+ if (cached)
135
+ request.erpUser = cached;
136
+ }
137
+ else if (isSupervisorAuth()) {
138
+ // SSO mode: try supervisor DB (human users), then hub DB (agents)
139
+ const match = (await findUserByApiKey(apiKey)) ??
140
+ (await findAgentByApiKey(apiKey));
141
+ if (match) {
142
+ let localUser = await erpDb.user.findUnique({
143
+ where: { uuid: match.uuid },
144
+ });
145
+ if (!localUser) {
146
+ localUser = await erpDb.user.create({
147
+ data: {
148
+ uuid: match.uuid,
149
+ username: match.username,
150
+ passwordHash: "!api-key-only",
151
+ isAgent: true,
152
+ },
153
+ });
154
+ }
155
+ const permissions = await loadPermissions(localUser.id);
156
+ const erpUser = {
157
+ id: localUser.id,
158
+ username: localUser.username,
159
+ permissions,
160
+ };
161
+ authCache.set(cacheKey, erpUser);
162
+ request.erpUser = erpUser;
163
+ }
164
+ else {
165
+ authCache.set(cacheKey, null);
166
+ }
167
+ }
168
+ else {
169
+ // Standalone mode: check local ERP user table
170
+ const localUser = await erpDb.user.findUnique({
171
+ where: { apiKey },
172
+ });
173
+ if (localUser) {
174
+ const permissions = await loadPermissions(localUser.id);
175
+ const erpUser = {
176
+ id: localUser.id,
177
+ username: localUser.username,
178
+ permissions,
179
+ };
180
+ authCache.set(cacheKey, erpUser);
181
+ request.erpUser = erpUser;
182
+ }
183
+ else {
184
+ authCache.set(cacheKey, null);
185
+ }
186
+ }
187
+ }
188
+ }
189
+ // Check if auth is required
190
+ if (request.erpUser)
191
+ return; // Authenticated, always allowed
192
+ if (isPublicRoute(request.url))
193
+ return; // Public route
194
+ if (publicRead && request.method === "GET")
195
+ return; // Public read mode
196
+ reply.status(401).send({
197
+ statusCode: 401,
198
+ error: "Unauthorized",
199
+ message: "Authentication required",
200
+ });
201
+ });
202
+ }
203
+ //# sourceMappingURL=auth-middleware.js.map
@@ -0,0 +1,5 @@
1
+ export declare function erpDbPath(): string;
2
+ export declare function erpDbUrl(): string;
3
+ /** We run migration scripts if this is greater than what's in the schema_version table */
4
+ export declare const ERP_DB_VERSION = 43;
5
+ //# sourceMappingURL=dbConfig.d.ts.map
@@ -0,0 +1,10 @@
1
+ import { join } from "path";
2
+ export function erpDbPath() {
3
+ return join(process.env.NAISYS_FOLDER || "", "database", "naisys_erp.db");
4
+ }
5
+ export function erpDbUrl() {
6
+ return "file:" + erpDbPath();
7
+ }
8
+ /** We run migration scripts if this is greater than what's in the schema_version table */
9
+ export const ERP_DB_VERSION = 43;
10
+ //# sourceMappingURL=dbConfig.js.map
@@ -0,0 +1,10 @@
1
+ import { PrismaClient } from "./generated/prisma/client.js";
2
+ /**
3
+ * Initialize the ERP database connection and run SQLite pragmas.
4
+ * Must be called after the database directory/file exists (i.e. after migrations).
5
+ */
6
+ export declare function initErpDb(): Promise<void>;
7
+ /** Lazily initialized Prisma client. First access creates the connection. */
8
+ declare const erpDb: PrismaClient;
9
+ export default erpDb;
10
+ //# sourceMappingURL=erpDb.d.ts.map
package/dist/erpDb.js ADDED
@@ -0,0 +1,34 @@
1
+ import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
2
+ import { erpDbUrl } from "./dbConfig.js";
3
+ import { PrismaClient } from "./generated/prisma/client.js";
4
+ let _db;
5
+ /**
6
+ * Initialize the ERP database connection and run SQLite pragmas.
7
+ * Must be called after the database directory/file exists (i.e. after migrations).
8
+ */
9
+ export async function initErpDb() {
10
+ if (_db)
11
+ return;
12
+ const adapter = new PrismaBetterSqlite3({
13
+ url: erpDbUrl(),
14
+ timeout: 10_000, // Wait up to 10s for SQLite lock to be released
15
+ });
16
+ _db = new PrismaClient({ adapter });
17
+ // Enable WAL mode for better concurrent read/write performance
18
+ await _db.$executeRawUnsafe("PRAGMA journal_mode=WAL");
19
+ // NORMAL is safe with WAL and avoids an extra fsync per commit
20
+ await _db.$executeRawUnsafe("PRAGMA synchronous=NORMAL");
21
+ // SQLite doesn't enforce foreign keys by default — must be enabled per connection
22
+ await _db.$executeRawUnsafe("PRAGMA foreign_keys=ON");
23
+ }
24
+ /** Lazily initialized Prisma client. First access creates the connection. */
25
+ const erpDb = new Proxy({}, {
26
+ get(_target, prop, receiver) {
27
+ if (!_db) {
28
+ throw new Error("erpDb accessed before initialization. Ensure migrations have completed first.");
29
+ }
30
+ return Reflect.get(_db, prop, receiver);
31
+ },
32
+ });
33
+ export default erpDb;
34
+ //# sourceMappingURL=erpDb.js.map
@@ -0,0 +1,10 @@
1
+ import "dotenv/config";
2
+ import "./schema-registry.js";
3
+ import { type FastifyPluginAsync } from "fastify";
4
+ export { enableSupervisorAuth } from "./supervisorAuth.js";
5
+ /**
6
+ * Fastify plugin that registers ERP routes and static files.
7
+ * Can be used standalone or registered inside another Fastify app (e.g. supervisor).
8
+ */
9
+ export declare const erpPlugin: FastifyPluginAsync;
10
+ //# sourceMappingURL=erpServer.d.ts.map