@xenterprises/fastify-xconfig 0.0.10 → 1.0.1
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/.github/workflows/ci.yml +19 -0
- package/.taprc +3 -0
- package/README.md +18 -134
- package/index.d.ts +13 -0
- package/index.js +9 -0
- package/package.json +33 -39
- package/test/index.test-d.ts +13 -0
- package/test/index.test.js +14 -0
- package/test/xConfig.js +115 -0
- package/tsconfig.json +9 -0
- package/dist/integrations/cloudinary.d.ts +0 -1
- package/dist/integrations/cloudinary.js +0 -25
- package/dist/integrations/cloudinary.js.map +0 -1
- package/dist/integrations/prisma.d.ts +0 -1
- package/dist/integrations/prisma.js +0 -13
- package/dist/integrations/prisma.js.map +0 -1
- package/dist/integrations/sendgrid.d.ts +0 -1
- package/dist/integrations/sendgrid.js +0 -22
- package/dist/integrations/sendgrid.js.map +0 -1
- package/dist/integrations/stripe.d.ts +0 -1
- package/dist/integrations/stripe.js +0 -15
- package/dist/integrations/stripe.js.map +0 -1
- package/dist/integrations/twilio.d.ts +0 -1
- package/dist/integrations/twilio.js +0 -17
- package/dist/integrations/twilio.js.map +0 -1
- package/dist/middleware/bugsnag.d.ts +0 -2
- package/dist/middleware/bugsnag.js +0 -9
- package/dist/middleware/bugsnag.js.map +0 -1
- package/dist/middleware/cors.d.ts +0 -2
- package/dist/middleware/cors.js +0 -11
- package/dist/middleware/cors.js.map +0 -1
- package/dist/middleware/errorHandler.d.ts +0 -2
- package/dist/middleware/errorHandler.js +0 -19
- package/dist/middleware/errorHandler.js.map +0 -1
- package/dist/middleware/multipart.d.ts +0 -2
- package/dist/middleware/multipart.js +0 -7
- package/dist/middleware/multipart.js.map +0 -1
- package/dist/middleware/rateLimit.d.ts +0 -2
- package/dist/middleware/rateLimit.js +0 -7
- package/dist/middleware/rateLimit.js.map +0 -1
- package/dist/middleware/underPressure.d.ts +0 -2
- package/dist/middleware/underPressure.js +0 -7
- package/dist/middleware/underPressure.js.map +0 -1
- package/dist/utils/colorize.d.ts +0 -4
- package/dist/utils/colorize.js +0 -33
- package/dist/utils/colorize.js.map +0 -1
- package/dist/utils/formatBytes.d.ts +0 -1
- package/dist/utils/formatBytes.js +0 -10
- package/dist/utils/formatBytes.js.map +0 -1
- package/dist/utils/randomUUID.d.ts +0 -1
- package/dist/utils/randomUUID.js +0 -3
- package/dist/utils/randomUUID.js.map +0 -1
- package/dist/utils/statAsync.d.ts +0 -2
- package/dist/utils/statAsync.js +0 -4
- package/dist/utils/statAsync.js.map +0 -1
- package/dist/xConfig.d.ts +0 -3
- package/dist/xConfig.js +0 -9
- package/dist/xConfig.js.map +0 -1
- package/server/app.js +0 -92
- package/src/auth/admin.js +0 -241
- package/src/auth/portal.js +0 -286
- package/src/integrations/cloudinary.js +0 -98
- package/src/integrations/geocode.js +0 -43
- package/src/integrations/prisma.js +0 -30
- package/src/integrations/sendgrid.js +0 -58
- package/src/integrations/twilio.js +0 -146
- package/src/lifecycle/xFastifyAfter.js +0 -27
- package/src/middleware/bugsnag.js +0 -10
- package/src/middleware/cors.js +0 -10
- package/src/middleware/fancyErrors.js +0 -26
- package/src/middleware/multipart.js +0 -6
- package/src/middleware/rateLimit.js +0 -6
- package/src/middleware/underPressure.js +0 -6
- package/src/utils/colorize.js +0 -37
- package/src/utils/cookie.js +0 -5
- package/src/utils/formatBytes.js +0 -16
- package/src/utils/health.js +0 -126
- package/src/utils/xEcho.js +0 -12
- package/src/utils/xSlugify.js +0 -20
- package/src/utils/xUUID.js +0 -14
- package/src/xConfig.js +0 -117
- package/test/index.js +0 -17
- package/ts-reference/integrations/cloudinary.ts +0 -26
- package/ts-reference/integrations/prisma.ts +0 -13
- package/ts-reference/integrations/sendgrid.ts +0 -27
- package/ts-reference/integrations/stripe.ts +0 -15
- package/ts-reference/integrations/twilio.ts +0 -20
- package/ts-reference/middleware/bugsnag.ts +0 -10
- package/ts-reference/middleware/cors.ts +0 -13
- package/ts-reference/middleware/errorHandler.ts +0 -24
- package/ts-reference/middleware/multipart.ts +0 -8
- package/ts-reference/middleware/rateLimit.ts +0 -8
- package/ts-reference/middleware/underPressure.ts +0 -11
- package/ts-reference/utils/colorize.ts +0 -45
- package/ts-reference/utils/formatBytes.ts +0 -8
- package/ts-reference/utils/randomUUID.ts +0 -3
- package/ts-reference/utils/statAsync.ts +0 -4
- package/xConfigReference.js +0 -1495
- package/xConfigWorkingList.js +0 -720
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../src/middleware/errorHandler.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAwB,EACxB,WAAoB;IAEpB,IAAI,WAAW,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;YAChD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,GAAG,CAAC;YAC3C,MAAM,QAAQ,GAAG;gBACf,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,uBAAuB;gBACjD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;aACxE,CAAC;YAEF,mCAAmC;YACnC,IAAI,OAAO,CAAC,OAAO;gBAAE,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAEnD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC5B,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"multipart.js","sourceRoot":"","sources":["../../src/middleware/multipart.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAwB,EAAE,OAAY;IACzE,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAC;QAC9D,OAAO,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rateLimit.js","sourceRoot":"","sources":["../../src/middleware/rateLimit.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAwB,EAAE,OAAY;IACzE,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,qBAAqB,CAAC,EAAE,OAAO,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"underPressure.js","sourceRoot":"","sources":["../../src/middleware/underPressure.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,OAAwB,EACxB,OAAY;IAEZ,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC7B,MAAM,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,yBAAyB,CAAC,EAAE,OAAO,CAAC,CAAC;QACnE,OAAO,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC"}
|
package/dist/utils/colorize.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { FastifyInstance, RouteOptions } from "fastify";
|
|
2
|
-
export declare function colorize(method: string, text: string): string;
|
|
3
|
-
export declare function printRoutes(routes: RouteOptions[], colors?: boolean): void;
|
|
4
|
-
export declare function captureRoutes(fastify: FastifyInstance): RouteOptions[];
|
package/dist/utils/colorize.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const COLORS = {
|
|
2
|
-
POST: 33,
|
|
3
|
-
GET: 32,
|
|
4
|
-
PUT: 34,
|
|
5
|
-
DELETE: 31,
|
|
6
|
-
PATCH: 90,
|
|
7
|
-
clear: 39,
|
|
8
|
-
};
|
|
9
|
-
// Function to colorize method and path names
|
|
10
|
-
export function colorize(method, text) {
|
|
11
|
-
const colorCode = COLORS[method] || COLORS.clear;
|
|
12
|
-
return `\u001b[${colorCode}m${text}\u001b[${COLORS.clear}m`;
|
|
13
|
-
}
|
|
14
|
-
// Function to print the collected routes
|
|
15
|
-
export function printRoutes(routes, colors = true) {
|
|
16
|
-
routes
|
|
17
|
-
.sort((a, b) => a.url.localeCompare(b.url))
|
|
18
|
-
.forEach(({ method, url }) => {
|
|
19
|
-
const methodsArray = Array.isArray(method) ? method : [method];
|
|
20
|
-
methodsArray
|
|
21
|
-
.filter((m) => m !== "HEAD")
|
|
22
|
-
.forEach((m) => console.info(`${colors ? colorize(m, m) : m}\t${colors ? colorize(m, url) : url}`));
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
// Helper function to capture all registered routes
|
|
26
|
-
export function captureRoutes(fastify) {
|
|
27
|
-
const routes = [];
|
|
28
|
-
fastify.addHook("onRoute", (routeOptions) => {
|
|
29
|
-
routes.push(routeOptions);
|
|
30
|
-
});
|
|
31
|
-
return routes;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=colorize.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"colorize.js","sourceRoot":"","sources":["../../src/utils/colorize.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,GAA+B;IACzC,IAAI,EAAE,EAAE;IACR,GAAG,EAAE,EAAE;IACP,GAAG,EAAE,EAAE;IACP,MAAM,EAAE,EAAE;IACV,KAAK,EAAE,EAAE;IACT,KAAK,EAAE,EAAE;CACV,CAAC;AAEF,6CAA6C;AAC7C,MAAM,UAAU,QAAQ,CAAC,MAAc,EAAE,IAAY;IACnD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAoB,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC;IAC/D,OAAO,UAAU,SAAS,IAAI,IAAI,UAAU,MAAM,CAAC,KAAK,GAAG,CAAC;AAC9D,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,WAAW,CAAC,MAAsB,EAAE,MAAM,GAAG,IAAI;IAC/D,MAAM;SACH,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;SAC1C,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE;QAC3B,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC/D,YAAY;aACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC;aAC3B,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CACb,OAAO,CAAC,IAAI,CACV,GAAG,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CACrE,CACF,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,aAAa,CAAC,OAAwB;IACpD,MAAM,MAAM,GAAmB,EAAE,CAAC;IAElC,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,YAAY,EAAE,EAAE;QAC1C,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function formatBytes(bytes: number, decimals?: number): string;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export function formatBytes(bytes, decimals = 2) {
|
|
2
|
-
if (bytes === 0)
|
|
3
|
-
return "0 Bytes";
|
|
4
|
-
const k = 1024;
|
|
5
|
-
const dm = decimals < 0 ? 0 : decimals;
|
|
6
|
-
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB"];
|
|
7
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
8
|
-
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
9
|
-
}
|
|
10
|
-
//# sourceMappingURL=formatBytes.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"formatBytes.js","sourceRoot":"","sources":["../../src/utils/formatBytes.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,QAAQ,GAAG,CAAC;IACrD,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAClC,MAAM,CAAC,GAAG,IAAI,CAAC;IACf,MAAM,EAAE,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACvC,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IACtD,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,OAAO,GAAG,UAAU,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3E,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const generateUUID: any;
|
package/dist/utils/randomUUID.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"randomUUID.js","sourceRoot":"","sources":["../../src/utils/randomUUID.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,MAAM,CAAC,MAAM,YAAY,GAAG,UAAU,CAAC"}
|
package/dist/utils/statAsync.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"statAsync.js","sourceRoot":"","sources":["../../src/utils/statAsync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC"}
|
package/dist/xConfig.d.ts
DELETED
package/dist/xConfig.js
DELETED
package/dist/xConfig.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"xConfig.js","sourceRoot":"","sources":["../src/xConfig.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAEhC,KAAK,UAAU,OAAO,CAAC,OAAO,EAAE,OAAO;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,2BAA2B,CAAC,CAAC;AAC/D,CAAC;AAED,eAAe,EAAE,CAAC,OAAO,EAAE;IACzB,IAAI,EAAE,SAAS;CAChB,CAAC,CAAC"}
|
package/server/app.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// server/app.js
|
|
2
|
-
import Fastify from 'fastify';
|
|
3
|
-
import xConfig from '../src/xConfig.js'; // Import your plugin correctly
|
|
4
|
-
|
|
5
|
-
const fastify = Fastify();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export default async function (fastify, opts) {
|
|
9
|
-
fastify.register(xConfig, {
|
|
10
|
-
professional: false,
|
|
11
|
-
fancyErrors: true,
|
|
12
|
-
prisma: {
|
|
13
|
-
active: false,
|
|
14
|
-
},
|
|
15
|
-
bugsnag: {
|
|
16
|
-
apiKey: process.env.BUGSNAG_API_KEY
|
|
17
|
-
},
|
|
18
|
-
rateLimit: {
|
|
19
|
-
max: process.env.RATE_LIMIT_MAX || 100,
|
|
20
|
-
timeWindow: process.env.RATE_LIMIT_TIME_WINDOW || '1 minute'
|
|
21
|
-
},
|
|
22
|
-
stripe: {
|
|
23
|
-
active: false,
|
|
24
|
-
apiKey: process.env.STRIPE_API_KEY
|
|
25
|
-
},
|
|
26
|
-
sendGrid: {
|
|
27
|
-
active: true,
|
|
28
|
-
apiKey: process.env.SENDGRID_API_KEY,
|
|
29
|
-
apiKeyEmailValidation: process.env.SENDGRID_API_EMAIL_VALIDATION_KEY,
|
|
30
|
-
fromEmail: process.env.SENDGRID_FROM_EMAIL || 'ops@getx.io',
|
|
31
|
-
},
|
|
32
|
-
twilio: {
|
|
33
|
-
active: true,
|
|
34
|
-
accountSid: process.env.TWILIO_ACCOUNT_SID,
|
|
35
|
-
authToken: process.env.TWILIO_AUTH_TOKEN,
|
|
36
|
-
phoneNumber: process.env.TWILIO_PHONE_NUMBER
|
|
37
|
-
},
|
|
38
|
-
cloudinary: {
|
|
39
|
-
active: false,
|
|
40
|
-
cloudName: process.env.CLOUDINARY_CLOUD_NAME,
|
|
41
|
-
apiKey: process.env.CLOUDINARY_API_KEY,
|
|
42
|
-
apiSecret: process.env.CLOUDINARY_API_SECRET,
|
|
43
|
-
basePath: process.env.CLOUDINARY_BASE_PATH || 'basepath'
|
|
44
|
-
},
|
|
45
|
-
cors: {
|
|
46
|
-
active: true,
|
|
47
|
-
origin: process.env.CORS_ORIGIN || ['http://localhost:3000', 'https://app.bandmate.io'],
|
|
48
|
-
credentials: true
|
|
49
|
-
},
|
|
50
|
-
auth: {
|
|
51
|
-
excludedPaths: ['/public', '/portal/auth/register'],
|
|
52
|
-
admin: {
|
|
53
|
-
active: true,
|
|
54
|
-
secret: process.env.ADMIN_JWT_SECRET,
|
|
55
|
-
expiresIn: '1h',
|
|
56
|
-
cookieOptions: {
|
|
57
|
-
name: 'adminToken',
|
|
58
|
-
httpOnly: true,
|
|
59
|
-
secure: true, // Set to false if not using HTTPS
|
|
60
|
-
sameSite: 'strict', // Can be 'lax', 'strict', or 'none'
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
user: {
|
|
64
|
-
active: true,
|
|
65
|
-
secret: process.env.USER_JWT_SECRET,
|
|
66
|
-
expiresIn: '1h',
|
|
67
|
-
cookieOptions: {
|
|
68
|
-
name: 'userToken',
|
|
69
|
-
httpOnly: true,
|
|
70
|
-
secure: true,
|
|
71
|
-
sameSite: 'strict',
|
|
72
|
-
},
|
|
73
|
-
me: {
|
|
74
|
-
isOnboarded: true
|
|
75
|
-
},
|
|
76
|
-
registerEmail: {
|
|
77
|
-
subject: 'Welcome to Bandmate!',
|
|
78
|
-
templateId: ''
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
geocode: {
|
|
83
|
-
active: true,
|
|
84
|
-
apiKey: process.env.GEOCODIO_API_KEY
|
|
85
|
-
}
|
|
86
|
-
}); // Register the default export, which should be a function
|
|
87
|
-
fastify.get('/', async (request, reply) => {
|
|
88
|
-
console.log(fastify.xEcho())
|
|
89
|
-
return { status: fastify.xEcho() }
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
};
|
package/src/auth/admin.js
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import jwt from "@fastify/jwt";
|
|
2
|
-
import bcrypt from "bcrypt";
|
|
3
|
-
const isProduction = process.env.NODE_ENV === 'production';
|
|
4
|
-
export async function setupAdminAuth(fastify, options) {
|
|
5
|
-
if (options.admin?.active !== false) {
|
|
6
|
-
|
|
7
|
-
// Ensure the admin JWT secret is provided
|
|
8
|
-
if (!options.admin.secret) {
|
|
9
|
-
throw new Error("Admin JWT secret must be provided.");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const adminAuthOptions = options.admin;
|
|
13
|
-
const adminCookieName =
|
|
14
|
-
adminAuthOptions.cookieOptions?.name || "adminToken";
|
|
15
|
-
const adminRefreshCookieName =
|
|
16
|
-
adminAuthOptions.cookieOptions?.refreshTokenName || "adminRefreshToken";
|
|
17
|
-
const adminCookieOptions = {
|
|
18
|
-
httpOnly: true, // Ensures the cookie is not accessible via JavaScript
|
|
19
|
-
secure: isProduction, // true in production (HTTPS), false in development (HTTP)
|
|
20
|
-
sameSite: isProduction ? 'None' : 'Lax', // 'None' for cross-origin, 'Lax' for development
|
|
21
|
-
path: '/', // Ensure cookies are valid for the entire site
|
|
22
|
-
};
|
|
23
|
-
const adminExcludedPaths = adminAuthOptions.excludedPaths || [
|
|
24
|
-
"/admin/auth/login",
|
|
25
|
-
"/admin/auth/logout",
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// Decorator to hash admin passwords
|
|
29
|
-
async function hashAdminPassword(password) {
|
|
30
|
-
const saltRounds = 10; // Number of salt rounds for bcrypt (10 is generally a good default)
|
|
31
|
-
try {
|
|
32
|
-
const hashedPassword = await bcrypt.hash(password, saltRounds);
|
|
33
|
-
return hashedPassword;
|
|
34
|
-
} catch (error) {
|
|
35
|
-
throw new Error("Failed to hash password: " + error.message);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
fastify.decorate("hashAdminPassword", hashAdminPassword);
|
|
40
|
-
|
|
41
|
-
// Register JWT for admin
|
|
42
|
-
await fastify.register(jwt, {
|
|
43
|
-
secret: adminAuthOptions.secret,
|
|
44
|
-
sign: { algorithm: 'HS256', expiresIn: adminAuthOptions.expiresIn || "15m" },
|
|
45
|
-
cookie: {
|
|
46
|
-
cookieName: adminCookieName,
|
|
47
|
-
signed: false,
|
|
48
|
-
},
|
|
49
|
-
namespace: "adminJwt",
|
|
50
|
-
jwtVerify: "adminJwtVerify",
|
|
51
|
-
jwtSign: "adminJwtSign",
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// Common function to set tokens as cookies
|
|
55
|
-
const setAdminAuthCookies = (reply, accessToken, refreshToken) => {
|
|
56
|
-
reply.setCookie(adminCookieName, accessToken, adminCookieOptions);
|
|
57
|
-
reply.setCookie(adminRefreshCookieName, refreshToken, {
|
|
58
|
-
// ...adminCookieOptions,
|
|
59
|
-
httpOnly: true, // Ensures the cookie is not accessible via JavaScript
|
|
60
|
-
secure: isProduction, // true in production (HTTPS), false in development (HTTP)
|
|
61
|
-
sameSite: isProduction ? 'None' : 'Lax', // 'None' for cross-origin, 'Lax' for development
|
|
62
|
-
path: '/', // Ensure cookies are valid for the entire site
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Admin authentication hook
|
|
67
|
-
fastify.addHook("onRequest", async (request, reply) => {
|
|
68
|
-
const url = request.url;
|
|
69
|
-
|
|
70
|
-
// Skip authentication for excluded paths
|
|
71
|
-
if (adminExcludedPaths.some((path) => url.startsWith(path))) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (url.startsWith("/admin")) {
|
|
76
|
-
try {
|
|
77
|
-
// Extract token from cookie or Authorization header
|
|
78
|
-
const authHeader = request.headers.authorization;
|
|
79
|
-
const authToken =
|
|
80
|
-
authHeader && authHeader.startsWith("Bearer ")
|
|
81
|
-
? authHeader.slice(7)
|
|
82
|
-
: null;
|
|
83
|
-
const token = request.cookies[adminCookieName] || authToken;
|
|
84
|
-
|
|
85
|
-
if (!token) {
|
|
86
|
-
throw fastify.httpErrors.unauthorized(
|
|
87
|
-
"Admin access token not provided"
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Verify access token
|
|
92
|
-
const decoded = await request.adminJwtVerify(token);
|
|
93
|
-
request.adminAuth = decoded; // Attach admin auth context
|
|
94
|
-
} catch (err) {
|
|
95
|
-
// Use built-in HTTP error handling
|
|
96
|
-
reply.send(
|
|
97
|
-
fastify.httpErrors.unauthorized("Invalid or expired access token")
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// Admin login route
|
|
104
|
-
fastify.post("/admin/auth/login", async (req, reply) => {
|
|
105
|
-
try {
|
|
106
|
-
const { email, password } = req.body;
|
|
107
|
-
|
|
108
|
-
// Validate input
|
|
109
|
-
if (!email || !password) {
|
|
110
|
-
throw fastify.httpErrors.badRequest(
|
|
111
|
-
"Email and password are required"
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Fetch admin from the database
|
|
116
|
-
const admin = await fastify.prisma.admins.findUnique({
|
|
117
|
-
where: { email },
|
|
118
|
-
});
|
|
119
|
-
if (!admin) {
|
|
120
|
-
throw fastify.httpErrors.unauthorized("Invalid credentials");
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Compare passwords using bcrypt
|
|
124
|
-
const isValidPassword = await bcrypt.compare(password, admin.password);
|
|
125
|
-
if (!isValidPassword) {
|
|
126
|
-
throw fastify.httpErrors.unauthorized("Invalid credentials");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Issue access token
|
|
130
|
-
const accessToken = await reply.adminJwtSign({ id: admin.id });
|
|
131
|
-
|
|
132
|
-
// Generate refresh token
|
|
133
|
-
const refreshToken = await fastify.randomUUID();
|
|
134
|
-
const hashedRefreshToken = await bcrypt.hash(refreshToken, 10);
|
|
135
|
-
|
|
136
|
-
// Store hashed refresh token in the database
|
|
137
|
-
await fastify.prisma.admins.update({
|
|
138
|
-
where: { id: admin.id },
|
|
139
|
-
data: { refreshToken: hashedRefreshToken },
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
// Set tokens as cookies
|
|
143
|
-
setAdminAuthCookies(reply, accessToken, refreshToken);
|
|
144
|
-
|
|
145
|
-
reply.send({ accessToken });
|
|
146
|
-
} catch (err) {
|
|
147
|
-
reply.send(err);
|
|
148
|
-
}
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// Admin refresh token route
|
|
152
|
-
fastify.post("/admin/auth/refresh", async (req, reply) => {
|
|
153
|
-
try {
|
|
154
|
-
const adminAuth = req.adminAuth;
|
|
155
|
-
const refreshToken = req.cookies[adminRefreshCookieName];
|
|
156
|
-
if (!refreshToken) {
|
|
157
|
-
throw fastify.httpErrors.unauthorized("Refresh token not provided");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Fetch admin from the database using the refresh token
|
|
161
|
-
const admin = await fastify.prisma.admins.findFirst({
|
|
162
|
-
where: { id: adminAuth.id, refreshToken: { not: null } },
|
|
163
|
-
});
|
|
164
|
-
if (!admin) {
|
|
165
|
-
throw fastify.httpErrors.unauthorized("Invalid refresh token");
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Verify the refresh token
|
|
169
|
-
const isValid = await bcrypt.compare(refreshToken, admin.refreshToken);
|
|
170
|
-
if (!isValid) {
|
|
171
|
-
throw fastify.httpErrors.unauthorized("Invalid refresh token");
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Issue new access token
|
|
175
|
-
const accessToken = await reply.adminJwtSign({ id: admin.id });
|
|
176
|
-
|
|
177
|
-
// Generate new refresh token
|
|
178
|
-
const newRefreshToken = await fastify.randomUUID();
|
|
179
|
-
const hashedNewRefreshToken = await bcrypt.hash(newRefreshToken, 10);
|
|
180
|
-
|
|
181
|
-
// Update refresh token in the database
|
|
182
|
-
await fastify.prisma.admins.update({
|
|
183
|
-
where: { id: admin.id },
|
|
184
|
-
data: { refreshToken: hashedNewRefreshToken },
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
// Set new tokens as cookies
|
|
188
|
-
setAdminAuthCookies(reply, accessToken, newRefreshToken);
|
|
189
|
-
|
|
190
|
-
reply.send({ accessToken });
|
|
191
|
-
} catch (err) {
|
|
192
|
-
reply.send(err);
|
|
193
|
-
}
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
// Admin logout route
|
|
197
|
-
fastify.post("/admin/auth/logout", async (req, reply) => {
|
|
198
|
-
try {
|
|
199
|
-
const adminAuth = req.adminAuth;
|
|
200
|
-
if (adminAuth) {
|
|
201
|
-
// Delete refresh token from the database
|
|
202
|
-
await fastify.prisma.admins.update({
|
|
203
|
-
where: { id: adminAuth.id },
|
|
204
|
-
data: { refreshToken: null },
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Clear cookies
|
|
209
|
-
reply.clearCookie(adminCookieName, { path: "/" });
|
|
210
|
-
reply.clearCookie(adminRefreshCookieName, { path: "/" });
|
|
211
|
-
|
|
212
|
-
reply.send({ message: "Logged out successfully" });
|
|
213
|
-
} catch (err) {
|
|
214
|
-
reply.send(err);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// Admin authentication status route
|
|
219
|
-
fastify.get("/admin/auth/me", async (req, reply) => {
|
|
220
|
-
try {
|
|
221
|
-
const adminAuth = req.adminAuth;
|
|
222
|
-
|
|
223
|
-
// Fetch admin details from database
|
|
224
|
-
const admin = await fastify.prisma.admins.findUnique({
|
|
225
|
-
where: { id: adminAuth.id },
|
|
226
|
-
select: { id: true, firstName: true, lastName: true, email: true },
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
if (!admin) {
|
|
230
|
-
throw fastify.httpErrors.notFound("Admin not found");
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
reply.send(admin);
|
|
234
|
-
} catch (err) {
|
|
235
|
-
reply.send(err);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
console.info(" ✅ Auth Admin Enabled");
|
|
240
|
-
}
|
|
241
|
-
}
|