@axova/shared 1.0.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 (112) hide show
  1. package/CONFIGURATION_GUIDE.md +1 -0
  2. package/README.md +384 -0
  3. package/SCHEMA_ORGANIZATION.md +209 -0
  4. package/dist/configs/index.d.ts +85 -0
  5. package/dist/configs/index.js +555 -0
  6. package/dist/events/kafka.d.ts +40 -0
  7. package/dist/events/kafka.js +311 -0
  8. package/dist/index.d.ts +13 -0
  9. package/dist/index.js +41 -0
  10. package/dist/interfaces/customer-events.d.ts +85 -0
  11. package/dist/interfaces/customer-events.js +2 -0
  12. package/dist/interfaces/inventory-events.d.ts +453 -0
  13. package/dist/interfaces/inventory-events.js +3 -0
  14. package/dist/interfaces/inventory-types.d.ts +894 -0
  15. package/dist/interfaces/inventory-types.js +3 -0
  16. package/dist/interfaces/order-events.d.ts +320 -0
  17. package/dist/interfaces/order-events.js +3 -0
  18. package/dist/lib/auditLogger.d.ts +162 -0
  19. package/dist/lib/auditLogger.js +626 -0
  20. package/dist/lib/authOrganization.d.ts +24 -0
  21. package/dist/lib/authOrganization.js +110 -0
  22. package/dist/lib/db.d.ts +6 -0
  23. package/dist/lib/db.js +88 -0
  24. package/dist/middleware/serviceAuth.d.ts +60 -0
  25. package/dist/middleware/serviceAuth.js +272 -0
  26. package/dist/middleware/storeOwnership.d.ts +15 -0
  27. package/dist/middleware/storeOwnership.js +156 -0
  28. package/dist/middleware/storeValidationMiddleware.d.ts +44 -0
  29. package/dist/middleware/storeValidationMiddleware.js +180 -0
  30. package/dist/middleware/userAuth.d.ts +27 -0
  31. package/dist/middleware/userAuth.js +218 -0
  32. package/dist/schemas/admin/admin-schema.d.ts +741 -0
  33. package/dist/schemas/admin/admin-schema.js +111 -0
  34. package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +648 -0
  35. package/dist/schemas/ai-moderation/ai-moderation-schema.js +88 -0
  36. package/dist/schemas/common/common-schemas.d.ts +436 -0
  37. package/dist/schemas/common/common-schemas.js +94 -0
  38. package/dist/schemas/compliance/compliance-schema.d.ts +3388 -0
  39. package/dist/schemas/compliance/compliance-schema.js +472 -0
  40. package/dist/schemas/compliance/kyc-schema.d.ts +2642 -0
  41. package/dist/schemas/compliance/kyc-schema.js +361 -0
  42. package/dist/schemas/customer/customer-schema.d.ts +2727 -0
  43. package/dist/schemas/customer/customer-schema.js +399 -0
  44. package/dist/schemas/index.d.ts +27 -0
  45. package/dist/schemas/index.js +138 -0
  46. package/dist/schemas/inventory/inventory-tables.d.ts +9476 -0
  47. package/dist/schemas/inventory/inventory-tables.js +1470 -0
  48. package/dist/schemas/inventory/lot-tables.d.ts +3281 -0
  49. package/dist/schemas/inventory/lot-tables.js +608 -0
  50. package/dist/schemas/order/order-schema.d.ts +5825 -0
  51. package/dist/schemas/order/order-schema.js +954 -0
  52. package/dist/schemas/product/discount-relations.d.ts +15 -0
  53. package/dist/schemas/product/discount-relations.js +34 -0
  54. package/dist/schemas/product/discount-schema.d.ts +1975 -0
  55. package/dist/schemas/product/discount-schema.js +297 -0
  56. package/dist/schemas/product/product-relations.d.ts +41 -0
  57. package/dist/schemas/product/product-relations.js +133 -0
  58. package/dist/schemas/product/product-schema.d.ts +4544 -0
  59. package/dist/schemas/product/product-schema.js +671 -0
  60. package/dist/schemas/store/store-audit-schema.d.ts +4135 -0
  61. package/dist/schemas/store/store-audit-schema.js +556 -0
  62. package/dist/schemas/store/store-schema.d.ts +3100 -0
  63. package/dist/schemas/store/store-schema.js +381 -0
  64. package/dist/schemas/store/store-settings-schema.d.ts +665 -0
  65. package/dist/schemas/store/store-settings-schema.js +141 -0
  66. package/dist/schemas/types.d.ts +50 -0
  67. package/dist/schemas/types.js +3 -0
  68. package/dist/types/events.d.ts +2396 -0
  69. package/dist/types/events.js +505 -0
  70. package/dist/utils/errorHandler.d.ts +12 -0
  71. package/dist/utils/errorHandler.js +36 -0
  72. package/dist/utils/subdomain.d.ts +6 -0
  73. package/dist/utils/subdomain.js +20 -0
  74. package/nul +8 -0
  75. package/package.json +43 -0
  76. package/src/configs/index.ts +654 -0
  77. package/src/events/kafka.ts +429 -0
  78. package/src/index.ts +26 -0
  79. package/src/interfaces/customer-events.ts +106 -0
  80. package/src/interfaces/inventory-events.ts +545 -0
  81. package/src/interfaces/inventory-types.ts +1004 -0
  82. package/src/interfaces/order-events.ts +381 -0
  83. package/src/lib/auditLogger.ts +1117 -0
  84. package/src/lib/authOrganization.ts +153 -0
  85. package/src/lib/db.ts +64 -0
  86. package/src/middleware/serviceAuth.ts +328 -0
  87. package/src/middleware/storeOwnership.ts +199 -0
  88. package/src/middleware/storeValidationMiddleware.ts +247 -0
  89. package/src/middleware/userAuth.ts +248 -0
  90. package/src/schemas/admin/admin-schema.ts +208 -0
  91. package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -0
  92. package/src/schemas/common/common-schemas.ts +108 -0
  93. package/src/schemas/compliance/compliance-schema.ts +927 -0
  94. package/src/schemas/compliance/kyc-schema.ts +649 -0
  95. package/src/schemas/customer/customer-schema.ts +576 -0
  96. package/src/schemas/index.ts +189 -0
  97. package/src/schemas/inventory/inventory-tables.ts +1927 -0
  98. package/src/schemas/inventory/lot-tables.ts +799 -0
  99. package/src/schemas/order/order-schema.ts +1400 -0
  100. package/src/schemas/product/discount-relations.ts +44 -0
  101. package/src/schemas/product/discount-schema.ts +464 -0
  102. package/src/schemas/product/product-relations.ts +187 -0
  103. package/src/schemas/product/product-schema.ts +955 -0
  104. package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
  105. package/src/schemas/store/store-audit-schema.ts +1257 -0
  106. package/src/schemas/store/store-schema.ts +661 -0
  107. package/src/schemas/store/store-settings-schema.ts +231 -0
  108. package/src/schemas/types.ts +67 -0
  109. package/src/types/events.ts +646 -0
  110. package/src/utils/errorHandler.ts +44 -0
  111. package/src/utils/subdomain.ts +19 -0
  112. package/tsconfig.json +21 -0
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthOrganization = void 0;
4
+ const errorHandler_1 = require("../utils/errorHandler");
5
+ /**
6
+ * Creates an organization using the auth service
7
+ * @param organizationData Organization data to create
8
+ * @param headers Authentication headers to forward
9
+ * @returns Promise resolving to created organization data
10
+ */
11
+ const createAuthOrganization = async (organizationData, headers) => {
12
+ try {
13
+ console.log("Creating organization:", {
14
+ name: organizationData.name,
15
+ slug: organizationData.slug,
16
+ userId: organizationData.userId,
17
+ hasHeaders: !!headers,
18
+ });
19
+ // Prepare payload matching auth service expectations
20
+ const payload = {
21
+ name: organizationData.name,
22
+ slug: organizationData.slug,
23
+ ...(organizationData.logo && { logo: organizationData.logo }),
24
+ ...(organizationData.metadata && { metadata: organizationData.metadata }),
25
+ };
26
+ // Make direct HTTP request to auth service
27
+ const authBaseUrl = process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:4000";
28
+ const url = `${authBaseUrl}/api/auth/organization/create`;
29
+ const fetchOptions = {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ ...headers,
34
+ },
35
+ body: JSON.stringify(payload),
36
+ };
37
+ console.log("Sending request to auth service:", url);
38
+ const response = await fetch(url, fetchOptions);
39
+ console.log("Auth service response:", {
40
+ status: response.status,
41
+ statusText: response.statusText,
42
+ ok: response.ok,
43
+ headers: Object.fromEntries(response.headers.entries()),
44
+ });
45
+ // Check HTTP response status
46
+ if (!response.ok) {
47
+ const errorText = await response.text();
48
+ console.error("Auth service HTTP error:", {
49
+ status: response.status,
50
+ statusText: response.statusText,
51
+ body: errorText,
52
+ });
53
+ throw new errorHandler_1.APIError(`Auth service error: ${response.status} ${response.statusText}`, response.status, { code: "AUTH_HTTP_ERROR", response: errorText });
54
+ }
55
+ // Parse JSON response
56
+ let responseData;
57
+ try {
58
+ responseData = await response.json();
59
+ }
60
+ catch (parseError) {
61
+ console.error("Failed to parse auth service response:", parseError);
62
+ throw new errorHandler_1.APIError("Invalid response format from auth service", 500, {
63
+ code: "JSON_PARSE_ERROR",
64
+ originalError: parseError,
65
+ });
66
+ }
67
+ // Auth service returns organization data directly
68
+ // Check if response has the required organization fields
69
+ if (responseData.id && responseData.name) {
70
+ console.log("Organization created successfully:", {
71
+ id: responseData.id,
72
+ name: responseData.name,
73
+ slug: responseData.slug,
74
+ });
75
+ }
76
+ else {
77
+ console.error("Invalid organization response:", responseData);
78
+ throw new errorHandler_1.APIError("Invalid organization data format from auth service", 500, { code: "INVALID_ORGANIZATION_DATA", response: responseData });
79
+ }
80
+ // Return structured organization data (responseData contains the org data directly)
81
+ return {
82
+ id: responseData.id,
83
+ name: responseData.name,
84
+ slug: responseData.slug,
85
+ logo: responseData.logo || undefined,
86
+ metadata: responseData.metadata,
87
+ createdAt: new Date(responseData.createdAt),
88
+ updatedAt: undefined,
89
+ };
90
+ }
91
+ catch (error) {
92
+ console.error("Organization creation failed:", {
93
+ message: error.message,
94
+ status: error.status || error.statusCode,
95
+ code: error.code,
96
+ });
97
+ // Handle different error types
98
+ if (error instanceof errorHandler_1.APIError) {
99
+ throw error;
100
+ }
101
+ if (error.message && error.message.includes("JSON")) {
102
+ throw new errorHandler_1.APIError("Invalid response format from auth service", 500, {
103
+ code: "JSON_PARSE_ERROR",
104
+ originalError: error.message,
105
+ });
106
+ }
107
+ throw new errorHandler_1.APIError(error.message || "Failed to create organization", error.status || error.statusCode || 500, { code: "ORGANIZATION_CREATION_FAILED", originalError: error });
108
+ }
109
+ };
110
+ exports.createAuthOrganization = createAuthOrganization;
@@ -0,0 +1,6 @@
1
+ import { Pool } from "pg";
2
+ declare const pool: Pool;
3
+ export declare const db: import("drizzle-orm/node-postgres").NodePgDatabase<Record<string, never>> & {
4
+ $client: Pool;
5
+ };
6
+ export { pool };
package/dist/lib/db.js ADDED
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.pool = exports.db = void 0;
37
+ const dotenv = __importStar(require("dotenv"));
38
+ const node_postgres_1 = require("drizzle-orm/node-postgres");
39
+ const pg_1 = require("pg");
40
+ // Load environment variables from .env file
41
+ dotenv.config();
42
+ // Configure PostgreSQL connection
43
+ const connectionString = process.env.DATABASE_URL;
44
+ if (!connectionString) {
45
+ throw new Error("DATABASE_URL environment variable is not set");
46
+ }
47
+ // Configure PostgreSQL to handle timestamps and dates properly
48
+ const pool = new pg_1.Pool({
49
+ connectionString,
50
+ ssl: process.env.NODE_ENV === "production"
51
+ ? { rejectUnauthorized: false }
52
+ : false,
53
+ // Connection pool configuration to prevent "Connection is closed" errors
54
+ max: 20, // Maximum number of clients in the pool
55
+ min: 2, // Minimum number of clients in the pool
56
+ idleTimeoutMillis: 30000, // Close idle clients after 30 seconds
57
+ connectionTimeoutMillis: 10000, // Wait 10 seconds for a connection
58
+ // Keep connections alive
59
+ keepAlive: true,
60
+ keepAliveInitialDelayMillis: 10000,
61
+ // Add parsing configuration for timestamps
62
+ types: {
63
+ getTypeParser: (typeId, _format) => {
64
+ // Handle dates and timestamps to ensure they're properly converted to JavaScript Date objects
65
+ if (typeId === 1082 ||
66
+ typeId === 1083 ||
67
+ typeId === 1114 ||
68
+ typeId === 1184) {
69
+ return (val) => (val === null ? null : new Date(val));
70
+ }
71
+ return undefined;
72
+ },
73
+ },
74
+ });
75
+ exports.pool = pool;
76
+ // Add error handler to the pool
77
+ pool.on("error", (err) => {
78
+ console.error("❌ Unexpected database pool error:", err);
79
+ });
80
+ pool.on("connect", () => {
81
+ if (process.env.NODE_ENV === "development") {
82
+ console.log("✅ Database pool connection established");
83
+ }
84
+ });
85
+ // Create a Drizzle ORM instance
86
+ exports.db = (0, node_postgres_1.drizzle)(pool, {
87
+ logger: process.env.NODE_ENV === "development",
88
+ });
@@ -0,0 +1,60 @@
1
+ import type { FastifyReply, FastifyRequest } from "fastify";
2
+ import { z } from "zod";
3
+ export declare const ServiceClaimsSchema: z.ZodObject<{
4
+ service: z.ZodString;
5
+ permissions: z.ZodArray<z.ZodString, "many">;
6
+ iat: z.ZodNumber;
7
+ exp: z.ZodNumber;
8
+ iss: z.ZodLiteral<"axova-platform">;
9
+ }, "strip", z.ZodTypeAny, {
10
+ service: string;
11
+ permissions: string[];
12
+ iat: number;
13
+ exp: number;
14
+ iss: "axova-platform";
15
+ }, {
16
+ service: string;
17
+ permissions: string[];
18
+ iat: number;
19
+ exp: number;
20
+ iss: "axova-platform";
21
+ }>;
22
+ export type ServiceClaims = z.infer<typeof ServiceClaimsSchema>;
23
+ export interface ServiceAuthConfig {
24
+ internalSecret: string;
25
+ tokenExpiry?: string;
26
+ requiredPermissions?: string[];
27
+ }
28
+ export interface AuthenticatedRequest extends FastifyRequest {
29
+ serviceClaims?: ServiceClaims;
30
+ isServiceAuthenticated?: boolean;
31
+ }
32
+ export declare class ServiceAuthenticator {
33
+ private secret;
34
+ private tokenExpiry;
35
+ constructor(config: ServiceAuthConfig);
36
+ generateServiceToken(service: string, permissions?: string[]): string;
37
+ verifyServiceToken(token: string): ServiceClaims;
38
+ serviceAuthMiddleware(requiredPermissions?: string[]): (request: AuthenticatedRequest, reply: FastifyReply) => Promise<undefined>;
39
+ generateHMACSignature(payload: string, timestamp: string): string;
40
+ hmacAuthMiddleware(): (request: AuthenticatedRequest, reply: FastifyReply) => Promise<undefined>;
41
+ }
42
+ export declare function createServiceAuthenticator(config: ServiceAuthConfig): ServiceAuthenticator;
43
+ export declare function getServiceAuthenticator(): ServiceAuthenticator;
44
+ export declare function getServiceAuthConfigFromEnv(): ServiceAuthConfig;
45
+ export declare const SERVICE_PERMISSIONS: {
46
+ readonly STORE_READ: "store:read";
47
+ readonly STORE_WRITE: "store:write";
48
+ readonly STORE_DELETE: "store:delete";
49
+ readonly COMPLIANCE_READ: "compliance:read";
50
+ readonly COMPLIANCE_WRITE: "compliance:write";
51
+ readonly COMPLIANCE_BAN: "compliance:ban";
52
+ readonly COMPLIANCE_APPEAL: "compliance:appeal";
53
+ readonly NOTIFICATION_SEND: "notification:send";
54
+ readonly NOTIFICATION_READ: "notification:read";
55
+ readonly AUDIT_READ: "audit:read";
56
+ readonly AUDIT_WRITE: "audit:write";
57
+ readonly ADMIN_OVERRIDE: "admin:override";
58
+ readonly ADMIN_REVIEW: "admin:review";
59
+ readonly AI_MODERATE: "ai:moderate";
60
+ };
@@ -0,0 +1,272 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SERVICE_PERMISSIONS = exports.ServiceAuthenticator = exports.ServiceClaimsSchema = void 0;
7
+ exports.createServiceAuthenticator = createServiceAuthenticator;
8
+ exports.getServiceAuthenticator = getServiceAuthenticator;
9
+ exports.getServiceAuthConfigFromEnv = getServiceAuthConfigFromEnv;
10
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
11
+ const zod_1 = require("zod");
12
+ // Service claims schema
13
+ exports.ServiceClaimsSchema = zod_1.z.object({
14
+ service: zod_1.z.string(),
15
+ permissions: zod_1.z.array(zod_1.z.string()),
16
+ iat: zod_1.z.number(),
17
+ exp: zod_1.z.number(),
18
+ iss: zod_1.z.literal("axova-platform"),
19
+ });
20
+ // Service authentication class
21
+ class ServiceAuthenticator {
22
+ constructor(config) {
23
+ this.secret = config.internalSecret;
24
+ this.tokenExpiry = config.tokenExpiry || "1h";
25
+ }
26
+ // Generate service token
27
+ generateServiceToken(service, permissions = []) {
28
+ const payload = {
29
+ service,
30
+ permissions,
31
+ iss: "axova-platform",
32
+ };
33
+ return jsonwebtoken_1.default.sign(payload, this.secret, {
34
+ expiresIn: this.tokenExpiry,
35
+ algorithm: "HS256",
36
+ });
37
+ }
38
+ // Verify service token
39
+ verifyServiceToken(token) {
40
+ try {
41
+ const decoded = jsonwebtoken_1.default.verify(token, this.secret, {
42
+ algorithms: ["HS256"],
43
+ issuer: "axova-platform",
44
+ });
45
+ // Validate with Zod schema
46
+ return exports.ServiceClaimsSchema.parse(decoded);
47
+ }
48
+ catch (error) {
49
+ throw new Error(`Invalid service token: ${error instanceof Error ? error.message : "Unknown error"}`);
50
+ }
51
+ }
52
+ // Fastify middleware for service authentication
53
+ serviceAuthMiddleware(requiredPermissions = []) {
54
+ return async (request, reply) => {
55
+ try {
56
+ const authHeader = request.headers.authorization;
57
+ const serviceToken = request.headers["x-service-token"];
58
+ let token = null;
59
+ // Check for Bearer token in Authorization header
60
+ if (authHeader?.startsWith("Bearer ")) {
61
+ token = authHeader.substring(7);
62
+ }
63
+ // Check for service token in custom header
64
+ else if (serviceToken) {
65
+ token = serviceToken;
66
+ }
67
+ if (!token) {
68
+ return reply.code(401).send({
69
+ error: "Service authentication required",
70
+ code: "MISSING_SERVICE_TOKEN",
71
+ });
72
+ }
73
+ // DEVELOPMENT MODE: Accept simple constant tokens
74
+ const isDevelopment = process.env.NODE_ENV !== "production";
75
+ const developmentTokens = [
76
+ "dev-compliance-token",
77
+ "development-mode",
78
+ "compliance-dev-access",
79
+ "simple-dev-token",
80
+ "inventory-dev-access",
81
+ "product-dev-access",
82
+ "oxa-dev-access",
83
+ ];
84
+ if (isDevelopment && developmentTokens.includes(token)) {
85
+ // In development, validate against a proper development service registry
86
+ // instead of using mock claims
87
+ try {
88
+ // TODO: Implement proper development service authentication
89
+ // For now, only allow specific development services with limited permissions
90
+ let servicePermissions = [];
91
+ let serviceName = "unknown-service";
92
+ // Grant permissions based on the specific development token
93
+ switch (token) {
94
+ case "inventory-dev-access":
95
+ serviceName = "inventory-service-dev";
96
+ servicePermissions = ["store:read", "store:write"];
97
+ break;
98
+ case "product-dev-access":
99
+ serviceName = "product-service-dev";
100
+ servicePermissions = ["store:read", "store:write"];
101
+ break;
102
+ case "compliance-dev-access":
103
+ serviceName = "compliance-service-dev";
104
+ servicePermissions = ["compliance:read", "compliance:write"];
105
+ break;
106
+ case "oxa-dev-access":
107
+ serviceName = "oxa-service-dev";
108
+ servicePermissions = ["store:read", "ai:moderate"];
109
+ break;
110
+ default:
111
+ throw new Error("Unknown development token");
112
+ }
113
+ const devClaims = {
114
+ service: serviceName,
115
+ permissions: servicePermissions,
116
+ iss: "axova-platform",
117
+ iat: Math.floor(Date.now() / 1000),
118
+ exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24, // 24 hours
119
+ };
120
+ request.serviceClaims = devClaims;
121
+ request.isServiceAuthenticated = true;
122
+ console.log(`🔧 Development mode: Service authenticated with token "${token}"`);
123
+ return; // Skip JWT verification in development
124
+ }
125
+ catch (error) {
126
+ return reply.code(401).send({
127
+ error: "Invalid development token",
128
+ code: "INVALID_DEVELOPMENT_TOKEN",
129
+ message: error instanceof Error ? error.message : "Unknown error",
130
+ });
131
+ }
132
+ }
133
+ // Verify the token
134
+ const claims = this.verifyServiceToken(token);
135
+ // Check required permissions
136
+ if (requiredPermissions.length > 0) {
137
+ const hasPermissions = requiredPermissions.every((perm) => claims.permissions.includes(perm));
138
+ if (!hasPermissions) {
139
+ return reply.code(403).send({
140
+ error: "Insufficient permissions",
141
+ code: "INSUFFICIENT_PERMISSIONS",
142
+ required: requiredPermissions,
143
+ granted: claims.permissions,
144
+ });
145
+ }
146
+ }
147
+ // Attach claims to request
148
+ request.serviceClaims = claims;
149
+ request.isServiceAuthenticated = true;
150
+ console.log(`Service authenticated: ${claims.service}`);
151
+ }
152
+ catch (error) {
153
+ return reply.code(401).send({
154
+ error: "Invalid service token",
155
+ code: "INVALID_SERVICE_TOKEN",
156
+ message: error instanceof Error ? error.message : "Unknown error",
157
+ });
158
+ }
159
+ };
160
+ }
161
+ // Alternative HMAC-based authentication
162
+ generateHMACSignature(payload, timestamp) {
163
+ const crypto = require("node:crypto");
164
+ const message = `${timestamp}.${payload}`;
165
+ return crypto
166
+ .createHmac("sha256", this.secret)
167
+ .update(message)
168
+ .digest("hex");
169
+ }
170
+ // HMAC middleware
171
+ hmacAuthMiddleware() {
172
+ return async (request, reply) => {
173
+ try {
174
+ const signature = request.headers["x-signature"];
175
+ const timestamp = request.headers["x-timestamp"];
176
+ const service = request.headers["x-service"];
177
+ if (!signature || !timestamp || !service) {
178
+ return reply.code(401).send({
179
+ error: "HMAC authentication headers missing",
180
+ code: "MISSING_HMAC_HEADERS",
181
+ });
182
+ }
183
+ // Check timestamp (prevent replay attacks)
184
+ const now = Date.now();
185
+ const requestTime = Number.parseInt(timestamp);
186
+ const timeDiff = Math.abs(now - requestTime);
187
+ // Allow 5 minutes tolerance
188
+ if (timeDiff > 300000) {
189
+ return reply.code(401).send({
190
+ error: "Request timestamp too old",
191
+ code: "TIMESTAMP_EXPIRED",
192
+ });
193
+ }
194
+ // Get request body as string
195
+ const body = JSON.stringify(request.body || {});
196
+ const expectedSignature = this.generateHMACSignature(body, timestamp);
197
+ if (signature !== expectedSignature) {
198
+ return reply.code(401).send({
199
+ error: "Invalid HMAC signature",
200
+ code: "INVALID_HMAC_SIGNATURE",
201
+ });
202
+ }
203
+ // Set service info on request
204
+ request.serviceClaims = {
205
+ service,
206
+ permissions: [], // HMAC doesn't include permissions by default
207
+ iat: requestTime,
208
+ exp: requestTime + 300000, // 5 minutes
209
+ iss: "axova-platform",
210
+ };
211
+ request.isServiceAuthenticated = true;
212
+ console.log(`Service authenticated via HMAC: ${service}`);
213
+ }
214
+ catch (error) {
215
+ return reply.code(401).send({
216
+ error: "HMAC authentication failed",
217
+ code: "HMAC_AUTH_FAILED",
218
+ message: error instanceof Error ? error.message : "Unknown error",
219
+ });
220
+ }
221
+ };
222
+ }
223
+ }
224
+ exports.ServiceAuthenticator = ServiceAuthenticator;
225
+ // Singleton factory
226
+ let serviceAuthInstance = null;
227
+ function createServiceAuthenticator(config) {
228
+ if (!serviceAuthInstance) {
229
+ serviceAuthInstance = new ServiceAuthenticator(config);
230
+ }
231
+ return serviceAuthInstance;
232
+ }
233
+ function getServiceAuthenticator() {
234
+ if (!serviceAuthInstance) {
235
+ throw new Error("Service authenticator not created. Call createServiceAuthenticator() first.");
236
+ }
237
+ return serviceAuthInstance;
238
+ }
239
+ // Utility to get auth config from environment
240
+ function getServiceAuthConfigFromEnv() {
241
+ const internalSecret = process.env.INTERNAL_SECRET;
242
+ if (!internalSecret) {
243
+ throw new Error("INTERNAL_SECRET environment variable is required");
244
+ }
245
+ return {
246
+ internalSecret,
247
+ tokenExpiry: process.env.SERVICE_TOKEN_EXPIRY || "1h",
248
+ };
249
+ }
250
+ // Common service permissions
251
+ exports.SERVICE_PERMISSIONS = {
252
+ // Store service permissions
253
+ STORE_READ: "store:read",
254
+ STORE_WRITE: "store:write",
255
+ STORE_DELETE: "store:delete",
256
+ // Compliance service permissions
257
+ COMPLIANCE_READ: "compliance:read",
258
+ COMPLIANCE_WRITE: "compliance:write",
259
+ COMPLIANCE_BAN: "compliance:ban",
260
+ COMPLIANCE_APPEAL: "compliance:appeal",
261
+ // Notification service permissions
262
+ NOTIFICATION_SEND: "notification:send",
263
+ NOTIFICATION_READ: "notification:read",
264
+ // Audit service permissions
265
+ AUDIT_READ: "audit:read",
266
+ AUDIT_WRITE: "audit:write",
267
+ // Admin service permissions
268
+ ADMIN_OVERRIDE: "admin:override",
269
+ ADMIN_REVIEW: "admin:review",
270
+ // AI moderation permissions
271
+ AI_MODERATE: "ai:moderate",
272
+ };
@@ -0,0 +1,15 @@
1
+ import type { FastifyReply, FastifyRequest } from "fastify";
2
+ export interface StoreOwnerMiddlewareOptions {
3
+ requireActive?: boolean;
4
+ allowParameterStoreId?: boolean;
5
+ storeIdParamName?: string;
6
+ allowAdminOverride?: boolean;
7
+ overrideRoles?: string[];
8
+ overridePermissions?: string[];
9
+ }
10
+ /**
11
+ * Factory that returns a middleware ensuring the current user owns the target store.
12
+ * - Uses shared store validation under the hood for existence/active checks
13
+ * - Supports optional admin/permission overrides (e.g. SUPER_ADMIN or * permissions)
14
+ */
15
+ export declare function requireStoreOwner(options?: StoreOwnerMiddlewareOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;