@axova/shared 1.0.2 → 1.1.0

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 (68) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +2 -0
  3. package/dist/lib/db.d.ts +34406 -1
  4. package/dist/lib/db.js +21 -1
  5. package/dist/middleware/storeOwnership.js +22 -3
  6. package/dist/middleware/storeValidationMiddleware.js +16 -39
  7. package/dist/schemas/admin/admin-schema.d.ts +2 -2
  8. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +6 -6
  9. package/dist/schemas/common/common-schemas.d.ts +71 -71
  10. package/dist/schemas/compliance/compliance-schema.d.ts +20 -20
  11. package/dist/schemas/compliance/kyc-schema.d.ts +8 -8
  12. package/dist/schemas/customer/customer-schema.d.ts +18 -18
  13. package/dist/schemas/index.d.ts +28 -0
  14. package/dist/schemas/index.js +134 -3
  15. package/dist/schemas/inventory/inventory-tables.d.ts +188 -188
  16. package/dist/schemas/inventory/lot-tables.d.ts +102 -102
  17. package/dist/schemas/order/cart-schema.d.ts +2865 -0
  18. package/dist/schemas/order/cart-schema.js +396 -0
  19. package/dist/schemas/order/order-schema.d.ts +19 -19
  20. package/dist/schemas/order/order-schema.js +8 -2
  21. package/dist/schemas/product/discount-schema.d.ts +3 -3
  22. package/dist/schemas/product/product-schema.d.ts +3 -3
  23. package/dist/schemas/store/store-audit-schema.d.ts +20 -20
  24. package/dist/schemas/store/store-schema.d.ts +182 -2
  25. package/dist/schemas/store/store-schema.js +19 -0
  26. package/dist/schemas/store/storefront-config-schema.d.ts +434 -823
  27. package/dist/schemas/store/storefront-config-schema.js +35 -62
  28. package/dist/utils/subdomain.d.ts +1 -1
  29. package/dist/utils/subdomain.js +10 -15
  30. package/package.json +1 -1
  31. package/src/configs/index.ts +654 -654
  32. package/src/index.ts +26 -23
  33. package/src/interfaces/customer-events.ts +106 -106
  34. package/src/interfaces/inventory-events.ts +545 -545
  35. package/src/interfaces/inventory-types.ts +1004 -1004
  36. package/src/interfaces/order-events.ts +381 -381
  37. package/src/lib/auditLogger.ts +1117 -1117
  38. package/src/lib/authOrganization.ts +153 -153
  39. package/src/lib/db.ts +84 -64
  40. package/src/middleware/serviceAuth.ts +328 -328
  41. package/src/middleware/storeOwnership.ts +199 -181
  42. package/src/middleware/storeValidationMiddleware.ts +17 -50
  43. package/src/middleware/userAuth.ts +248 -248
  44. package/src/schemas/admin/admin-schema.ts +208 -208
  45. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -180
  46. package/src/schemas/common/common-schemas.ts +108 -108
  47. package/src/schemas/compliance/compliance-schema.ts +927 -0
  48. package/src/schemas/compliance/kyc-schema.ts +649 -0
  49. package/src/schemas/customer/customer-schema.ts +576 -0
  50. package/src/schemas/index.ts +202 -3
  51. package/src/schemas/inventory/inventory-tables.ts +1927 -0
  52. package/src/schemas/inventory/lot-tables.ts +799 -0
  53. package/src/schemas/order/cart-schema.ts +652 -0
  54. package/src/schemas/order/order-schema.ts +1406 -0
  55. package/src/schemas/product/discount-relations.ts +44 -0
  56. package/src/schemas/product/discount-schema.ts +464 -0
  57. package/src/schemas/product/product-relations.ts +187 -0
  58. package/src/schemas/product/product-schema.ts +955 -0
  59. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
  60. package/src/schemas/store/store-audit-schema.ts +1257 -0
  61. package/src/schemas/store/store-schema.ts +682 -0
  62. package/src/schemas/store/store-settings-schema.ts +231 -0
  63. package/src/schemas/store/storefront-config-schema.ts +382 -0
  64. package/src/schemas/types.ts +67 -67
  65. package/src/types/events.ts +646 -646
  66. package/src/utils/errorHandler.ts +44 -44
  67. package/src/utils/subdomain.ts +19 -23
  68. package/tsconfig.json +21 -21
