maxserver 0.0.14 → 0.0.15
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/README.md +7 -12
- package/package.json +1 -1
- package/src/index.js +42 -9
- package/src/setup.js +56 -55
package/README.md
CHANGED
|
@@ -20,7 +20,6 @@ Ready node server setup based on **Fastify** to speedup api development.
|
|
|
20
20
|
- **Dev server**
|
|
21
21
|
<br><br>
|
|
22
22
|
|
|
23
|
-
|
|
24
23
|
- Dependencies: original fastify packages + scalar/fastify-api-reference (doc generator)
|
|
25
24
|
- The source is simple and short. Everyone shall be able to read, understand and modify if needed.
|
|
26
25
|
|
|
@@ -36,23 +35,19 @@ npm install maxserver
|
|
|
36
35
|
## Setup
|
|
37
36
|
```js
|
|
38
37
|
import maxserver from "maxserver";
|
|
38
|
+
|
|
39
39
|
const server = await maxserver();
|
|
40
|
-
|
|
41
|
-
port: Number(process.env.PORT || 3000),
|
|
42
|
-
});
|
|
40
|
+
await server.listen();
|
|
43
41
|
|
|
44
|
-
console.log("Server running at",
|
|
42
|
+
console.log("Server running at ", server.getAddress());
|
|
45
43
|
export default server;
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
**maxserver(options)** forwards options to fastify(options).
|
|
49
|
-
It returns the fully configured Fastify server instance.
|
|
50
|
-
|
|
51
44
|
|
|
45
|
+
```
|
|
52
46
|
---
|
|
53
47
|
|
|
54
|
-
## ⚙️
|
|
55
|
-
|
|
48
|
+
## ⚙️ Configure
|
|
49
|
+
Quick configure your server by passing options to the **maxserver()** or define them in your .env file.
|
|
50
|
+
You can also pass any fastify options.
|
|
56
51
|
|
|
57
52
|
| Variable | Default | Description |
|
|
58
53
|
| :--- | :--- | :--- |
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -15,24 +15,50 @@ import {
|
|
|
15
15
|
import { setupGetAddress } from "./getAddress.js";
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
export default async function maxserver(config = {}) {
|
|
19
|
+
|
|
20
|
+
const {
|
|
21
|
+
|
|
22
|
+
// maxserver options
|
|
23
|
+
secret = process.env.SECRET,
|
|
24
|
+
cookieSecret = process.env.COOKIE_SECRET,
|
|
25
|
+
mongodbUri = process.env.MONGODB_URI,
|
|
26
|
+
docs = process.env.DOCS !== "false",
|
|
27
|
+
staticDir = process.env.STATIC_DIR,
|
|
28
|
+
corsOrigin = process.env.CORS_ORIGIN || "*",
|
|
29
|
+
|
|
30
|
+
// everything else goes straight to Fastify
|
|
31
|
+
...fastifyOpts
|
|
32
|
+
|
|
33
|
+
} = config;
|
|
34
|
+
|
|
35
|
+
if (process.env.NODE_ENV == "development") {
|
|
36
|
+
console.error("Please define secret in env !");
|
|
37
|
+
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let maxserverConfig = {
|
|
41
|
+
secret, mongodbUri, docs, staticDir, corsOrigin
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
process.env.NODE_ENV;
|
|
45
|
+
|
|
46
|
+
|
|
18
47
|
|
|
19
|
-
export default async function maxserver(options = {}) {
|
|
20
48
|
|
|
21
49
|
const app = Fastify({
|
|
22
|
-
|
|
23
|
-
host: "0.0.0.0",
|
|
50
|
+
|
|
24
51
|
https: getHttpsOptions() || undefined,
|
|
52
|
+
trustProxy: true,
|
|
25
53
|
|
|
26
54
|
// To allow writing example value fields to schemas for doucumentation
|
|
27
55
|
ajv: { customOptions: { strictSchema: false } },
|
|
28
|
-
...
|
|
56
|
+
...fastifyOpts,
|
|
29
57
|
});
|
|
30
58
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return err;
|
|
35
|
-
};
|
|
59
|
+
app.decorate("maxserver", maxserverConfig);
|
|
60
|
+
|
|
61
|
+
|
|
36
62
|
|
|
37
63
|
setupGetAddress(app);
|
|
38
64
|
await setupCookie(app);
|
|
@@ -45,5 +71,12 @@ export default async function maxserver(options = {}) {
|
|
|
45
71
|
await setupRoutes(app);
|
|
46
72
|
|
|
47
73
|
|
|
74
|
+
global.createError = function (code, message) {
|
|
75
|
+
const err = new Error(message);
|
|
76
|
+
err.statusCode = code;
|
|
77
|
+
return err;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
|
|
48
81
|
return app;
|
|
49
82
|
}
|
package/src/setup.js
CHANGED
|
@@ -12,20 +12,11 @@ import apiReference from "@scalar/fastify-api-reference";
|
|
|
12
12
|
|
|
13
13
|
import { loadRoutes } from "./routeLoader.js";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Use COOKIE_SECRET or fall back to JWT_SECRET so you don't need a new env var immediately
|
|
18
|
-
const secret = process.env.COOKIE_SECRET || process.env.JWT_SECRET || "change-me-in-prod";
|
|
19
|
-
|
|
20
|
-
await app.register(cookie, {
|
|
21
|
-
secret,
|
|
22
|
-
hook: "onRequest", // Crucial: Ensures cookies are parsed before your route handlers run
|
|
23
|
-
parseOptions: {}
|
|
24
|
-
});
|
|
15
|
+
export async function setupRoutes(app) {
|
|
16
|
+
await loadRoutes(app);
|
|
25
17
|
}
|
|
26
18
|
|
|
27
19
|
|
|
28
|
-
|
|
29
20
|
export async function setupHelmet(app) {
|
|
30
21
|
await app.register(helmet, {
|
|
31
22
|
contentSecurityPolicy: false,
|
|
@@ -35,21 +26,19 @@ export async function setupHelmet(app) {
|
|
|
35
26
|
});
|
|
36
27
|
}
|
|
37
28
|
|
|
29
|
+
|
|
38
30
|
export async function setupCors(app) {
|
|
39
31
|
const isProd = process.env.NODE_ENV === "production";
|
|
40
|
-
const origin =
|
|
32
|
+
const origin = app.maxserver.corsOrigin ?? true;
|
|
41
33
|
|
|
42
|
-
if (isProd &&
|
|
34
|
+
if (isProd && origin === true) {
|
|
35
|
+
app.log.warn("CORS origin not set, allowing all origins");
|
|
36
|
+
}
|
|
43
37
|
|
|
44
38
|
await app.register(cors, { origin });
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
export async function setupRoutes(app) {
|
|
50
|
-
await loadRoutes(app);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
42
|
export function getHttpsOptions() {
|
|
54
43
|
const { TLS_KEY, TLS_CERT } = process.env;
|
|
55
44
|
if (!TLS_KEY || !TLS_CERT) return null;
|
|
@@ -67,67 +56,63 @@ export function getHttpsOptions() {
|
|
|
67
56
|
|
|
68
57
|
|
|
69
58
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
if (url.startsWith("/static/")) return true;
|
|
76
|
-
return false;
|
|
59
|
+
export async function setupCookie(app) {
|
|
60
|
+
await app.register(cookie, {
|
|
61
|
+
secret: app.maxserver.secret || "supersecret",
|
|
62
|
+
hook: "onRequest"
|
|
63
|
+
});
|
|
77
64
|
}
|
|
78
65
|
|
|
66
|
+
|
|
79
67
|
export async function setupJwt(app) {
|
|
80
|
-
const secret = process.env.JWT_SECRET;
|
|
81
|
-
if (!secret) return;
|
|
82
68
|
|
|
83
|
-
|
|
84
|
-
|
|
69
|
+
await app.register(jwt, {
|
|
70
|
+
secret: app.maxserver.secret || "supersecret",
|
|
71
|
+
cookie: { cookieName: "token" }
|
|
72
|
+
});
|
|
85
73
|
|
|
86
|
-
|
|
74
|
+
app.addHook("preHandler", async function (req) {
|
|
87
75
|
|
|
88
|
-
|
|
76
|
+
// Let preflight requests pass
|
|
89
77
|
if (req.method === "OPTIONS") return;
|
|
90
78
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
79
|
+
const auth =
|
|
80
|
+
req.routeOptions?.config?.auth ??
|
|
81
|
+
req.routeOptions?.schema?.auth;
|
|
94
82
|
|
|
95
|
-
|
|
83
|
+
if (!auth) return;
|
|
96
84
|
|
|
85
|
+
await req.jwtVerify();
|
|
97
86
|
const u = req.user;
|
|
98
87
|
req.userId = u?.sub || u?.userId || u?.userid || u?.id || null;
|
|
99
88
|
});
|
|
89
|
+
|
|
100
90
|
}
|
|
101
91
|
|
|
102
|
-
export async function setupMongo(app) {
|
|
103
92
|
|
|
104
|
-
|
|
93
|
+
export async function setupMongo(app) {
|
|
94
|
+
const url = app.maxserver.mongodbUri;
|
|
105
95
|
if (!url) return;
|
|
106
|
-
|
|
107
96
|
await app.register(mongodb, { url });
|
|
108
97
|
|
|
109
|
-
// ObjectId is available on app.mongo after registration
|
|
110
98
|
const { ObjectId, db } = app.mongo;
|
|
111
|
-
|
|
112
99
|
global.oid = id => new ObjectId(id ? String(id) : undefined);
|
|
113
100
|
global.db = db;
|
|
114
101
|
}
|
|
115
102
|
|
|
116
|
-
export async function setupStatic(app) {
|
|
117
|
-
const dir = process.env.STATIC_DIR;
|
|
118
|
-
if (!dir) return;
|
|
119
|
-
|
|
120
|
-
await app.register(fastifyStatic, {
|
|
121
|
-
root: path.resolve(dir),
|
|
122
|
-
prefix: "/static/",
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
103
|
|
|
126
104
|
export async function setupDocs(app) {
|
|
127
|
-
|
|
105
|
+
|
|
106
|
+
const info = app.maxserver.openapiInfo || {
|
|
107
|
+
title: "API",
|
|
108
|
+
version: "1.0.0",
|
|
109
|
+
};
|
|
128
110
|
|
|
129
111
|
await app.register(swagger, {
|
|
130
112
|
openapi: {
|
|
113
|
+
info,
|
|
114
|
+
// OpenAPI 3.x: securitySchemes must be defined globally here not per route
|
|
115
|
+
// Routes only add `security: [...]` that references these scheme names
|
|
131
116
|
components: {
|
|
132
117
|
securitySchemes: {
|
|
133
118
|
bearerAuth: { type: "http", scheme: "bearer" },
|
|
@@ -137,13 +122,29 @@ export async function setupDocs(app) {
|
|
|
137
122
|
},
|
|
138
123
|
});
|
|
139
124
|
|
|
140
|
-
app.get("/openapi.json", { config: { public: true } }, () => app.swagger());
|
|
141
125
|
|
|
142
|
-
|
|
126
|
+
app.get("/openapi.json", {}, () => app.swagger());
|
|
127
|
+
|
|
128
|
+
if (app.maxserver.docs != false)
|
|
129
|
+
await app.register(apiReference, { routePrefix: "/docs", openapi: true });
|
|
143
130
|
|
|
144
131
|
app.addHook("onRoute", (route) => {
|
|
145
|
-
|
|
132
|
+
const auth = route.config?.auth ?? route.schema?.auth;
|
|
133
|
+
if (!auth) return;
|
|
146
134
|
route.schema ||= {};
|
|
147
|
-
route.schema.security
|
|
135
|
+
route.schema.security ||= [{ bearerAuth: [] }, { cookieAuth: [] }];
|
|
148
136
|
});
|
|
149
|
-
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
export async function setupStatic(app) {
|
|
142
|
+
const dir = app.maxserver.staticDir;
|
|
143
|
+
if (!dir) return;
|
|
144
|
+
|
|
145
|
+
await app.register(fastifyStatic, {
|
|
146
|
+
root: path.resolve(dir),
|
|
147
|
+
prefix: "/static/",
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|