@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,248 +1,248 @@
|
|
|
1
|
-
import { createAuthMiddleware } from "better-middleware";
|
|
2
|
-
import type { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
|
|
3
|
-
import fp from "fastify-plugin";
|
|
4
|
-
|
|
5
|
-
// Standardized user context aligned with existing request augmentation in this package
|
|
6
|
-
export interface SharedUserContext {
|
|
7
|
-
userId: string;
|
|
8
|
-
storeId: string;
|
|
9
|
-
role: string;
|
|
10
|
-
permissions: string[];
|
|
11
|
-
organizationId?: string;
|
|
12
|
-
activeOrganizationId?: string;
|
|
13
|
-
iat?: number;
|
|
14
|
-
exp?: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Optional role → permission mapping (used for helpers)
|
|
18
|
-
export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
|
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
|
-
|
|
41
|
-
// Core adapter of better-middleware to Fastify
|
|
42
|
-
function createFastifyAuthHandler() {
|
|
43
|
-
const auth = createAuthMiddleware<FastifyRequest>({
|
|
44
|
-
baseURL: process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:4000",
|
|
45
|
-
cache: { enabled: true, ttl: 300, max: 1000 },
|
|
46
|
-
onError: async () => ({
|
|
47
|
-
status: 401,
|
|
48
|
-
body: {
|
|
49
|
-
success: false,
|
|
50
|
-
error: "Authentication required",
|
|
51
|
-
code: "UNAUTHORIZED",
|
|
52
|
-
},
|
|
53
|
-
}),
|
|
54
|
-
logger: {
|
|
55
|
-
info: (m, d) =>
|
|
56
|
-
process.env.NODE_ENV === "development"
|
|
57
|
-
? console.log(`[Auth] ${m}`, d)
|
|
58
|
-
: undefined,
|
|
59
|
-
error: (m, d) => console.error(`[Auth] ${m}`, d),
|
|
60
|
-
debug: (m, d) =>
|
|
61
|
-
process.env.NODE_ENV === "development"
|
|
62
|
-
? console.debug(`[Auth] ${m}`, d)
|
|
63
|
-
: undefined,
|
|
64
|
-
},
|
|
65
|
-
framework: {
|
|
66
|
-
getHeaders: (request: any) => {
|
|
67
|
-
const headers: Record<string, string> = {};
|
|
68
|
-
Object.entries(request.headers || {}).forEach(([k, v]) => {
|
|
69
|
-
if (typeof v === "string") headers[k] = v;
|
|
70
|
-
else if (Array.isArray(v)) headers[k] = v.join(", ");
|
|
71
|
-
});
|
|
72
|
-
const hasCookie = (headers["cookie"] || "").includes(
|
|
73
|
-
"better-auth.session_token=",
|
|
74
|
-
);
|
|
75
|
-
const bearer = headers["authorization"]?.startsWith("Bearer ")
|
|
76
|
-
? headers["authorization"].substring(7)
|
|
77
|
-
: undefined;
|
|
78
|
-
if (!hasCookie && bearer) {
|
|
79
|
-
headers["cookie"] =
|
|
80
|
-
`${headers["cookie"] ? headers["cookie"] + "; " : ""}better-auth.session_token=${bearer}`;
|
|
81
|
-
}
|
|
82
|
-
return headers;
|
|
83
|
-
},
|
|
84
|
-
getCookies: (request: any) => {
|
|
85
|
-
const cookies = request.cookies || {};
|
|
86
|
-
if (
|
|
87
|
-
!cookies["better-auth.session_token"] &&
|
|
88
|
-
request.headers.authorization?.startsWith("Bearer ")
|
|
89
|
-
) {
|
|
90
|
-
cookies["better-auth.session_token"] =
|
|
91
|
-
request.headers.authorization.substring(7);
|
|
92
|
-
}
|
|
93
|
-
return cookies;
|
|
94
|
-
},
|
|
95
|
-
setContext: (request: any, key: "user" | "session", value: any) => {
|
|
96
|
-
if (key === "user" && value) {
|
|
97
|
-
const u = value as any;
|
|
98
|
-
request.user = {
|
|
99
|
-
userId: u.id,
|
|
100
|
-
storeId: u.activeOrganizationId || u.organizationId || "",
|
|
101
|
-
role: u.role || "USER",
|
|
102
|
-
permissions: DEFAULT_ROLE_PERMISSIONS[u.role || "USER"] || [],
|
|
103
|
-
organizationId: u.organizationId,
|
|
104
|
-
activeOrganizationId: u.activeOrganizationId,
|
|
105
|
-
iat: u.iat,
|
|
106
|
-
exp: u.exp,
|
|
107
|
-
} satisfies SharedUserContext;
|
|
108
|
-
} else if (key === "session" && value) {
|
|
109
|
-
const s = value as any;
|
|
110
|
-
if (request.user && !request.user.storeId)
|
|
111
|
-
request.user.storeId =
|
|
112
|
-
s.activeOrganizationId || request.user.storeId;
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
createResponse: (_request: any, body: unknown, status: number) => ({
|
|
116
|
-
status,
|
|
117
|
-
body,
|
|
118
|
-
}),
|
|
119
|
-
},
|
|
120
|
-
});
|
|
121
|
-
return auth;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Pre-handler that runs authentication; emits response when not authorized
|
|
125
|
-
export const AuthPreHandler = async (
|
|
126
|
-
request: FastifyRequest,
|
|
127
|
-
reply: FastifyReply,
|
|
128
|
-
) => {
|
|
129
|
-
const auth = createFastifyAuthHandler();
|
|
130
|
-
const result = await auth(request, request, async () => {});
|
|
131
|
-
if (result) {
|
|
132
|
-
reply.code(result.status).send(result.body);
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
|
|
136
|
-
// Fastify plugin for easy registration
|
|
137
|
-
export const fastifyUserAuthPlugin: FastifyPluginAsync = fp(
|
|
138
|
-
async (fastify: any) => {
|
|
139
|
-
fastify.addHook("preHandler", AuthPreHandler as any);
|
|
140
|
-
},
|
|
141
|
-
);
|
|
142
|
-
|
|
143
|
-
// ---------- Helper utilities ----------
|
|
144
|
-
export function getAuthTokenFromRequest(
|
|
145
|
-
request: FastifyRequest,
|
|
146
|
-
): string | undefined {
|
|
147
|
-
const cookies = (request as any).cookies || {};
|
|
148
|
-
const tokenFromCookie = cookies["better-auth.session_token"] as
|
|
149
|
-
| string
|
|
150
|
-
| undefined;
|
|
151
|
-
if (tokenFromCookie) return tokenFromCookie;
|
|
152
|
-
const cookieHeader = request.headers.cookie;
|
|
153
|
-
if (cookieHeader) {
|
|
154
|
-
const match = cookieHeader.match(/better-auth\.session_token=([^;]+)/);
|
|
155
|
-
if (match) return match[1];
|
|
156
|
-
}
|
|
157
|
-
const authHeader = request.headers.authorization;
|
|
158
|
-
if (authHeader?.startsWith("Bearer ")) return authHeader.substring(7);
|
|
159
|
-
return undefined;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export function getAuthHeadersFromRequest(
|
|
163
|
-
request: FastifyRequest,
|
|
164
|
-
): Record<string, string> {
|
|
165
|
-
const headers: Record<string, string> = {
|
|
166
|
-
"Content-Type": "application/json",
|
|
167
|
-
};
|
|
168
|
-
const token = getAuthTokenFromRequest(request);
|
|
169
|
-
if (token) {
|
|
170
|
-
headers["Cookie"] = `better-auth.session_token=${token}`;
|
|
171
|
-
headers["Authorization"] = `Bearer ${token}`;
|
|
172
|
-
}
|
|
173
|
-
return headers;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export function getUserDataFromRequest(request: FastifyRequest): {
|
|
177
|
-
userId: string;
|
|
178
|
-
organizationId?: string;
|
|
179
|
-
role?: string;
|
|
180
|
-
permissions?: string[];
|
|
181
|
-
} | null {
|
|
182
|
-
const user = (request as any).user as SharedUserContext | undefined;
|
|
183
|
-
if (user) {
|
|
184
|
-
return {
|
|
185
|
-
userId: user.userId,
|
|
186
|
-
organizationId: user.storeId || user.organizationId,
|
|
187
|
-
role: user.role,
|
|
188
|
-
permissions: user.permissions,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const userIdFromHeader = request.headers["x-user-id"] as string | undefined;
|
|
193
|
-
const storeIdFromHeader = request.headers["x-store-id"] as string | undefined;
|
|
194
|
-
const roleFromHeader = request.headers["x-user-role"] as string | undefined;
|
|
195
|
-
if (userIdFromHeader) {
|
|
196
|
-
return {
|
|
197
|
-
userId: userIdFromHeader,
|
|
198
|
-
organizationId: storeIdFromHeader,
|
|
199
|
-
role: roleFromHeader || "USER",
|
|
200
|
-
permissions: DEFAULT_ROLE_PERMISSIONS[roleFromHeader || "USER"] || [],
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export function getUserIdFromRequest(request: FastifyRequest): string | null {
|
|
207
|
-
return getUserDataFromRequest(request)?.userId || null;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function hasPermission(
|
|
211
|
-
request: FastifyRequest,
|
|
212
|
-
permission: string,
|
|
213
|
-
): boolean {
|
|
214
|
-
const user = (request as any).user as SharedUserContext | undefined;
|
|
215
|
-
const perms = user?.permissions || [];
|
|
216
|
-
return perms.includes("*") || perms.includes(permission);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export function hasRole(request: FastifyRequest, role: string): boolean {
|
|
220
|
-
const user = (request as any).user as SharedUserContext | undefined;
|
|
221
|
-
return (user?.role || "").toUpperCase() === role.toUpperCase();
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
export function requirePermission(permission: string) {
|
|
225
|
-
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
226
|
-
if (!hasPermission(request, permission)) {
|
|
227
|
-
reply.code(403).send({
|
|
228
|
-
success: false,
|
|
229
|
-
error: "Insufficient permissions",
|
|
230
|
-
message: `Required permission: ${permission}`,
|
|
231
|
-
code: "FORBIDDEN",
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
export function requireRole(role: string) {
|
|
238
|
-
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
239
|
-
if (!hasRole(request, role)) {
|
|
240
|
-
reply.code(403).send({
|
|
241
|
-
success: false,
|
|
242
|
-
error: "Insufficient role",
|
|
243
|
-
message: `Required role: ${role}`,
|
|
244
|
-
code: "FORBIDDEN",
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
};
|
|
248
|
-
}
|
|
1
|
+
import { createAuthMiddleware } from "better-middleware";
|
|
2
|
+
import type { FastifyPluginAsync, FastifyReply, FastifyRequest } from "fastify";
|
|
3
|
+
import fp from "fastify-plugin";
|
|
4
|
+
|
|
5
|
+
// Standardized user context aligned with existing request augmentation in this package
|
|
6
|
+
export interface SharedUserContext {
|
|
7
|
+
userId: string;
|
|
8
|
+
storeId: string;
|
|
9
|
+
role: string;
|
|
10
|
+
permissions: string[];
|
|
11
|
+
organizationId?: string;
|
|
12
|
+
activeOrganizationId?: string;
|
|
13
|
+
iat?: number;
|
|
14
|
+
exp?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Optional role → permission mapping (used for helpers)
|
|
18
|
+
export const DEFAULT_ROLE_PERMISSIONS: Record<string, string[]> = {
|
|
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
|
+
|
|
41
|
+
// Core adapter of better-middleware to Fastify
|
|
42
|
+
function createFastifyAuthHandler() {
|
|
43
|
+
const auth = createAuthMiddleware<FastifyRequest>({
|
|
44
|
+
baseURL: process.env.NEXT_PUBLIC_AUTH_URL || "http://localhost:4000",
|
|
45
|
+
cache: { enabled: true, ttl: 300, max: 1000 },
|
|
46
|
+
onError: async () => ({
|
|
47
|
+
status: 401,
|
|
48
|
+
body: {
|
|
49
|
+
success: false,
|
|
50
|
+
error: "Authentication required",
|
|
51
|
+
code: "UNAUTHORIZED",
|
|
52
|
+
},
|
|
53
|
+
}),
|
|
54
|
+
logger: {
|
|
55
|
+
info: (m, d) =>
|
|
56
|
+
process.env.NODE_ENV === "development"
|
|
57
|
+
? console.log(`[Auth] ${m}`, d)
|
|
58
|
+
: undefined,
|
|
59
|
+
error: (m, d) => console.error(`[Auth] ${m}`, d),
|
|
60
|
+
debug: (m, d) =>
|
|
61
|
+
process.env.NODE_ENV === "development"
|
|
62
|
+
? console.debug(`[Auth] ${m}`, d)
|
|
63
|
+
: undefined,
|
|
64
|
+
},
|
|
65
|
+
framework: {
|
|
66
|
+
getHeaders: (request: any) => {
|
|
67
|
+
const headers: Record<string, string> = {};
|
|
68
|
+
Object.entries(request.headers || {}).forEach(([k, v]) => {
|
|
69
|
+
if (typeof v === "string") headers[k] = v;
|
|
70
|
+
else if (Array.isArray(v)) headers[k] = v.join(", ");
|
|
71
|
+
});
|
|
72
|
+
const hasCookie = (headers["cookie"] || "").includes(
|
|
73
|
+
"better-auth.session_token=",
|
|
74
|
+
);
|
|
75
|
+
const bearer = headers["authorization"]?.startsWith("Bearer ")
|
|
76
|
+
? headers["authorization"].substring(7)
|
|
77
|
+
: undefined;
|
|
78
|
+
if (!hasCookie && bearer) {
|
|
79
|
+
headers["cookie"] =
|
|
80
|
+
`${headers["cookie"] ? headers["cookie"] + "; " : ""}better-auth.session_token=${bearer}`;
|
|
81
|
+
}
|
|
82
|
+
return headers;
|
|
83
|
+
},
|
|
84
|
+
getCookies: (request: any) => {
|
|
85
|
+
const cookies = request.cookies || {};
|
|
86
|
+
if (
|
|
87
|
+
!cookies["better-auth.session_token"] &&
|
|
88
|
+
request.headers.authorization?.startsWith("Bearer ")
|
|
89
|
+
) {
|
|
90
|
+
cookies["better-auth.session_token"] =
|
|
91
|
+
request.headers.authorization.substring(7);
|
|
92
|
+
}
|
|
93
|
+
return cookies;
|
|
94
|
+
},
|
|
95
|
+
setContext: (request: any, key: "user" | "session", value: any) => {
|
|
96
|
+
if (key === "user" && value) {
|
|
97
|
+
const u = value as any;
|
|
98
|
+
request.user = {
|
|
99
|
+
userId: u.id,
|
|
100
|
+
storeId: u.activeOrganizationId || u.organizationId || "",
|
|
101
|
+
role: u.role || "USER",
|
|
102
|
+
permissions: DEFAULT_ROLE_PERMISSIONS[u.role || "USER"] || [],
|
|
103
|
+
organizationId: u.organizationId,
|
|
104
|
+
activeOrganizationId: u.activeOrganizationId,
|
|
105
|
+
iat: u.iat,
|
|
106
|
+
exp: u.exp,
|
|
107
|
+
} satisfies SharedUserContext;
|
|
108
|
+
} else if (key === "session" && value) {
|
|
109
|
+
const s = value as any;
|
|
110
|
+
if (request.user && !request.user.storeId)
|
|
111
|
+
request.user.storeId =
|
|
112
|
+
s.activeOrganizationId || request.user.storeId;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
createResponse: (_request: any, body: unknown, status: number) => ({
|
|
116
|
+
status,
|
|
117
|
+
body,
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
return auth;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Pre-handler that runs authentication; emits response when not authorized
|
|
125
|
+
export const AuthPreHandler = async (
|
|
126
|
+
request: FastifyRequest,
|
|
127
|
+
reply: FastifyReply,
|
|
128
|
+
) => {
|
|
129
|
+
const auth = createFastifyAuthHandler();
|
|
130
|
+
const result = await auth(request, request, async () => {});
|
|
131
|
+
if (result) {
|
|
132
|
+
reply.code(result.status).send(result.body);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// Fastify plugin for easy registration
|
|
137
|
+
export const fastifyUserAuthPlugin: FastifyPluginAsync = fp(
|
|
138
|
+
async (fastify: any) => {
|
|
139
|
+
fastify.addHook("preHandler", AuthPreHandler as any);
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// ---------- Helper utilities ----------
|
|
144
|
+
export function getAuthTokenFromRequest(
|
|
145
|
+
request: FastifyRequest,
|
|
146
|
+
): string | undefined {
|
|
147
|
+
const cookies = (request as any).cookies || {};
|
|
148
|
+
const tokenFromCookie = cookies["better-auth.session_token"] as
|
|
149
|
+
| string
|
|
150
|
+
| undefined;
|
|
151
|
+
if (tokenFromCookie) return tokenFromCookie;
|
|
152
|
+
const cookieHeader = request.headers.cookie;
|
|
153
|
+
if (cookieHeader) {
|
|
154
|
+
const match = cookieHeader.match(/better-auth\.session_token=([^;]+)/);
|
|
155
|
+
if (match) return match[1];
|
|
156
|
+
}
|
|
157
|
+
const authHeader = request.headers.authorization;
|
|
158
|
+
if (authHeader?.startsWith("Bearer ")) return authHeader.substring(7);
|
|
159
|
+
return undefined;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function getAuthHeadersFromRequest(
|
|
163
|
+
request: FastifyRequest,
|
|
164
|
+
): Record<string, string> {
|
|
165
|
+
const headers: Record<string, string> = {
|
|
166
|
+
"Content-Type": "application/json",
|
|
167
|
+
};
|
|
168
|
+
const token = getAuthTokenFromRequest(request);
|
|
169
|
+
if (token) {
|
|
170
|
+
headers["Cookie"] = `better-auth.session_token=${token}`;
|
|
171
|
+
headers["Authorization"] = `Bearer ${token}`;
|
|
172
|
+
}
|
|
173
|
+
return headers;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function getUserDataFromRequest(request: FastifyRequest): {
|
|
177
|
+
userId: string;
|
|
178
|
+
organizationId?: string;
|
|
179
|
+
role?: string;
|
|
180
|
+
permissions?: string[];
|
|
181
|
+
} | null {
|
|
182
|
+
const user = (request as any).user as SharedUserContext | undefined;
|
|
183
|
+
if (user) {
|
|
184
|
+
return {
|
|
185
|
+
userId: user.userId,
|
|
186
|
+
organizationId: user.storeId || user.organizationId,
|
|
187
|
+
role: user.role,
|
|
188
|
+
permissions: user.permissions,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const userIdFromHeader = request.headers["x-user-id"] as string | undefined;
|
|
193
|
+
const storeIdFromHeader = request.headers["x-store-id"] as string | undefined;
|
|
194
|
+
const roleFromHeader = request.headers["x-user-role"] as string | undefined;
|
|
195
|
+
if (userIdFromHeader) {
|
|
196
|
+
return {
|
|
197
|
+
userId: userIdFromHeader,
|
|
198
|
+
organizationId: storeIdFromHeader,
|
|
199
|
+
role: roleFromHeader || "USER",
|
|
200
|
+
permissions: DEFAULT_ROLE_PERMISSIONS[roleFromHeader || "USER"] || [],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function getUserIdFromRequest(request: FastifyRequest): string | null {
|
|
207
|
+
return getUserDataFromRequest(request)?.userId || null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export function hasPermission(
|
|
211
|
+
request: FastifyRequest,
|
|
212
|
+
permission: string,
|
|
213
|
+
): boolean {
|
|
214
|
+
const user = (request as any).user as SharedUserContext | undefined;
|
|
215
|
+
const perms = user?.permissions || [];
|
|
216
|
+
return perms.includes("*") || perms.includes(permission);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function hasRole(request: FastifyRequest, role: string): boolean {
|
|
220
|
+
const user = (request as any).user as SharedUserContext | undefined;
|
|
221
|
+
return (user?.role || "").toUpperCase() === role.toUpperCase();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function requirePermission(permission: string) {
|
|
225
|
+
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
226
|
+
if (!hasPermission(request, permission)) {
|
|
227
|
+
reply.code(403).send({
|
|
228
|
+
success: false,
|
|
229
|
+
error: "Insufficient permissions",
|
|
230
|
+
message: `Required permission: ${permission}`,
|
|
231
|
+
code: "FORBIDDEN",
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export function requireRole(role: string) {
|
|
238
|
+
return async (request: FastifyRequest, reply: FastifyReply) => {
|
|
239
|
+
if (!hasRole(request, role)) {
|
|
240
|
+
reply.code(403).send({
|
|
241
|
+
success: false,
|
|
242
|
+
error: "Insufficient role",
|
|
243
|
+
message: `Required role: ${role}`,
|
|
244
|
+
code: "FORBIDDEN",
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|