@@ -1,328 +1,328 @@
1
- import type { FastifyReply, FastifyRequest } from "fastify";
2
- import jwt from "jsonwebtoken";
3
- import { z } from "zod";
4
-
5
- // Service claims schema
6
- export const ServiceClaimsSchema = z.object({
7
- service: z.string(),
8
- permissions: z.array(z.string()),
9
- iat: z.number(),
10
- exp: z.number(),
11
- iss: z.literal("axova-platform"),
12
- });
13
-
14
- export type ServiceClaims = z.infer<typeof ServiceClaimsSchema>;
15
-
16
- // Service authentication configuration
17
- export interface ServiceAuthConfig {
18
- internalSecret: string;
19
- tokenExpiry?: string; // Default: '1h'
20
- requiredPermissions?: string[];
21
- }
22
-
23
- // Extended request interface
24
- export interface AuthenticatedRequest extends FastifyRequest {
25
- serviceClaims?: ServiceClaims;
26
- isServiceAuthenticated?: boolean;
27
- }
28
-
29
- // Service authentication class
30
- export class ServiceAuthenticator {
31
- private secret: string;
32
- private tokenExpiry: string;
33
-
34
- constructor(config: ServiceAuthConfig) {
35
- this.secret = config.internalSecret;
36
- this.tokenExpiry = config.tokenExpiry || "1h";
37
- }
38
-
39
- // Generate service token
40
- generateServiceToken(service: string, permissions: string[] = []): string {
41
- const payload: Omit<ServiceClaims, "iat" | "exp"> = {
42
- service,
43
- permissions,
44
- iss: "axova-platform",
45
- };
46
-
47
- return jwt.sign(payload, this.secret, {
48
- expiresIn: this.tokenExpiry,
49
- algorithm: "HS256",
50
- } as jwt.SignOptions);
51
- }
52
-
53
- // Verify service token
54
- verifyServiceToken(token: string): ServiceClaims {
55
- try {
56
- const decoded = jwt.verify(token, this.secret, {
57
- algorithms: ["HS256"],
58
- issuer: "axova-platform",
59
- }) as ServiceClaims;
60
-
61
- // Validate with Zod schema
62
- return ServiceClaimsSchema.parse(decoded);
63
- } catch (error) {
64
- throw new Error(
65
- `Invalid service token: ${error instanceof Error ? error.message : "Unknown error"}`,
66
- );
67
- }
68
- }
69
-
70
- // Fastify middleware for service authentication
71
- serviceAuthMiddleware(requiredPermissions: string[] = []) {
72
- return async (request: AuthenticatedRequest, reply: FastifyReply) => {
73
- try {
74
- const authHeader = request.headers.authorization;
75
- const serviceToken = request.headers["x-service-token"] as string;
76
-
77
- let token: string | null = null;
78
-
79
- // Check for Bearer token in Authorization header
80
- if (authHeader?.startsWith("Bearer ")) {
81
- token = authHeader.substring(7);
82
- }
83
- // Check for service token in custom header
84
- else if (serviceToken) {
85
- token = serviceToken;
86
- }
87
-
88
- if (!token) {
89
- return reply.code(401).send({
90
- error: "Service authentication required",
91
- code: "MISSING_SERVICE_TOKEN",
92
- });
93
- }
94
-
95
- // DEVELOPMENT MODE: Accept simple constant tokens
96
- const isDevelopment = process.env.NODE_ENV !== "production";
97
- const developmentTokens = [
98
- "dev-compliance-token",
99
- "development-mode",
100
- "compliance-dev-access",
101
- "simple-dev-token",
102
- "inventory-dev-access",
103
- "product-dev-access",
104
- "oxa-dev-access",
105
- ];
106
-
107
- if (isDevelopment && developmentTokens.includes(token)) {
108
- // In development, validate against a proper development service registry
109
- // instead of using mock claims
110
- try {
111
- // TODO: Implement proper development service authentication
112
- // For now, only allow specific development services with limited permissions
113
- let servicePermissions: string[] = [];
114
- let serviceName = "unknown-service";
115
-
116
- // Grant permissions based on the specific development token
117
- switch (token) {
118
- case "inventory-dev-access":
119
- serviceName = "inventory-service-dev";
120
- servicePermissions = ["store:read", "store:write"];
121
- break;
122
- case "product-dev-access":
123
- serviceName = "product-service-dev";
124
- servicePermissions = ["store:read", "store:write"];
125
- break;
126
- case "compliance-dev-access":
127
- serviceName = "compliance-service-dev";
128
- servicePermissions = ["compliance:read", "compliance:write"];
129
- break;
130
- case "oxa-dev-access":
131
- serviceName = "oxa-service-dev";
132
- servicePermissions = ["store:read", "ai:moderate"];
133
- break;
134
- default:
135
- throw new Error("Unknown development token");
136
- }
137
-
138
- const devClaims: ServiceClaims = {
139
- service: serviceName,
140
- permissions: servicePermissions,
141
- iss: "axova-platform",
142
- iat: Math.floor(Date.now() / 1000),
143
- exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, // 24 hours
144
- };
145
-
146
- request.serviceClaims = devClaims;
147
- request.isServiceAuthenticated = true;
148
- console.log(
149
- `🔧 Development mode: Service authenticated with token "${token}"`,
150
- );
151
- return; // Skip JWT verification in development
152
- } catch (error) {
153
- return reply.code(401).send({
154
- error: "Invalid development token",
155
- code: "INVALID_DEVELOPMENT_TOKEN",
156
- message: error instanceof Error ? error.message : "Unknown error",
157
- });
158
- }
159
- }
160
-
161
- // Verify the token
162
- const claims = this.verifyServiceToken(token);
163
-
164
- // Check required permissions
165
- if (requiredPermissions.length > 0) {
166
- const hasPermissions = requiredPermissions.every((perm) =>
167
- claims.permissions.includes(perm),
168
- );
169
-
170
- if (!hasPermissions) {
171
- return reply.code(403).send({
172
- error: "Insufficient permissions",
173
- code: "INSUFFICIENT_PERMISSIONS",
174
- required: requiredPermissions,
175
- granted: claims.permissions,
176
- });
177
- }
178
- }
179
-
180
- // Attach claims to request
181
- request.serviceClaims = claims;
182
- request.isServiceAuthenticated = true;
183
-
184
- console.log(`Service authenticated: ${claims.service}`);
185
- } catch (error) {
186
- return reply.code(401).send({
187
- error: "Invalid service token",
188
- code: "INVALID_SERVICE_TOKEN",
189
- message: error instanceof Error ? error.message : "Unknown error",
190
- });
191
- }
192
- };
193
- }
194
-
195
- // Alternative HMAC-based authentication
196
- generateHMACSignature(payload: string, timestamp: string): string {
197
- const crypto = require("node:crypto");
198
- const message = `${timestamp}.${payload}`;
199
- return crypto
200
- .createHmac("sha256", this.secret)
201
- .update(message)
202
- .digest("hex");
203
- }
204
-
205
- // HMAC middleware
206
- hmacAuthMiddleware() {
207
- return async (request: AuthenticatedRequest, reply: FastifyReply) => {
208
- try {
209
- const signature = request.headers["x-signature"] as string;
210
- const timestamp = request.headers["x-timestamp"] as string;
211
- const service = request.headers["x-service"] as string;
212
-
213
- if (!signature || !timestamp || !service) {
214
- return reply.code(401).send({
215
- error: "HMAC authentication headers missing",
216
- code: "MISSING_HMAC_HEADERS",
217
- });
218
- }
219
-
220
- // Check timestamp (prevent replay attacks)
221
- const now = Date.now();
222
- const requestTime = Number.parseInt(timestamp);
223
- const timeDiff = Math.abs(now - requestTime);
224
-
225
- // Allow 5 minutes tolerance
226
- if (timeDiff > 300000) {
227
- return reply.code(401).send({
228
- error: "Request timestamp too old",
229
- code: "TIMESTAMP_EXPIRED",
230
- });
231
- }
232
-
233
- // Get request body as string
234
- const body = JSON.stringify(request.body || {});
235
- const expectedSignature = this.generateHMACSignature(body, timestamp);
236
-
237
- if (signature !== expectedSignature) {
238
- return reply.code(401).send({
239
- error: "Invalid HMAC signature",
240
- code: "INVALID_HMAC_SIGNATURE",
241
- });
242
- }
243
-
244
- // Set service info on request
245
- request.serviceClaims = {
246
- service,
247
- permissions: [], // HMAC doesn't include permissions by default
248
- iat: requestTime,
249
- exp: requestTime + 300000, // 5 minutes
250
- iss: "axova-platform",
251
- };
252
- request.isServiceAuthenticated = true;
253
-
254
- console.log(`Service authenticated via HMAC: ${service}`);
255
- } catch (error) {
256
- return reply.code(401).send({
257
- error: "HMAC authentication failed",
258
- code: "HMAC_AUTH_FAILED",
259
- message: error instanceof Error ? error.message : "Unknown error",
260
- });
261
- }
262
- };
263
- }
264
- }
265
-
266
- // Singleton factory
267
- let serviceAuthInstance: ServiceAuthenticator | null = null;
268
-
269
- export function createServiceAuthenticator(
270
- config: ServiceAuthConfig,
271
- ): ServiceAuthenticator {
272
- if (!serviceAuthInstance) {
273
- serviceAuthInstance = new ServiceAuthenticator(config);
274
- }
275
- return serviceAuthInstance;
276
- }
277
-
278
- export function getServiceAuthenticator(): ServiceAuthenticator {
279
- if (!serviceAuthInstance) {
280
- throw new Error(
281
- "Service authenticator not created. Call createServiceAuthenticator() first.",
282
- );
283
- }
284
- return serviceAuthInstance;
285
- }
286
-
287
- // Utility to get auth config from environment
288
- export function getServiceAuthConfigFromEnv(): ServiceAuthConfig {
289
- const internalSecret = process.env.INTERNAL_SECRET;
290
-
291
- if (!internalSecret) {
292
- throw new Error("INTERNAL_SECRET environment variable is required");
293
- }
294
-
295
- return {
296
- internalSecret,
297
- tokenExpiry: process.env.SERVICE_TOKEN_EXPIRY || "1h",
298
- };
299
- }
300
-
301
- // Common service permissions
302
- export const SERVICE_PERMISSIONS = {
303
- // Store service permissions
304
- STORE_READ: "store:read",
305
- STORE_WRITE: "store:write",
306
- STORE_DELETE: "store:delete",
307
-
308
- // Compliance service permissions
309
- COMPLIANCE_READ: "compliance:read",
310
- COMPLIANCE_WRITE: "compliance:write",
311
- COMPLIANCE_BAN: "compliance:ban",
312
- COMPLIANCE_APPEAL: "compliance:appeal",
313
-
314
- // Notification service permissions
315
- NOTIFICATION_SEND: "notification:send",
316
- NOTIFICATION_READ: "notification:read",
317
-
318
- // Audit service permissions
319
- AUDIT_READ: "audit:read",
320
- AUDIT_WRITE: "audit:write",
321
-
322
- // Admin service permissions
323
- ADMIN_OVERRIDE: "admin:override",
324
- ADMIN_REVIEW: "admin:review",
325
-
326
- // AI moderation permissions
327
- AI_MODERATE: "ai:moderate",
328
- } as const;
1
+ import type { FastifyReply, FastifyRequest } from "fastify";
2
+ import jwt from "jsonwebtoken";
3
+ import { z } from "zod";
4
+
5
+ // Service claims schema
6
+ export const ServiceClaimsSchema = z.object({
7
+ service: z.string(),
8
+ permissions: z.array(z.string()),
9
+ iat: z.number(),
10
+ exp: z.number(),
11
+ iss: z.literal("axova-platform"),
12
+ });
13
+
14
+ export type ServiceClaims = z.infer<typeof ServiceClaimsSchema>;
15
+
16
+ // Service authentication configuration
17
+ export interface ServiceAuthConfig {
18
+ internalSecret: string;
19
+ tokenExpiry?: string; // Default: '1h'
20
+ requiredPermissions?: string[];
21
+ }
22
+
23
+ // Extended request interface
24
+ export interface AuthenticatedRequest extends FastifyRequest {
25
+ serviceClaims?: ServiceClaims;
26
+ isServiceAuthenticated?: boolean;
27
+ }
28
+
29
+ // Service authentication class
30
+ export class ServiceAuthenticator {
31
+ private secret: string;
32
+ private tokenExpiry: string;
33
+
34
+ constructor(config: ServiceAuthConfig) {
35
+ this.secret = config.internalSecret;
36
+ this.tokenExpiry = config.tokenExpiry || "1h";
37
+ }
38
+
39
+ // Generate service token
40
+ generateServiceToken(service: string, permissions: string[] = []): string {
41
+ const payload: Omit<ServiceClaims, "iat" | "exp"> = {
42
+ service,
43
+ permissions,
44
+ iss: "axova-platform",
45
+ };
46
+
47
+ return jwt.sign(payload, this.secret, {
48
+ expiresIn: this.tokenExpiry,
49
+ algorithm: "HS256",
50
+ } as jwt.SignOptions);
51
+ }
52
+
53
+ // Verify service token
54
+ verifyServiceToken(token: string): ServiceClaims {
55
+ try {
56
+ const decoded = jwt.verify(token, this.secret, {
57
+ algorithms: ["HS256"],
58
+ issuer: "axova-platform",
59
+ }) as ServiceClaims;
60
+
61
+ // Validate with Zod schema
62
+ return ServiceClaimsSchema.parse(decoded);
63
+ } catch (error) {
64
+ throw new Error(
65
+ `Invalid service token: ${error instanceof Error ? error.message : "Unknown error"}`,
66
+ );
67
+ }
68
+ }
69
+
70
+ // Fastify middleware for service authentication
71
+ serviceAuthMiddleware(requiredPermissions: string[] = []) {
72
+ return async (request: AuthenticatedRequest, reply: FastifyReply) => {
73
+ try {
74
+ const authHeader = request.headers.authorization;
75
+ const serviceToken = request.headers["x-service-token"] as string;
76
+
77
+ let token: string | null = null;
78
+
79
+ // Check for Bearer token in Authorization header
80
+ if (authHeader?.startsWith("Bearer ")) {
81
+ token = authHeader.substring(7);
82
+ }
83
+ // Check for service token in custom header
84
+ else if (serviceToken) {
85
+ token = serviceToken;
86
+ }
87
+
88
+ if (!token) {
89
+ return reply.code(401).send({
90
+ error: "Service authentication required",
91
+ code: "MISSING_SERVICE_TOKEN",
92
+ });
93
+ }
94
+
95
+ // DEVELOPMENT MODE: Accept simple constant tokens
96
+ const isDevelopment = process.env.NODE_ENV !== "production";
97
+ const developmentTokens = [
98
+ "dev-compliance-token",
99
+ "development-mode",
100
+ "compliance-dev-access",
101
+ "simple-dev-token",
102
+ "inventory-dev-access",
103
+ "product-dev-access",
104
+ "oxa-dev-access",
105
+ ];
106
+
107
+ if (isDevelopment && developmentTokens.includes(token)) {
108
+ // In development, validate against a proper development service registry
109
+ // instead of using mock claims
110
+ try {
111
+ // TODO: Implement proper development service authentication
112
+ // For now, only allow specific development services with limited permissions
113
+ let servicePermissions: string[] = [];
114
+ let serviceName = "unknown-service";
115
+
116
+ // Grant permissions based on the specific development token
117
+ switch (token) {
118
+ case "inventory-dev-access":
119
+ serviceName = "inventory-service-dev";
120
+ servicePermissions = ["store:read", "store:write"];
121
+ break;
122
+ case "product-dev-access":
123
+ serviceName = "product-service-dev";
124
+ servicePermissions = ["store:read", "store:write"];
125
+ break;
126
+ case "compliance-dev-access":
127
+ serviceName = "compliance-service-dev";
128
+ servicePermissions = ["compliance:read", "compliance:write"];
129
+ break;
130
+ case "oxa-dev-access":
131
+ serviceName = "oxa-service-dev";
132
+ servicePermissions = ["store:read", "ai:moderate"];
133
+ break;
134
+ default:
135
+ throw new Error("Unknown development token");
136
+ }
137
+
138
+ const devClaims: ServiceClaims = {
139
+ service: serviceName,
140
+ permissions: servicePermissions,
141
+ iss: "axova-platform",
142
+ iat: Math.floor(Date.now() / 1000),
143
+ exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, // 24 hours
144
+ };
145
+
146
+ request.serviceClaims = devClaims;
147
+ request.isServiceAuthenticated = true;
148
+ console.log(
149
+ `🔧 Development mode: Service authenticated with token "${token}"`,
150
+ );
151
+ return; // Skip JWT verification in development
152
+ } catch (error) {
153
+ return reply.code(401).send({
154
+ error: "Invalid development token",
155
+ code: "INVALID_DEVELOPMENT_TOKEN",
156
+ message: error instanceof Error ? error.message : "Unknown error",
157
+ });
158
+ }
159
+ }
160
+
161
+ // Verify the token
162
+ const claims = this.verifyServiceToken(token);
163
+
164
+ // Check required permissions
165
+ if (requiredPermissions.length > 0) {
166
+ const hasPermissions = requiredPermissions.every((perm) =>
167
+ claims.permissions.includes(perm),
168
+ );
169
+
170
+ if (!hasPermissions) {
171
+ return reply.code(403).send({
172
+ error: "Insufficient permissions",
173
+ code: "INSUFFICIENT_PERMISSIONS",
174
+ required: requiredPermissions,
175
+ granted: claims.permissions,
176
+ });
177
+ }
178
+ }
179
+
180
+ // Attach claims to request
181
+ request.serviceClaims = claims;
182
+ request.isServiceAuthenticated = true;
183
+
184
+ console.log(`Service authenticated: ${claims.service}`);
185
+ } catch (error) {
186
+ return reply.code(401).send({
187
+ error: "Invalid service token",
188
+ code: "INVALID_SERVICE_TOKEN",
189
+ message: error instanceof Error ? error.message : "Unknown error",
190
+ });
191
+ }
192
+ };
193
+ }
194
+
195
+ // Alternative HMAC-based authentication
196
+ generateHMACSignature(payload: string, timestamp: string): string {
197
+ const crypto = require("node:crypto");
198
+ const message = `${timestamp}.${payload}`;
199
+ return crypto
200
+ .createHmac("sha256", this.secret)
201
+ .update(message)
202
+ .digest("hex");
203
+ }
204
+
205
+ // HMAC middleware
206
+ hmacAuthMiddleware() {
207
+ return async (request: AuthenticatedRequest, reply: FastifyReply) => {
208
+ try {
209
+ const signature = request.headers["x-signature"] as string;
210
+ const timestamp = request.headers["x-timestamp"] as string;
211
+ const service = request.headers["x-service"] as string;
212
+
213
+ if (!signature || !timestamp || !service) {
214
+ return reply.code(401).send({
215
+ error: "HMAC authentication headers missing",
216
+ code: "MISSING_HMAC_HEADERS",
217
+ });
218
+ }
219
+
220
+ // Check timestamp (prevent replay attacks)
221
+ const now = Date.now();
222
+ const requestTime = Number.parseInt(timestamp);
223
+ const timeDiff = Math.abs(now - requestTime);
224
+
225
+ // Allow 5 minutes tolerance
226
+ if (timeDiff > 300000) {
227
+ return reply.code(401).send({
228
+ error: "Request timestamp too old",
229
+ code: "TIMESTAMP_EXPIRED",
230
+ });
231
+ }
232
+
233
+ // Get request body as string
234
+ const body = JSON.stringify(request.body || {});
235
+ const expectedSignature = this.generateHMACSignature(body, timestamp);
236
+
237
+ if (signature !== expectedSignature) {
238
+ return reply.code(401).send({
239
+ error: "Invalid HMAC signature",
240
+ code: "INVALID_HMAC_SIGNATURE",
241
+ });
242
+ }
243
+
244
+ // Set service info on request
245
+ request.serviceClaims = {
246
+ service,
247
+ permissions: [], // HMAC doesn't include permissions by default
248
+ iat: requestTime,
249
+ exp: requestTime + 300000, // 5 minutes
250
+ iss: "axova-platform",
251
+ };
252
+ request.isServiceAuthenticated = true;
253
+
254
+ console.log(`Service authenticated via HMAC: ${service}`);
255
+ } catch (error) {
256
+ return reply.code(401).send({
257
+ error: "HMAC authentication failed",
258
+ code: "HMAC_AUTH_FAILED",
259
+ message: error instanceof Error ? error.message : "Unknown error",
260
+ });
261
+ }
262
+ };
263
+ }
264
+ }
265
+
266
+ // Singleton factory
267
+ let serviceAuthInstance: ServiceAuthenticator | null = null;
268
+
269
+ export function createServiceAuthenticator(
270
+ config: ServiceAuthConfig,
271
+ ): ServiceAuthenticator {
272
+ if (!serviceAuthInstance) {
273
+ serviceAuthInstance = new ServiceAuthenticator(config);
274
+ }
275
+ return serviceAuthInstance;
276
+ }
277
+
278
+ export function getServiceAuthenticator(): ServiceAuthenticator {
279
+ if (!serviceAuthInstance) {
280
+ throw new Error(
281
+ "Service authenticator not created. Call createServiceAuthenticator() first.",
282
+ );
283
+ }
284
+ return serviceAuthInstance;
285
+ }
286
+
287
+ // Utility to get auth config from environment
288
+ export function getServiceAuthConfigFromEnv(): ServiceAuthConfig {
289
+ const internalSecret = process.env.INTERNAL_SECRET;
290
+
291
+ if (!internalSecret) {
292
+ throw new Error("INTERNAL_SECRET environment variable is required");
293
+ }
294
+
295
+ return {
296
+ internalSecret,
297
+ tokenExpiry: process.env.SERVICE_TOKEN_EXPIRY || "1h",
298
+ };
299
+ }
300
+
301
+ // Common service permissions
302
+ export const SERVICE_PERMISSIONS = {
303
+ // Store service permissions
304
+ STORE_READ: "store:read",
305
+ STORE_WRITE: "store:write",
306
+ STORE_DELETE: "store:delete",
307
+
308
+ // Compliance service permissions
309
+ COMPLIANCE_READ: "compliance:read",
310
+ COMPLIANCE_WRITE: "compliance:write",
311
+ COMPLIANCE_BAN: "compliance:ban",
312
+ COMPLIANCE_APPEAL: "compliance:appeal",
313
+
314
+ // Notification service permissions
315
+ NOTIFICATION_SEND: "notification:send",
316
+ NOTIFICATION_READ: "notification:read",
317
+
318
+ // Audit service permissions
319
+ AUDIT_READ: "audit:read",
320
+ AUDIT_WRITE: "audit:write",
321
+
322
+ // Admin service permissions
323
+ ADMIN_OVERRIDE: "admin:override",
324
+ ADMIN_REVIEW: "admin:review",
325
+
326
+ // AI moderation permissions
327
+ AI_MODERATE: "ai:moderate",
328
+ } as const;