maxserver 0.0.18 → 0.1.2
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 +1 -0
- package/bin/init.js +1 -1
- package/package.json +1 -1
- package/src/getAddress.js +8 -0
- package/src/index.js +7 -5
- package/src/setup.js +33 -49
- package/src/{routeLoader.js → setupRoutes.js} +3 -3
package/README.md
CHANGED
|
@@ -64,6 +64,7 @@ Any fastify options can be passed to maxserver() too.
|
|
|
64
64
|
| `mongodb` | *-* | MongoDB URI, if set auto-connects db |
|
|
65
65
|
| `public` | `false` | Set `true` to expose the server publicly (binds to `0.0.0.0`) |
|
|
66
66
|
| `static` | *-* | If set, serves this directory statically |
|
|
67
|
+
| `routesDir` | *src* | Directory to auto collect routes |
|
|
67
68
|
---
|
|
68
69
|
|
|
69
70
|
<br>
|
package/bin/init.js
CHANGED
package/package.json
CHANGED
package/src/getAddress.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import os from "node:os";
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* When listening on all interfaces (0.0.0.0), Node returns "0.0.0.0".
|
|
6
|
+
* This is a setting, not a valid URL for other devices.
|
|
7
|
+
* So we try hunt down the actual LAN IP to print the real address on which server can be accessed
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
|
|
3
11
|
function getLanIp() {
|
|
4
12
|
const nets = os.networkInterfaces();
|
|
5
13
|
|
package/src/index.js
CHANGED
|
@@ -6,13 +6,13 @@ import {
|
|
|
6
6
|
setupJwt,
|
|
7
7
|
setupMongo,
|
|
8
8
|
setupStatic,
|
|
9
|
-
setupRoutes,
|
|
10
9
|
setupDocs,
|
|
11
10
|
setupCookie,
|
|
12
|
-
getHttpsOptions,
|
|
13
11
|
} from "./setup.js";
|
|
14
12
|
|
|
13
|
+
|
|
15
14
|
import { getAddress } from "./getAddress.js";
|
|
15
|
+
import { setupRoutes } from "./setupRoutes.js";
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
export default async function maxserver(config = {}) {
|
|
@@ -26,6 +26,7 @@ export default async function maxserver(config = {}) {
|
|
|
26
26
|
docs = process.env.DOCS !== "false",
|
|
27
27
|
cors = process.env.CORS || "*",
|
|
28
28
|
env = process.env.NODE_ENV || "development",
|
|
29
|
+
routesDir = process.env.ROUTESDIR || "src",
|
|
29
30
|
openapiInfo,
|
|
30
31
|
static: isStatic = process.env.STATIC,
|
|
31
32
|
public: isPublic = process.env.PUBLIC === "true",
|
|
@@ -36,18 +37,19 @@ export default async function maxserver(config = {}) {
|
|
|
36
37
|
} = config;
|
|
37
38
|
|
|
38
39
|
const maxserverConfig = {
|
|
39
|
-
port, secret, mongodb, docs, cors,
|
|
40
|
+
port, secret, mongodb, docs, cors, env, openapiInfo, routesDir,
|
|
40
41
|
static: isStatic,
|
|
41
42
|
public: isPublic
|
|
42
43
|
};
|
|
43
44
|
|
|
45
|
+
if (!secret)
|
|
46
|
+
throw new Error("secret is must have");
|
|
47
|
+
|
|
44
48
|
|
|
45
49
|
let app;
|
|
46
50
|
try {
|
|
47
51
|
app = Fastify({
|
|
48
|
-
https: getHttpsOptions() || undefined,
|
|
49
52
|
trustProxy: true,
|
|
50
|
-
|
|
51
53
|
// Required to allow adding doc fields on schema
|
|
52
54
|
ajv: { customOptions: { strictSchema: false } },
|
|
53
55
|
...fastifyOpts
|
package/src/setup.js
CHANGED
|
@@ -10,12 +10,6 @@ import helmet from "@fastify/helmet";
|
|
|
10
10
|
import swagger from "@fastify/swagger";
|
|
11
11
|
import apiReference from "@scalar/fastify-api-reference";
|
|
12
12
|
|
|
13
|
-
import { loadRoutes } from "./routeLoader.js";
|
|
14
|
-
|
|
15
|
-
export async function setupRoutes(app) {
|
|
16
|
-
await loadRoutes(app);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
13
|
|
|
20
14
|
export async function setupHelmet(app) {
|
|
21
15
|
await app.register(helmet, {
|
|
@@ -27,50 +21,41 @@ export async function setupHelmet(app) {
|
|
|
27
21
|
}
|
|
28
22
|
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
32
24
|
export async function setupCors(app) {
|
|
33
|
-
const isProd =
|
|
25
|
+
const isProd = app.maxserver.env === "production";
|
|
34
26
|
const origin = app.maxserver.cors ?? "*";
|
|
35
|
-
|
|
36
27
|
if (isProd && origin === "*") {
|
|
37
28
|
app.log.warn("CORS: allowing all origins (*) in production");
|
|
38
29
|
}
|
|
39
|
-
|
|
40
30
|
await app.register(cors, { origin });
|
|
41
31
|
}
|
|
42
32
|
|
|
43
33
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
try {
|
|
50
|
-
return {
|
|
51
|
-
key: fs.readFileSync(TLS_KEY),
|
|
52
|
-
cert: fs.readFileSync(TLS_CERT),
|
|
53
|
-
};
|
|
54
|
-
} catch (err) {
|
|
55
|
-
throw new Error(`TLS read failed: ${err.message || err}`);
|
|
56
|
-
}
|
|
34
|
+
export async function setupCookie(app) {
|
|
35
|
+
await app.register(cookie, {
|
|
36
|
+
secret: app.maxserver.secret,
|
|
37
|
+
hook: "onRequest"
|
|
38
|
+
});
|
|
57
39
|
}
|
|
58
40
|
|
|
59
41
|
|
|
60
42
|
|
|
61
43
|
|
|
62
|
-
export async function
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
44
|
+
export async function setupMongo(app) {
|
|
45
|
+
const url = app.maxserver.mongodb;
|
|
46
|
+
if (!url) return;
|
|
47
|
+
await app.register(mongodb, { url });
|
|
48
|
+
|
|
49
|
+
const { ObjectId, db } = app.mongo;
|
|
50
|
+
global.oid = id => new ObjectId(id);
|
|
51
|
+
global.db = db;
|
|
67
52
|
}
|
|
68
53
|
|
|
69
54
|
|
|
70
|
-
export async function setupJwt(app) {
|
|
71
55
|
|
|
56
|
+
export async function setupJwt(app) {
|
|
72
57
|
await app.register(jwt, {
|
|
73
|
-
secret: app.maxserver.secret
|
|
58
|
+
secret: app.maxserver.secret,
|
|
74
59
|
cookie: { cookieName: "token" }
|
|
75
60
|
});
|
|
76
61
|
|
|
@@ -79,9 +64,7 @@ export async function setupJwt(app) {
|
|
|
79
64
|
// Let preflight requests pass
|
|
80
65
|
if (req.method === "OPTIONS") return;
|
|
81
66
|
|
|
82
|
-
const auth =
|
|
83
|
-
req.routeOptions?.config?.auth ??
|
|
84
|
-
req.routeOptions?.schema?.auth;
|
|
67
|
+
const auth = req.routeOptions?.config?.auth;
|
|
85
68
|
|
|
86
69
|
if (!auth) return;
|
|
87
70
|
|
|
@@ -93,15 +76,6 @@ export async function setupJwt(app) {
|
|
|
93
76
|
}
|
|
94
77
|
|
|
95
78
|
|
|
96
|
-
export async function setupMongo(app) {
|
|
97
|
-
const url = app.maxserver.mongodb;
|
|
98
|
-
if (!url) return;
|
|
99
|
-
await app.register(mongodb, { url });
|
|
100
|
-
|
|
101
|
-
const { ObjectId, db } = app.mongo;
|
|
102
|
-
global.oid = id => new ObjectId(id ? String(id) : undefined);
|
|
103
|
-
global.db = db;
|
|
104
|
-
}
|
|
105
79
|
|
|
106
80
|
|
|
107
81
|
export async function setupDocs(app) {
|
|
@@ -128,11 +102,11 @@ export async function setupDocs(app) {
|
|
|
128
102
|
|
|
129
103
|
app.get("/openapi.json", {}, () => app.swagger());
|
|
130
104
|
|
|
131
|
-
if (app.maxserver.docs
|
|
105
|
+
if (app.maxserver.docs !== false)
|
|
132
106
|
await app.register(apiReference, { routePrefix: "/docs", openapi: true });
|
|
133
107
|
|
|
134
108
|
app.addHook("onRoute", (route) => {
|
|
135
|
-
const auth = route.config?.auth
|
|
109
|
+
const auth = route.config?.auth;
|
|
136
110
|
if (!auth) return;
|
|
137
111
|
route.schema ||= {};
|
|
138
112
|
route.schema.security ||= [{ bearerAuth: [] }, { cookieAuth: [] }];
|
|
@@ -145,9 +119,19 @@ export async function setupStatic(app) {
|
|
|
145
119
|
const dir = app.maxserver.static;
|
|
146
120
|
if (!dir) return;
|
|
147
121
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
122
|
+
if (typeof dir !== "string") {
|
|
123
|
+
console.error("❌ maxserver.static must be a string path");
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const abs = path.resolve(dir);
|
|
128
|
+
if (!fs.existsSync(abs)) {
|
|
129
|
+
console.error(`❌ maxserver.static not found: ${abs}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await app.register(fastifyStatic, { root: abs });
|
|
152
134
|
}
|
|
153
135
|
|
|
136
|
+
|
|
137
|
+
|
|
@@ -74,9 +74,9 @@ async function importDefault(file) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
export async function
|
|
77
|
+
export async function setupRoutes(app) {
|
|
78
78
|
const seen = new Map();
|
|
79
|
-
const root = path.
|
|
79
|
+
const root = path.resolve(app.maxserver.routesDir || "src");
|
|
80
80
|
|
|
81
81
|
for (const file of walk(root)) {
|
|
82
82
|
// Skip schema files; they are loaded alongside their route file
|
|
@@ -125,6 +125,6 @@ export async function loadRoutes(fastify) {
|
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
app[info.method](info.url, { ...routeOptions, schema }, handler);
|
|
129
129
|
}
|
|
130
130
|
}
|