@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.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/lib/db.d.ts +34406 -1
- package/dist/lib/db.js +21 -1
- package/dist/middleware/storeOwnership.js +22 -3
- package/dist/middleware/storeValidationMiddleware.js +16 -39
- package/dist/schemas/admin/admin-schema.d.ts +2 -2
- package/dist/schemas/ai-moderation/ai-moderation-schema.d.ts +6 -6
- package/dist/schemas/common/common-schemas.d.ts +71 -71
- package/dist/schemas/compliance/compliance-schema.d.ts +20 -20
- package/dist/schemas/compliance/kyc-schema.d.ts +8 -8
- package/dist/schemas/customer/customer-schema.d.ts +18 -18
- package/dist/schemas/index.d.ts +28 -0
- package/dist/schemas/index.js +134 -3
- package/dist/schemas/inventory/inventory-tables.d.ts +188 -188
- package/dist/schemas/inventory/lot-tables.d.ts +102 -102
- package/dist/schemas/order/cart-schema.d.ts +2865 -0
- package/dist/schemas/order/cart-schema.js +396 -0
- package/dist/schemas/order/order-schema.d.ts +19 -19
- package/dist/schemas/order/order-schema.js +8 -2
- package/dist/schemas/product/discount-schema.d.ts +3 -3
- package/dist/schemas/product/product-schema.d.ts +3 -3
- package/dist/schemas/store/store-audit-schema.d.ts +20 -20
- package/dist/schemas/store/store-schema.d.ts +182 -2
- package/dist/schemas/store/store-schema.js +19 -0
- package/dist/schemas/store/storefront-config-schema.d.ts +434 -823
- package/dist/schemas/store/storefront-config-schema.js +35 -62
- package/dist/utils/subdomain.d.ts +1 -1
- package/dist/utils/subdomain.js +10 -15
- package/package.json +1 -1
- package/src/configs/index.ts +654 -654
- package/src/index.ts +26 -23
- package/src/interfaces/customer-events.ts +106 -106
- package/src/interfaces/inventory-events.ts +545 -545
- package/src/interfaces/inventory-types.ts +1004 -1004
- package/src/interfaces/order-events.ts +381 -381
- package/src/lib/auditLogger.ts +1117 -1117
- package/src/lib/authOrganization.ts +153 -153
- package/src/lib/db.ts +84 -64
- package/src/middleware/serviceAuth.ts +328 -328
- package/src/middleware/storeOwnership.ts +199 -181
- package/src/middleware/storeValidationMiddleware.ts +17 -50
- package/src/middleware/userAuth.ts +248 -248
- package/src/schemas/admin/admin-schema.ts +208 -208
- package/src/schemas/ai-moderation/ai-moderation-schema.ts +180 -180
- package/src/schemas/common/common-schemas.ts +108 -108
- package/src/schemas/compliance/compliance-schema.ts +927 -0
- package/src/schemas/compliance/kyc-schema.ts +649 -0
- package/src/schemas/customer/customer-schema.ts +576 -0
- package/src/schemas/index.ts +202 -3
- package/src/schemas/inventory/inventory-tables.ts +1927 -0
- package/src/schemas/inventory/lot-tables.ts +799 -0
- package/src/schemas/order/cart-schema.ts +652 -0
- package/src/schemas/order/order-schema.ts +1406 -0
- package/src/schemas/product/discount-relations.ts +44 -0
- package/src/schemas/product/discount-schema.ts +464 -0
- package/src/schemas/product/product-relations.ts +187 -0
- package/src/schemas/product/product-schema.ts +955 -0
- package/src/schemas/store/ethiopian_business_api.md.resolved +212 -0
- package/src/schemas/store/store-audit-schema.ts +1257 -0
- package/src/schemas/store/store-schema.ts +682 -0
- package/src/schemas/store/store-settings-schema.ts +231 -0
- package/src/schemas/store/storefront-config-schema.ts +382 -0
- package/src/schemas/types.ts +67 -67
- package/src/types/events.ts +646 -646
- package/src/utils/errorHandler.ts +44 -44
- package/src/utils/subdomain.ts +19 -23
- package/tsconfig.json +21 -21
|
@@ -1,181 +1,199 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
if (
|
|
49
|
-
storeId =
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
2
|
+
import type { FastifyReply, FastifyRequest } from "fastify";
|
|
3
|
+
import { db } from "../lib/db";
|
|
4
|
+
import { stores } from "../schemas/store/store-schema";
|
|
5
|
+
import {
|
|
6
|
+
createServiceAuthenticator,
|
|
7
|
+
getServiceAuthConfigFromEnv,
|
|
8
|
+
} from "./serviceAuth";
|
|
9
|
+
|
|
10
|
+
export interface StoreOwnerMiddlewareOptions {
|
|
11
|
+
requireActive?: boolean;
|
|
12
|
+
allowParameterStoreId?: boolean;
|
|
13
|
+
storeIdParamName?: string;
|
|
14
|
+
allowAdminOverride?: boolean;
|
|
15
|
+
overrideRoles?: string[];
|
|
16
|
+
overridePermissions?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Factory that returns a middleware ensuring the current user owns the target store.
|
|
21
|
+
* - Uses shared store validation under the hood for existence/active checks
|
|
22
|
+
* - Supports optional admin/permission overrides (e.g. SUPER_ADMIN or * permissions)
|
|
23
|
+
*/
|
|
24
|
+
export function requireStoreOwner(options: StoreOwnerMiddlewareOptions = {}) {
|
|
25
|
+
const {
|
|
26
|
+
requireActive = true,
|
|
27
|
+
allowParameterStoreId = true,
|
|
28
|
+
storeIdParamName = "storeId",
|
|
29
|
+
allowAdminOverride = true,
|
|
30
|
+
overrideRoles = ["SUPER_ADMIN"],
|
|
31
|
+
overridePermissions = ["*", "store:read:all", "store:write:all"],
|
|
32
|
+
} = options;
|
|
33
|
+
|
|
34
|
+
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
35
|
+
const user = (request as any).user as
|
|
36
|
+
| {
|
|
37
|
+
userId: string;
|
|
38
|
+
role?: string;
|
|
39
|
+
permissions?: string[];
|
|
40
|
+
}
|
|
41
|
+
| undefined;
|
|
42
|
+
|
|
43
|
+
// Extract storeId from params/query/body/user.context
|
|
44
|
+
let storeId: string | undefined;
|
|
45
|
+
const params = (request.params || {}) as Record<string, any>;
|
|
46
|
+
const query = (request.query || {}) as Record<string, any>;
|
|
47
|
+
const body = (request.body || {}) as Record<string, any>;
|
|
48
|
+
if (allowParameterStoreId && typeof params[storeIdParamName] === "string") {
|
|
49
|
+
storeId = params[storeIdParamName] as string;
|
|
50
|
+
}
|
|
51
|
+
if (!storeId && typeof query.storeId === "string")
|
|
52
|
+
storeId = query.storeId as string;
|
|
53
|
+
if (!storeId && typeof body.storeId === "string")
|
|
54
|
+
storeId = body.storeId as string;
|
|
55
|
+
if (!storeId && typeof (request as any).user?.storeId === "string") {
|
|
56
|
+
storeId = (request as any).user?.storeId as string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (!storeId) {
|
|
60
|
+
return reply.status(400).send({
|
|
61
|
+
error: "Store ID Required",
|
|
62
|
+
message: `Store ID must be provided in URL (:${storeIdParamName}), query, or body`,
|
|
63
|
+
code: "STORE_ID_MISSING",
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Admin/permission overrides
|
|
68
|
+
const hasRoleOverride = !!user?.role && overrideRoles.includes(user.role);
|
|
69
|
+
const hasPermissionOverride = !!user?.permissions?.some((p) =>
|
|
70
|
+
overridePermissions.includes(p),
|
|
71
|
+
);
|
|
72
|
+
if (allowAdminOverride && (hasRoleOverride || hasPermissionOverride)) {
|
|
73
|
+
// Optionally attach minimal store info by fetching; if it fails, still allow
|
|
74
|
+
await attachStoreInfoIfPossible(request, storeId);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fetch store details from Store Service internal route
|
|
79
|
+
try {
|
|
80
|
+
const store = await fetchStoreById(storeId);
|
|
81
|
+
|
|
82
|
+
if (!store) {
|
|
83
|
+
return reply.status(404).send({
|
|
84
|
+
error: "Store Not Found",
|
|
85
|
+
message: `Store with ID ${storeId} does not exist`,
|
|
86
|
+
code: "STORE_NOT_FOUND",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// requireActive check
|
|
91
|
+
if (requireActive && store.isActive === false) {
|
|
92
|
+
return reply.status(403).send({
|
|
93
|
+
error: "Store Inactive",
|
|
94
|
+
message: `Store ${storeId} is currently inactive`,
|
|
95
|
+
code: "STORE_INACTIVE",
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Ownership check
|
|
100
|
+
if (!user?.userId || store.userId !== user.userId) {
|
|
101
|
+
return reply.status(403).send({
|
|
102
|
+
error: "Store Access Denied",
|
|
103
|
+
message: "You do not have permission to access this store",
|
|
104
|
+
code: "STORE_ACCESS_DENIED",
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Attach store info to request for downstream handlers
|
|
109
|
+
(request as any).storeInfo = {
|
|
110
|
+
storeId: store.id,
|
|
111
|
+
storeName: store.storeName,
|
|
112
|
+
isActive: !!store.isActive,
|
|
113
|
+
userId: store.userId,
|
|
114
|
+
};
|
|
115
|
+
} catch (error) {
|
|
116
|
+
console.error("Store ownership validation error:", error);
|
|
117
|
+
return reply.status(500).send({
|
|
118
|
+
error: "Internal Server Error",
|
|
119
|
+
message: "Failed to validate store ownership",
|
|
120
|
+
code: "STORE_OWNERSHIP_VALIDATION_ERROR",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Fetches store by ID from the Store Service internal endpoint
|
|
127
|
+
async function fetchStoreById(storeId: string): Promise<{
|
|
128
|
+
id: string;
|
|
129
|
+
storeName: string;
|
|
130
|
+
isActive?: boolean;
|
|
131
|
+
userId: string;
|
|
132
|
+
} | null> {
|
|
133
|
+
const baseUrl = process.env.STORE_SERVICE_URL || "http://localhost:3001";
|
|
134
|
+
const url = `${baseUrl}/internal/stores/${encodeURIComponent(storeId)}`;
|
|
135
|
+
|
|
136
|
+
let token: string | undefined;
|
|
137
|
+
try {
|
|
138
|
+
const cfg = getServiceAuthConfigFromEnv();
|
|
139
|
+
token = createServiceAuthenticator(cfg).generateServiceToken(
|
|
140
|
+
process.env.SERVICE_NAME || "inventory-core-service",
|
|
141
|
+
["store:read"],
|
|
142
|
+
);
|
|
143
|
+
} catch {
|
|
144
|
+
// Development fallback tokens accepted by store service
|
|
145
|
+
const isDev = process.env.NODE_ENV !== "production";
|
|
146
|
+
if (isDev) token = "inventory-dev-access";
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const headers: Record<string, string> = {
|
|
150
|
+
"Content-Type": "application/json",
|
|
151
|
+
};
|
|
152
|
+
if (token) headers["x-service-token"] = token;
|
|
153
|
+
|
|
154
|
+
try {
|
|
155
|
+
const res = await fetch(url, { method: "GET", headers });
|
|
156
|
+
if (res.ok) {
|
|
157
|
+
const data = (await res.json()) as any;
|
|
158
|
+
const store = data?.store || data?.data?.store || null;
|
|
159
|
+
if (store) return store;
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// ignore network error and try DB fallback when possible
|
|
163
|
+
}
|
|
164
|
+
// DB fallback (primarily for in-process store-service)
|
|
165
|
+
try {
|
|
166
|
+
const result = await db
|
|
167
|
+
.select({
|
|
168
|
+
id: stores.id,
|
|
169
|
+
storeName: stores.storeName,
|
|
170
|
+
isActive: stores.isActive,
|
|
171
|
+
userId: stores.userId,
|
|
172
|
+
})
|
|
173
|
+
.from(stores)
|
|
174
|
+
.where(eq(stores.id, storeId))
|
|
175
|
+
.limit(1);
|
|
176
|
+
return result[0] || null;
|
|
177
|
+
} catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function attachStoreInfoIfPossible(
|
|
183
|
+
request: FastifyRequest,
|
|
184
|
+
storeId: string,
|
|
185
|
+
) {
|
|
186
|
+
try {
|
|
187
|
+
const store = await fetchStoreById(storeId);
|
|
188
|
+
if (store) {
|
|
189
|
+
(request as any).storeInfo = {
|
|
190
|
+
storeId: store.id,
|
|
191
|
+
storeName: store.storeName,
|
|
192
|
+
isActive: !!store.isActive,
|
|
193
|
+
userId: store.userId,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
} catch {
|
|
197
|
+
// ignore
|
|
198
|
+
}
|
|
199
|
+
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
+
import { eq } from "drizzle-orm";
|
|
1
2
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
getServiceAuthConfigFromEnv,
|
|
5
|
-
} from "./serviceAuth";
|
|
3
|
+
import { db } from "../lib/db";
|
|
4
|
+
import { stores } from "../schemas/store/store-schema";
|
|
6
5
|
|
|
7
6
|
// Store cache to reduce database calls
|
|
8
7
|
const storeCache = new Map<
|
|
@@ -144,10 +143,19 @@ export const validateStoreMiddleware = (
|
|
|
144
143
|
}
|
|
145
144
|
}
|
|
146
145
|
|
|
147
|
-
// Validate store exists and get details from
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
// Validate store exists and get details from database
|
|
147
|
+
const store = await db
|
|
148
|
+
.select({
|
|
149
|
+
id: stores.id,
|
|
150
|
+
storeName: stores.storeName,
|
|
151
|
+
isActive: stores.isActive,
|
|
152
|
+
userId: stores.userId,
|
|
153
|
+
})
|
|
154
|
+
.from(stores)
|
|
155
|
+
.where(eq(stores.id, storeId))
|
|
156
|
+
.limit(1);
|
|
157
|
+
|
|
158
|
+
if (store.length === 0) {
|
|
151
159
|
// Cache negative result
|
|
152
160
|
storeCache.set(storeId, { isValid: false, timestamp: Date.now() });
|
|
153
161
|
|
|
@@ -158,7 +166,7 @@ export const validateStoreMiddleware = (
|
|
|
158
166
|
});
|
|
159
167
|
}
|
|
160
168
|
|
|
161
|
-
const storeData =
|
|
169
|
+
const storeData = store[0];
|
|
162
170
|
|
|
163
171
|
// Check if store is active when required
|
|
164
172
|
if (requireActive && !storeData.isActive) {
|
|
@@ -237,44 +245,3 @@ export const requireOwnedStore = validateStoreMiddleware({
|
|
|
237
245
|
export const requireAnyStore = validateStoreMiddleware({
|
|
238
246
|
requireActive: false,
|
|
239
247
|
});
|
|
240
|
-
|
|
241
|
-
// Helper function to fetch store from store service
|
|
242
|
-
async function fetchStoreById(storeId: string): Promise<{
|
|
243
|
-
id: string;
|
|
244
|
-
storeName: string;
|
|
245
|
-
isActive: boolean;
|
|
246
|
-
userId: string;
|
|
247
|
-
} | null> {
|
|
248
|
-
const baseUrl = process.env.STORE_SERVICE_URL || "http://localhost:3001";
|
|
249
|
-
const url = `${baseUrl}/internal/stores/${encodeURIComponent(storeId)}`;
|
|
250
|
-
|
|
251
|
-
let token: string | undefined;
|
|
252
|
-
try {
|
|
253
|
-
const cfg = getServiceAuthConfigFromEnv();
|
|
254
|
-
token = createServiceAuthenticator(cfg).generateServiceToken(
|
|
255
|
-
process.env.SERVICE_NAME || "shared-middleware",
|
|
256
|
-
["store:read"],
|
|
257
|
-
);
|
|
258
|
-
} catch {
|
|
259
|
-
// Development fallback tokens accepted by store service
|
|
260
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
261
|
-
if (isDev) token = "dev-access";
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
const headers: Record<string, string> = {
|
|
265
|
-
"Content-Type": "application/json",
|
|
266
|
-
};
|
|
267
|
-
if (token) headers["x-service-token"] = token;
|
|
268
|
-
|
|
269
|
-
try {
|
|
270
|
-
const res = await fetch(url, { method: "GET", headers });
|
|
271
|
-
if (res.ok) {
|
|
272
|
-
const data = (await res.json()) as any;
|
|
273
|
-
const store = data?.store || data?.data?.store || null;
|
|
274
|
-
if (store) return store;
|
|
275
|
-
}
|
|
276
|
-
} catch (error) {
|
|
277
|
-
console.error('Failed to fetch store from store service:', error);
|
|
278
|
-
}
|
|
279
|
-
return null;
|
|
280
|
-
}
|