@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,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireStoreOwner = requireStoreOwner;
4
+ const drizzle_orm_1 = require("drizzle-orm");
5
+ const db_1 = require("../lib/db");
6
+ const store_schema_1 = require("../schemas/store/store-schema");
7
+ const serviceAuth_1 = require("./serviceAuth");
8
+ /**
9
+ * Factory that returns a middleware ensuring the current user owns the target store.
10
+ * - Uses shared store validation under the hood for existence/active checks
11
+ * - Supports optional admin/permission overrides (e.g. SUPER_ADMIN or * permissions)
12
+ */
13
+ function requireStoreOwner(options = {}) {
14
+ const { requireActive = true, allowParameterStoreId = true, storeIdParamName = "storeId", allowAdminOverride = true, overrideRoles = ["SUPER_ADMIN"], overridePermissions = ["*", "store:read:all", "store:write:all"], } = options;
15
+ return async (request, reply) => {
16
+ const user = request.user;
17
+ // Extract storeId from params/query/body/user.context
18
+ let storeId;
19
+ const params = (request.params || {});
20
+ const query = (request.query || {});
21
+ const body = (request.body || {});
22
+ if (allowParameterStoreId && typeof params[storeIdParamName] === "string") {
23
+ storeId = params[storeIdParamName];
24
+ }
25
+ if (!storeId && typeof query.storeId === "string")
26
+ storeId = query.storeId;
27
+ if (!storeId && typeof body.storeId === "string")
28
+ storeId = body.storeId;
29
+ if (!storeId && typeof request.user?.storeId === "string") {
30
+ storeId = request.user?.storeId;
31
+ }
32
+ if (!storeId) {
33
+ return reply.status(400).send({
34
+ error: "Store ID Required",
35
+ message: `Store ID must be provided in URL (:${storeIdParamName}), query, or body`,
36
+ code: "STORE_ID_MISSING",
37
+ });
38
+ }
39
+ // Admin/permission overrides
40
+ const hasRoleOverride = !!user?.role && overrideRoles.includes(user.role);
41
+ const hasPermissionOverride = !!user?.permissions?.some((p) => overridePermissions.includes(p));
42
+ if (allowAdminOverride && (hasRoleOverride || hasPermissionOverride)) {
43
+ // Optionally attach minimal store info by fetching; if it fails, still allow
44
+ await attachStoreInfoIfPossible(request, storeId);
45
+ return;
46
+ }
47
+ // Fetch store details from Store Service internal route
48
+ try {
49
+ const store = await fetchStoreById(storeId);
50
+ if (!store) {
51
+ return reply.status(404).send({
52
+ error: "Store Not Found",
53
+ message: `Store with ID ${storeId} does not exist`,
54
+ code: "STORE_NOT_FOUND",
55
+ });
56
+ }
57
+ // requireActive check
58
+ if (requireActive && store.isActive === false) {
59
+ return reply.status(403).send({
60
+ error: "Store Inactive",
61
+ message: `Store ${storeId} is currently inactive`,
62
+ code: "STORE_INACTIVE",
63
+ });
64
+ }
65
+ // Ownership check
66
+ if (!user?.userId || store.userId !== user.userId) {
67
+ return reply.status(403).send({
68
+ error: "Store Access Denied",
69
+ message: "You do not have permission to access this store",
70
+ code: "STORE_ACCESS_DENIED",
71
+ });
72
+ }
73
+ // Attach store info to request for downstream handlers
74
+ request.storeInfo = {
75
+ storeId: store.id,
76
+ storeName: store.storeName,
77
+ isActive: !!store.isActive,
78
+ userId: store.userId,
79
+ };
80
+ }
81
+ catch (error) {
82
+ console.error("Store ownership validation error:", error);
83
+ return reply.status(500).send({
84
+ error: "Internal Server Error",
85
+ message: "Failed to validate store ownership",
86
+ code: "STORE_OWNERSHIP_VALIDATION_ERROR",
87
+ });
88
+ }
89
+ };
90
+ }
91
+ // Fetches store by ID from the Store Service internal endpoint
92
+ async function fetchStoreById(storeId) {
93
+ const baseUrl = process.env.STORE_SERVICE_URL || "http://localhost:3001";
94
+ const url = `${baseUrl}/internal/stores/${encodeURIComponent(storeId)}`;
95
+ let token;
96
+ try {
97
+ const cfg = (0, serviceAuth_1.getServiceAuthConfigFromEnv)();
98
+ token = (0, serviceAuth_1.createServiceAuthenticator)(cfg).generateServiceToken(process.env.SERVICE_NAME || "inventory-core-service", ["store:read"]);
99
+ }
100
+ catch {
101
+ // Development fallback tokens accepted by store service
102
+ const isDev = process.env.NODE_ENV !== "production";
103
+ if (isDev)
104
+ token = "inventory-dev-access";
105
+ }
106
+ const headers = {
107
+ "Content-Type": "application/json",
108
+ };
109
+ if (token)
110
+ headers["x-service-token"] = token;
111
+ try {
112
+ const res = await fetch(url, { method: "GET", headers });
113
+ if (res.ok) {
114
+ const data = (await res.json());
115
+ const store = data?.store || data?.data?.store || null;
116
+ if (store)
117
+ return store;
118
+ }
119
+ }
120
+ catch {
121
+ // ignore network error and try DB fallback when possible
122
+ }
123
+ // DB fallback (primarily for in-process store-service)
124
+ try {
125
+ const result = await db_1.db
126
+ .select({
127
+ id: store_schema_1.stores.id,
128
+ storeName: store_schema_1.stores.storeName,
129
+ isActive: store_schema_1.stores.isActive,
130
+ userId: store_schema_1.stores.userId,
131
+ })
132
+ .from(store_schema_1.stores)
133
+ .where((0, drizzle_orm_1.eq)(store_schema_1.stores.id, storeId))
134
+ .limit(1);
135
+ return result[0] || null;
136
+ }
137
+ catch {
138
+ return null;
139
+ }
140
+ }
141
+ async function attachStoreInfoIfPossible(request, storeId) {
142
+ try {
143
+ const store = await fetchStoreById(storeId);
144
+ if (store) {
145
+ request.storeInfo = {
146
+ storeId: store.id,
147
+ storeName: store.storeName,
148
+ isActive: !!store.isActive,
149
+ userId: store.userId,
150
+ };
151
+ }
152
+ }
153
+ catch {
154
+ // ignore
155
+ }
156
+ }
@@ -0,0 +1,44 @@
1
+ import { FastifyReply, FastifyRequest } from "fastify";
2
+ export interface StoreValidationOptions {
3
+ requireActive?: boolean;
4
+ allowParameterStoreId?: boolean;
5
+ storeIdParamName?: string;
6
+ requireOwnership?: boolean;
7
+ }
8
+ declare module "fastify" {
9
+ interface FastifyRequest {
10
+ user?: {
11
+ userId: string;
12
+ storeId: string;
13
+ role: string;
14
+ permissions: string[];
15
+ iat?: number;
16
+ exp?: number;
17
+ };
18
+ session?: unknown;
19
+ storeInfo?: {
20
+ storeId: string;
21
+ storeName: string;
22
+ isActive: boolean;
23
+ userId: string;
24
+ };
25
+ }
26
+ }
27
+ /**
28
+ * Middleware to validate store existence and status
29
+ */
30
+ export declare const validateStoreMiddleware: (options?: StoreValidationOptions) => (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
31
+ /**
32
+ * Helper to clear store cache entry
33
+ */
34
+ export declare const clearStoreCache: (storeId: string) => void;
35
+ /**
36
+ * Helper to clear all store cache
37
+ */
38
+ export declare const clearAllStoreCache: () => void;
39
+ /**
40
+ * Simplified middleware for common use cases
41
+ */
42
+ export declare const requireValidStore: (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
43
+ export declare const requireOwnedStore: (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
44
+ export declare const requireAnyStore: (request: FastifyRequest, reply: FastifyReply) => Promise<undefined>;
@@ -0,0 +1,180 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.requireAnyStore = exports.requireOwnedStore = exports.requireValidStore = exports.clearAllStoreCache = exports.clearStoreCache = exports.validateStoreMiddleware = void 0;
4
+ const drizzle_orm_1 = require("drizzle-orm");
5
+ const db_1 = require("../lib/db");
6
+ const store_schema_1 = require("../schemas/store/store-schema");
7
+ // Store cache to reduce database calls
8
+ const storeCache = new Map();
9
+ const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
10
+ /**
11
+ * Middleware to validate store existence and status
12
+ */
13
+ const validateStoreMiddleware = (options = {}) => {
14
+ return async (request, reply) => {
15
+ try {
16
+ const { requireActive = true, allowParameterStoreId = true, storeIdParamName = "storeId", requireOwnership = false, } = options;
17
+ // Extract store ID from various sources
18
+ let storeId;
19
+ // 1. Try from authenticated user context
20
+ if (request.user?.storeId) {
21
+ storeId = request.user.storeId;
22
+ }
23
+ // 2. Try from URL parameters if allowed
24
+ if (!storeId && allowParameterStoreId) {
25
+ const params = request.params;
26
+ storeId = params[storeIdParamName];
27
+ }
28
+ // 3. Try from query parameters
29
+ if (!storeId) {
30
+ const query = request.query;
31
+ storeId = query.storeId;
32
+ }
33
+ // 4. Try from request body
34
+ if (!storeId && request.body && typeof request.body === "object") {
35
+ const body = request.body;
36
+ if (typeof body.storeId === "string") {
37
+ storeId = body.storeId;
38
+ }
39
+ }
40
+ if (!storeId) {
41
+ return reply.status(400).send({
42
+ error: "Store ID Required",
43
+ message: "Store ID must be provided in URL, query parameters, or request body",
44
+ code: "STORE_ID_MISSING",
45
+ });
46
+ }
47
+ // Check cache first
48
+ const cached = storeCache.get(storeId);
49
+ if (cached && Date.now() - cached.timestamp < CACHE_TTL) {
50
+ if (!cached.isValid) {
51
+ return reply.status(404).send({
52
+ error: "Store Not Found",
53
+ message: `Store with ID ${storeId} does not exist`,
54
+ code: "STORE_NOT_FOUND",
55
+ });
56
+ }
57
+ // Use cached store data if available
58
+ if (cached.storeData) {
59
+ const storeData = cached.storeData;
60
+ // Check if store is active when required
61
+ if (requireActive && !storeData.isActive) {
62
+ return reply.status(403).send({
63
+ error: "Store Inactive",
64
+ message: `Store ${storeId} is currently inactive`,
65
+ code: "STORE_INACTIVE",
66
+ });
67
+ }
68
+ // Check ownership when required
69
+ if (requireOwnership && request.user?.userId !== storeData.userId) {
70
+ return reply.status(403).send({
71
+ error: "Store Access Denied",
72
+ message: "You do not have permission to access this store",
73
+ code: "STORE_ACCESS_DENIED",
74
+ });
75
+ }
76
+ // Attach store information to request
77
+ request.storeInfo = {
78
+ storeId: storeData.id,
79
+ storeName: storeData.storeName,
80
+ isActive: storeData.isActive,
81
+ userId: storeData.userId,
82
+ };
83
+ return; // Successfully validated using cache
84
+ }
85
+ }
86
+ // Validate store exists and get details from database
87
+ const store = await db_1.db
88
+ .select({
89
+ id: store_schema_1.stores.id,
90
+ storeName: store_schema_1.stores.storeName,
91
+ isActive: store_schema_1.stores.isActive,
92
+ userId: store_schema_1.stores.userId,
93
+ })
94
+ .from(store_schema_1.stores)
95
+ .where((0, drizzle_orm_1.eq)(store_schema_1.stores.id, storeId))
96
+ .limit(1);
97
+ if (store.length === 0) {
98
+ // Cache negative result
99
+ storeCache.set(storeId, { isValid: false, timestamp: Date.now() });
100
+ return reply.status(404).send({
101
+ error: "Store Not Found",
102
+ message: `Store with ID ${storeId} does not exist`,
103
+ code: "STORE_NOT_FOUND",
104
+ });
105
+ }
106
+ const storeData = store[0];
107
+ // Check if store is active when required
108
+ if (requireActive && !storeData.isActive) {
109
+ return reply.status(403).send({
110
+ error: "Store Inactive",
111
+ message: `Store ${storeId} is currently inactive`,
112
+ code: "STORE_INACTIVE",
113
+ });
114
+ }
115
+ // Check ownership when required
116
+ if (requireOwnership && request.user?.userId !== storeData.userId) {
117
+ return reply.status(403).send({
118
+ error: "Store Access Denied",
119
+ message: "You do not have permission to access this store",
120
+ code: "STORE_ACCESS_DENIED",
121
+ });
122
+ }
123
+ // Cache positive result with store data
124
+ storeCache.set(storeId, {
125
+ isValid: true,
126
+ timestamp: Date.now(),
127
+ storeData: {
128
+ id: storeData.id,
129
+ storeName: storeData.storeName,
130
+ isActive: storeData.isActive,
131
+ userId: storeData.userId,
132
+ },
133
+ });
134
+ // Attach store information to request
135
+ request.storeInfo = {
136
+ storeId: storeData.id,
137
+ storeName: storeData.storeName,
138
+ isActive: storeData.isActive,
139
+ userId: storeData.userId,
140
+ };
141
+ // Continue to next handler - explicitly return to proceed
142
+ }
143
+ catch (error) {
144
+ console.error("Store validation error:", error);
145
+ return reply.status(500).send({
146
+ error: "Internal Server Error",
147
+ message: "Failed to validate store",
148
+ code: "STORE_VALIDATION_ERROR",
149
+ });
150
+ }
151
+ };
152
+ };
153
+ exports.validateStoreMiddleware = validateStoreMiddleware;
154
+ /**
155
+ * Helper to clear store cache entry
156
+ */
157
+ const clearStoreCache = (storeId) => {
158
+ storeCache.delete(storeId);
159
+ };
160
+ exports.clearStoreCache = clearStoreCache;
161
+ /**
162
+ * Helper to clear all store cache
163
+ */
164
+ const clearAllStoreCache = () => {
165
+ storeCache.clear();
166
+ };
167
+ exports.clearAllStoreCache = clearAllStoreCache;
168
+ /**
169
+ * Simplified middleware for common use cases
170
+ */
171
+ exports.requireValidStore = (0, exports.validateStoreMiddleware)({
172
+ requireActive: true,
173
+ });
174
+ exports.requireOwnedStore = (0, exports.validateStoreMiddleware)({
175
+ requireActive: true,
176
+ requireOwnership: true,
177
+ });
178
+ exports.requireAnyStore = (0, exports.validateStoreMiddleware)({
179
+ requireActive: false,
180
+ });
@@ -0,0 +1,27 @@
1
+ import type { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
2
+ export interface SharedUserContext {
3
+ userId: string;
4
+ storeId: string;
5
+ role: string;
6
+ permissions: string[];
7
+ organizationId?: string;
8
+ activeOrganizationId?: string;
9
+ iat?: number;
10
+ exp?: number;
11
+ }
12
+ export declare const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]>;
13
+ export declare const AuthPreHandler: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
14
+ export declare const fastifyUserAuthPlugin: FastifyPluginAsync;
15
+ export declare function getAuthTokenFromRequest(request: FastifyRequest): string | undefined;
16
+ export declare function getAuthHeadersFromRequest(request: FastifyRequest): Record<string, string>;
17
+ export declare function getUserDataFromRequest(request: FastifyRequest): {
18
+ userId: string;
19
+ organizationId?: string;
20
+ role?: string;
21
+ permissions?: string[];
22
+ } | null;
23
+ export declare function getUserIdFromRequest(request: FastifyRequest): string | null;
24
+ export declare function hasPermission(request: FastifyRequest, permission: string): boolean;
25
+ export declare function hasRole(request: FastifyRequest, role: string): boolean;
26
+ export declare function requirePermission(permission: string): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
27
+ export declare function requireRole(role: string): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
@@ -0,0 +1,218 @@
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.fastifyUserAuthPlugin = exports.AuthPreHandler = exports.DEFAULT_ROLE_PERMISSIONS = void 0;
7
+ exports.getAuthTokenFromRequest = getAuthTokenFromRequest;
8
+ exports.getAuthHeadersFromRequest = getAuthHeadersFromRequest;
9
+ exports.getUserDataFromRequest = getUserDataFromRequest;
10
+ exports.getUserIdFromRequest = getUserIdFromRequest;
11
+ exports.hasPermission = hasPermission;
12
+ exports.hasRole = hasRole;
13
+ exports.requirePermission = requirePermission;
14
+ exports.requireRole = requireRole;
15
+ const better_middleware_1 = require("better-middleware");
16
+ const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
17
+ // Optional role → permission mapping (used for helpers)
18
+ exports.DEFAULT_ROLE_PERMISSIONS = {
19
+ SUPER_ADMIN: ["*"],
20
+ STORE_ADMIN: [
21
+ "store:read",
22
+ "store:write",
23
+ "store:delete",
24
+ "orders:read",
25
+ "orders:write",
26
+ "products:read",
27
+ "products:write",
28
+ ],
29
+ STORE_MANAGER: [
30
+ "store:read",
31
+ "store:write",
32
+ "orders:read",
33
+ "orders:write",
34
+ "products:read",
35
+ "products:write",
36
+ ],
37
+ STORE_EMPLOYEE: ["store:read", "orders:read", "products:read"],
38
+ USER: ["store:read"],
39
+ };
40
+ // Core adapter of better-middleware to Fastify
41
+ function createFastifyAuthHandler() {
42
+ const auth = (0, better_middleware_1.createAuthMiddleware)({
43
+ baseURL: process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:4000",
44
+ cache: { enabled: true, ttl: 300, max: 1000 },
45
+ onError: async () => ({
46
+ status: 401,
47
+ body: {
48
+ success: false,
49
+ error: "Authentication required",
50
+ code: "UNAUTHORIZED",
51
+ },
52
+ }),
53
+ logger: {
54
+ info: (m, d) => process.env.NODE_ENV === "development"
55
+ ? console.log(`[Auth] ${m}`, d)
56
+ : undefined,
57
+ error: (m, d) => console.error(`[Auth] ${m}`, d),
58
+ debug: (m, d) => process.env.NODE_ENV === "development"
59
+ ? console.debug(`[Auth] ${m}`, d)
60
+ : undefined,
61
+ },
62
+ framework: {
63
+ getHeaders: (request) => {
64
+ const headers = {};
65
+ Object.entries(request.headers || {}).forEach(([k, v]) => {
66
+ if (typeof v === "string")
67
+ headers[k] = v;
68
+ else if (Array.isArray(v))
69
+ headers[k] = v.join(", ");
70
+ });
71
+ const hasCookie = (headers["cookie"] || "").includes("better-auth.session_token=");
72
+ const bearer = headers["authorization"]?.startsWith("Bearer ")
73
+ ? headers["authorization"].substring(7)
74
+ : undefined;
75
+ if (!hasCookie && bearer) {
76
+ headers["cookie"] =
77
+ `${headers["cookie"] ? headers["cookie"] + "; " : ""}better-auth.session_token=${bearer}`;
78
+ }
79
+ return headers;
80
+ },
81
+ getCookies: (request) => {
82
+ const cookies = request.cookies || {};
83
+ if (!cookies["better-auth.session_token"] &&
84
+ request.headers.authorization?.startsWith("Bearer ")) {
85
+ cookies["better-auth.session_token"] =
86
+ request.headers.authorization.substring(7);
87
+ }
88
+ return cookies;
89
+ },
90
+ setContext: (request, key, value) => {
91
+ if (key === "user" && value) {
92
+ const u = value;
93
+ request.user = {
94
+ userId: u.id,
95
+ storeId: u.activeOrganizationId || u.organizationId || "",
96
+ role: u.role || "USER",
97
+ permissions: exports.DEFAULT_ROLE_PERMISSIONS[u.role || "USER"] || [],
98
+ organizationId: u.organizationId,
99
+ activeOrganizationId: u.activeOrganizationId,
100
+ iat: u.iat,
101
+ exp: u.exp,
102
+ };
103
+ }
104
+ else if (key === "session" && value) {
105
+ const s = value;
106
+ if (request.user && !request.user.storeId)
107
+ request.user.storeId =
108
+ s.activeOrganizationId || request.user.storeId;
109
+ }
110
+ },
111
+ createResponse: (_request, body, status) => ({
112
+ status,
113
+ body,
114
+ }),
115
+ },
116
+ });
117
+ return auth;
118
+ }
119
+ // Pre-handler that runs authentication; emits response when not authorized
120
+ const AuthPreHandler = async (request, reply) => {
121
+ const auth = createFastifyAuthHandler();
122
+ const result = await auth(request, request, async () => { });
123
+ if (result) {
124
+ reply.code(result.status).send(result.body);
125
+ }
126
+ };
127
+ exports.AuthPreHandler = AuthPreHandler;
128
+ // Fastify plugin for easy registration
129
+ exports.fastifyUserAuthPlugin = (0, fastify_plugin_1.default)(async (fastify) => {
130
+ fastify.addHook("preHandler", exports.AuthPreHandler);
131
+ });
132
+ // ---------- Helper utilities ----------
133
+ function getAuthTokenFromRequest(request) {
134
+ const cookies = request.cookies || {};
135
+ const tokenFromCookie = cookies["better-auth.session_token"];
136
+ if (tokenFromCookie)
137
+ return tokenFromCookie;
138
+ const cookieHeader = request.headers.cookie;
139
+ if (cookieHeader) {
140
+ const match = cookieHeader.match(/better-auth\.session_token=([^;]+)/);
141
+ if (match)
142
+ return match[1];
143
+ }
144
+ const authHeader = request.headers.authorization;
145
+ if (authHeader?.startsWith("Bearer "))
146
+ return authHeader.substring(7);
147
+ return undefined;
148
+ }
149
+ function getAuthHeadersFromRequest(request) {
150
+ const headers = {
151
+ "Content-Type": "application/json",
152
+ };
153
+ const token = getAuthTokenFromRequest(request);
154
+ if (token) {
155
+ headers["Cookie"] = `better-auth.session_token=${token}`;
156
+ headers["Authorization"] = `Bearer ${token}`;
157
+ }
158
+ return headers;
159
+ }
160
+ function getUserDataFromRequest(request) {
161
+ const user = request.user;
162
+ if (user) {
163
+ return {
164
+ userId: user.userId,
165
+ organizationId: user.storeId || user.organizationId,
166
+ role: user.role,
167
+ permissions: user.permissions,
168
+ };
169
+ }
170
+ const userIdFromHeader = request.headers["x-user-id"];
171
+ const storeIdFromHeader = request.headers["x-store-id"];
172
+ const roleFromHeader = request.headers["x-user-role"];
173
+ if (userIdFromHeader) {
174
+ return {
175
+ userId: userIdFromHeader,
176
+ organizationId: storeIdFromHeader,
177
+ role: roleFromHeader || "USER",
178
+ permissions: exports.DEFAULT_ROLE_PERMISSIONS[roleFromHeader || "USER"] || [],
179
+ };
180
+ }
181
+ return null;
182
+ }
183
+ function getUserIdFromRequest(request) {
184
+ return getUserDataFromRequest(request)?.userId || null;
185
+ }
186
+ function hasPermission(request, permission) {
187
+ const user = request.user;
188
+ const perms = user?.permissions || [];
189
+ return perms.includes("*") || perms.includes(permission);
190
+ }
191
+ function hasRole(request, role) {
192
+ const user = request.user;
193
+ return (user?.role || "").toUpperCase() === role.toUpperCase();
194
+ }
195
+ function requirePermission(permission) {
196
+ return async (request, reply) => {
197
+ if (!hasPermission(request, permission)) {
198
+ reply.code(403).send({
199
+ success: false,
200
+ error: "Insufficient permissions",
201
+ message: `Required permission: ${permission}`,
202
+ code: "FORBIDDEN",
203
+ });
204
+ }
205
+ };
206
+ }
207
+ function requireRole(role) {
208
+ return async (request, reply) => {
209
+ if (!hasRole(request, role)) {
210
+ reply.code(403).send({
211
+ success: false,
212
+ error: "Insufficient role",
213
+ message: `Required role: ${role}`,
214
+ code: "FORBIDDEN",
215
+ });
216
+ }
217
+ };
218
+ }