maxserver 0.8.2 → 0.8.4
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/README.md +32 -7
- package/index.d.ts +10 -4
- package/package.json +1 -1
- package/src/index.js +15 -18
- package/src/setup.js +14 -23
- package/src/setupDocs.js +1 -32
- package/src/setupRoutes.js +67 -27
- package/templates/ai/BASE +2 -0
- package/templates/package.json +1 -16
- package/templates/src/Models/user.schema.js +13 -0
package/.github/README.md
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# maxserver
|
|
2
2
|
Node server setup based on **Fastify** to speed up backend development.
|
|
3
|
-
maxserver stands for **maximized simplicity** and **minimum boilerplate**.
|
|
4
3
|
|
|
5
4
|
- **Auto Routes**: auto imports and registers routes and schemas
|
|
6
5
|
- **Auto Docs**: auto generates docs based on schemas
|
|
@@ -123,9 +122,6 @@ export default {
|
|
|
123
122
|
};
|
|
124
123
|
```
|
|
125
124
|
|
|
126
|
-
**‼️ Important use export default**
|
|
127
|
-
Some examples in the template folder.
|
|
128
|
-
|
|
129
125
|
|
|
130
126
|
### MODELS
|
|
131
127
|
You can also auto register **models** (schemas which are shared between multiple routes).
|
|
@@ -134,6 +130,9 @@ schema or generic model, by looking if a sibling file exist or not 😉
|
|
|
134
130
|
|
|
135
131
|
<br>
|
|
136
132
|
|
|
133
|
+
**‼️ Important use export default**
|
|
134
|
+
Some examples in the template folder.
|
|
135
|
+
|
|
137
136
|
|
|
138
137
|
|
|
139
138
|
## 📚 API Docs
|
|
@@ -145,6 +144,20 @@ And you can also easily test any route.
|
|
|
145
144
|
|
|
146
145
|
|
|
147
146
|
|
|
147
|
+
## Global Named Exports
|
|
148
|
+
|
|
149
|
+
Every named export across your JavaScript files is automatically assigned to the Node.js `global` object on startup. This makes your utility functions, constants, or services instantly accessible anywhere in the application without manual `import` statements. The system safely ignores `default` exports and lifecycle hooks, and it will immediately halt with a clear console error if it detects duplicate variable names across different files.
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
148
161
|
|
|
149
162
|
|
|
150
163
|
## 🔐 Authentication
|
|
@@ -217,10 +230,22 @@ Rule of thumb: make the message something you would want to see at 03:00 in logs
|
|
|
217
230
|
<br>
|
|
218
231
|
|
|
219
232
|
|
|
233
|
+
## Autoregister Hooks
|
|
234
|
+
|
|
235
|
+
Exported functions starting with `autoregister_` automatically execute on startup and receive the Fastify `app` instance. This allows files to self-inject custom hooks, plugins, or configurations locally.
|
|
236
|
+
|
|
237
|
+
### Example
|
|
238
|
+
```javascript
|
|
239
|
+
// In any standard .js file
|
|
240
|
+
export async function autoregister_custom_auth(app) {
|
|
241
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
242
|
+
// Local hook logic here
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
|
|
220
248
|
|
|
221
|
-
## Note
|
|
222
|
-
On loading routes - possible side effects execute.
|
|
223
|
-
Means you can eg declare globals.
|
|
224
249
|
|
|
225
250
|
## About
|
|
226
251
|
- Dependencies: original fastify packages + scalar/fastify-api-reference
|
package/index.d.ts
CHANGED
|
@@ -8,16 +8,21 @@ declare global {
|
|
|
8
8
|
|
|
9
9
|
var global: typeof globalThis;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
var ENV: NodeJS.ProcessEnv & {
|
|
12
|
+
development: boolean;
|
|
13
|
+
production: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Casts a string ID to a MongoDB ObjectId using the global helper. */
|
|
12
17
|
var oid: (id?: string) => import("mongodb").ObjectId;
|
|
13
18
|
|
|
14
|
-
/** Access to the global MongoDB database instance
|
|
19
|
+
/** Access to the global MongoDB database instance. */
|
|
15
20
|
var db: import("mongodb").Db;
|
|
16
21
|
|
|
17
22
|
/**
|
|
18
|
-
* Creates an Error object with an attached HTTP status code
|
|
23
|
+
* Creates an Error object with an attached HTTP status code.
|
|
19
24
|
* Fastify catches this to return a structured JSON response.
|
|
20
|
-
*
|
|
25
|
+
* @param code The HTTP status code (e.g., 400, 401, 403, 404).
|
|
21
26
|
* @param message The specific failure reason returned in the JSON body.
|
|
22
27
|
*/
|
|
23
28
|
var createError: (code: number, message: string) => Error;
|
|
@@ -31,6 +36,7 @@ declare module "fastify" {
|
|
|
31
36
|
secret?: string;
|
|
32
37
|
mongodb?: string;
|
|
33
38
|
static?: string;
|
|
39
|
+
errorLogger?: boolean;
|
|
34
40
|
};
|
|
35
41
|
}
|
|
36
42
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -7,56 +7,54 @@ import {
|
|
|
7
7
|
setupMongo,
|
|
8
8
|
setupStatic,
|
|
9
9
|
setupCookie,
|
|
10
|
+
setupErrorLogger,
|
|
10
11
|
} from "./setup.js";
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
import { getAddress } from "./getAddress.js";
|
|
14
14
|
import { setupDocs } from "./setupDocs.js";
|
|
15
15
|
import { setupRoutes } from "./setupRoutes.js";
|
|
16
16
|
import { setupDevSounds } from "./devSounds.js";
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
import fastifyWebsocket from "@fastify/websocket";
|
|
20
19
|
|
|
21
20
|
export default async function maxserver(config = {}) {
|
|
22
|
-
|
|
23
|
-
|
|
24
21
|
const {
|
|
25
|
-
|
|
26
|
-
// maxserver options
|
|
27
22
|
port = Number(process.env.PORT || 3000),
|
|
28
23
|
secret = process.env.SECRET,
|
|
29
24
|
mongodb = process.env.MONGODB,
|
|
30
25
|
docs = process.env.DOCS !== "false",
|
|
31
26
|
cors = process.env.CORS || "*",
|
|
32
|
-
env = process.env.NODE_ENV || "development",
|
|
27
|
+
env = process.env.NODE_ENV || "development",
|
|
33
28
|
routesDir = process.env.ROUTESDIR || "src",
|
|
34
29
|
scalar = {},
|
|
35
30
|
openapiInfo,
|
|
36
31
|
sounds,
|
|
37
32
|
static: isStatic = process.env.STATIC,
|
|
38
33
|
public: isPublic = process.env.PUBLIC === "true",
|
|
34
|
+
errorLogger = process.env.ERROR_LOGGER === "true",
|
|
39
35
|
|
|
40
|
-
// everything else goes straight to Fastify
|
|
41
36
|
...fastifyOpts
|
|
42
|
-
|
|
43
37
|
} = config;
|
|
44
38
|
|
|
39
|
+
globalThis.ENV = {
|
|
40
|
+
...process.env,
|
|
41
|
+
development: process.env.NODE_ENV !== "production",
|
|
42
|
+
production: process.env.NODE_ENV === "production"
|
|
43
|
+
};
|
|
44
|
+
|
|
45
45
|
const maxserverConfig = {
|
|
46
46
|
port, secret, mongodb, docs, cors, env, openapiInfo, routesDir, scalar, sounds,
|
|
47
47
|
static: isStatic,
|
|
48
|
-
public: isPublic
|
|
48
|
+
public: isPublic,
|
|
49
|
+
errorLogger
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
if (!secret)
|
|
52
|
-
throw new Error("secret is must have");
|
|
53
|
-
|
|
52
|
+
if (!secret) throw new Error("secret is must have");
|
|
54
53
|
|
|
55
54
|
let app;
|
|
56
55
|
try {
|
|
57
56
|
app = Fastify({
|
|
58
57
|
trustProxy: true,
|
|
59
|
-
// Required to allow adding doc fields on schema
|
|
60
58
|
ajv: { customOptions: { strictSchema: false } },
|
|
61
59
|
...fastifyOpts
|
|
62
60
|
});
|
|
@@ -65,19 +63,19 @@ export default async function maxserver(config = {}) {
|
|
|
65
63
|
throw err;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
|
|
69
66
|
app.decorate("maxserver", maxserverConfig);
|
|
70
67
|
|
|
71
68
|
app.decorate("start", async function () {
|
|
72
69
|
const port = this.maxserver.port ?? 3000;
|
|
73
|
-
const host = this.maxserver.public ?
|
|
70
|
+
const host = this.maxserver.public ? "0.0.0.0" : "127.0.0.1";
|
|
74
71
|
await this.listen({ port, host });
|
|
75
|
-
console.log(
|
|
72
|
+
console.log("🟢 ", getAddress(this));
|
|
76
73
|
});
|
|
77
74
|
|
|
78
75
|
app.register(fastifyWebsocket);
|
|
79
76
|
|
|
80
77
|
await setupDevSounds(app);
|
|
78
|
+
await setupErrorLogger(app);
|
|
81
79
|
await setupCookie(app);
|
|
82
80
|
await setupHelmet(app);
|
|
83
81
|
await setupCors(app);
|
|
@@ -87,7 +85,6 @@ export default async function maxserver(config = {}) {
|
|
|
87
85
|
await setupDocs(app);
|
|
88
86
|
await setupRoutes(app);
|
|
89
87
|
|
|
90
|
-
|
|
91
88
|
global.createError = function (code, message) {
|
|
92
89
|
const err = new Error(message);
|
|
93
90
|
err.statusCode = code;
|
package/src/setup.js
CHANGED
|
@@ -8,8 +8,6 @@ import mongodb from "@fastify/mongodb";
|
|
|
8
8
|
import fastifyStatic from "@fastify/static";
|
|
9
9
|
import helmet from "@fastify/helmet";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
11
|
export async function setupHelmet(app) {
|
|
14
12
|
await app.register(helmet, {
|
|
15
13
|
contentSecurityPolicy: false,
|
|
@@ -20,16 +18,12 @@ export async function setupHelmet(app) {
|
|
|
20
18
|
});
|
|
21
19
|
}
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
21
|
export async function setupCors(app) {
|
|
26
22
|
const isProd = app.maxserver.env === "production";
|
|
27
23
|
let origin = app.maxserver.cors ?? "*";
|
|
28
24
|
|
|
29
|
-
// Fix: Credentials + "*" = Browser Error
|
|
30
|
-
// If no origin is defined in dev, we should allow the specific requester
|
|
31
25
|
if (origin === "*" && !isProd)
|
|
32
|
-
origin = true;
|
|
26
|
+
origin = true;
|
|
33
27
|
|
|
34
28
|
if (isProd && (origin === "*" || origin === true))
|
|
35
29
|
app.log.warn("CORS: allowing all origins in production with credentials is risky");
|
|
@@ -37,12 +31,10 @@ export async function setupCors(app) {
|
|
|
37
31
|
await app.register(cors, {
|
|
38
32
|
origin,
|
|
39
33
|
credentials: true,
|
|
40
|
-
methods: [
|
|
34
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"]
|
|
41
35
|
});
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
|
|
45
|
-
|
|
46
38
|
export async function setupCookie(app) {
|
|
47
39
|
await app.register(cookie, {
|
|
48
40
|
secret: app.maxserver.secret,
|
|
@@ -50,9 +42,6 @@ export async function setupCookie(app) {
|
|
|
50
42
|
});
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
45
|
export async function setupMongo(app) {
|
|
57
46
|
const url = app.maxserver.mongodb;
|
|
58
47
|
if (!url) return;
|
|
@@ -63,8 +52,6 @@ export async function setupMongo(app) {
|
|
|
63
52
|
global.db = db;
|
|
64
53
|
}
|
|
65
54
|
|
|
66
|
-
|
|
67
|
-
|
|
68
55
|
export async function setupJwt(app) {
|
|
69
56
|
await app.register(jwt, {
|
|
70
57
|
secret: app.maxserver.secret,
|
|
@@ -72,25 +59,17 @@ export async function setupJwt(app) {
|
|
|
72
59
|
});
|
|
73
60
|
|
|
74
61
|
app.addHook("preHandler", async function (req) {
|
|
75
|
-
|
|
76
|
-
// Let preflight requests pass
|
|
77
62
|
if (req.method === "OPTIONS") return;
|
|
78
63
|
|
|
79
64
|
const auth = req.routeOptions?.config?.auth;
|
|
80
|
-
|
|
81
65
|
if (!auth) return;
|
|
82
66
|
|
|
83
67
|
await req.jwtVerify();
|
|
84
68
|
const u = req.user;
|
|
85
69
|
req.userId = u?.sub || u?.userId || u?.userid || u?.id || null;
|
|
86
70
|
});
|
|
87
|
-
|
|
88
71
|
}
|
|
89
72
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
73
|
export async function setupStatic(app) {
|
|
95
74
|
const dir = app.maxserver.static;
|
|
96
75
|
if (!dir) return;
|
|
@@ -109,5 +88,17 @@ export async function setupStatic(app) {
|
|
|
109
88
|
await app.register(fastifyStatic, { root: abs });
|
|
110
89
|
}
|
|
111
90
|
|
|
91
|
+
export async function setupErrorLogger(app) {
|
|
92
|
+
if (!app.maxserver.errorLogger) return;
|
|
112
93
|
|
|
94
|
+
app.addHook("onError", async (req, res, error) => {
|
|
95
|
+
console.log("\n‼️ ERROR ‼️");
|
|
113
96
|
|
|
97
|
+
const stackLine = error.stack.split("\n")[1];
|
|
98
|
+
const match = stackLine.match(/([^\s()]+):(\d+):\d+/);
|
|
99
|
+
const file = match?.[1].replace("file://" + process.cwd(), "");
|
|
100
|
+
|
|
101
|
+
if (file) console.log(`${file} -> line ${match[2]}`);
|
|
102
|
+
console.log(error.message);
|
|
103
|
+
});
|
|
104
|
+
}
|
package/src/setupDocs.js
CHANGED
|
@@ -2,37 +2,6 @@ import swagger from "@fastify/swagger";
|
|
|
2
2
|
import apiReference from "@scalar/fastify-api-reference";
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
/*
|
|
6
|
-
const schema = {
|
|
7
|
-
summary: "OpenAPI Specification",
|
|
8
|
-
description: "Returns the full OpenAPI 3.0 specification.",
|
|
9
|
-
tags: ["Docs"],
|
|
10
|
-
response: {
|
|
11
|
-
200: {
|
|
12
|
-
type: "object",
|
|
13
|
-
additionalProperties: true,
|
|
14
|
-
required: ["openapi", "info", "paths"],
|
|
15
|
-
properties: {
|
|
16
|
-
openapi: { type: "string", example: "3.0.3" },
|
|
17
|
-
info: {
|
|
18
|
-
type: "object",
|
|
19
|
-
required: ["title", "version"],
|
|
20
|
-
properties: {
|
|
21
|
-
title: { type: "string", example: "API" },
|
|
22
|
-
version: { type: "string", example: "1.0.0" }
|
|
23
|
-
}
|
|
24
|
-
},
|
|
25
|
-
paths: { type: "object", example: {} }
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
5
|
export async function setupDocs(app) {
|
|
37
6
|
|
|
38
7
|
const info = app.maxserver.openapiInfo || {
|
|
@@ -76,7 +45,7 @@ export async function setupDocs(app) {
|
|
|
76
45
|
persistAuth: true,
|
|
77
46
|
showDeveloperTools: "never",
|
|
78
47
|
//"expandAllModelSections": true,
|
|
79
|
-
operationsSorter: "alpha",
|
|
48
|
+
// operationsSorter: "alpha",
|
|
80
49
|
orderSchemaPropertiesBy: "preserve",
|
|
81
50
|
metaData: {
|
|
82
51
|
title: "API Docs 👨💻",
|
package/src/setupRoutes.js
CHANGED
|
@@ -54,64 +54,77 @@ function getRoute(file) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* Safe dynamic import for ESM.
|
|
59
|
-
*/
|
|
60
|
-
async function importDefault(file) {
|
|
61
|
-
return (await import(pathToFileURL(file).href)).default;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
57
|
export async function setupRoutes(app) {
|
|
65
58
|
const root = path.resolve(app.maxserver.routesDir || "src");
|
|
66
59
|
const files = walk(root);
|
|
67
60
|
|
|
68
|
-
// 1. Pass One: Register
|
|
61
|
+
// 1. Pass One: Register Named Exports Globally
|
|
62
|
+
const globals = new Map();
|
|
69
63
|
for (const file of files) {
|
|
70
|
-
|
|
71
|
-
if (file.endsWith(".schema.js") && !fs.existsSync(file.replace(".schema.js", ".js"))) {
|
|
72
|
-
const mod = await import(pathToFileURL(file).href);
|
|
64
|
+
if (file.endsWith(".schema.js")) continue;
|
|
73
65
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
66
|
+
const mod = await import(pathToFileURL(file).href);
|
|
67
|
+
for (const [key, val] of Object.entries(mod)) {
|
|
68
|
+
if (key === "default" || key.startsWith("autoregister_")) continue;
|
|
69
|
+
|
|
70
|
+
if (globals.has(key)) {
|
|
71
|
+
console.error("\n❌ Global Identifier Conflict!");
|
|
72
|
+
console.error(`The export "${key}" is defined in multiple files:`);
|
|
73
|
+
console.error(` -> ${globals.get(key)}`);
|
|
74
|
+
console.error(` -> ${file}\n`);
|
|
75
|
+
throw new Error(`Duplicate global identifier "${key}"`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
globals.set(key, file);
|
|
79
|
+
global[key] = val;
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
// 2. Pass Two:
|
|
83
|
+
// 2. Pass Two: Register Global Schemas (Lonely .schema.js files)
|
|
81
84
|
for (const file of files) {
|
|
85
|
+
const isLonely = file.endsWith(".schema.js") &&
|
|
86
|
+
!fs.existsSync(file.replace(".schema.js", ".js"));
|
|
87
|
+
|
|
88
|
+
if (!isLonely) continue;
|
|
89
|
+
|
|
90
|
+
const mod = await import(pathToFileURL(file).href);
|
|
91
|
+
for (const schema of Object.values(mod))
|
|
92
|
+
if (schema?.$id) app.addSchema(schema);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 3. Pass Three: Auto-register hooks
|
|
96
|
+
for (const file of files) {
|
|
97
|
+
if (file.endsWith(".schema.js")) continue;
|
|
98
|
+
|
|
82
99
|
const mod = await import(pathToFileURL(file).href);
|
|
83
100
|
for (const [key, fn] of Object.entries(mod))
|
|
84
101
|
if (key.startsWith("autoregister_") && typeof fn === "function") await fn(app);
|
|
85
102
|
}
|
|
86
103
|
|
|
87
|
-
//
|
|
88
|
-
const
|
|
104
|
+
// 4. Pass Four: Collect and Group Routes by Directory
|
|
105
|
+
const groups = new Map();
|
|
89
106
|
for (const file of files) {
|
|
90
107
|
if (file.endsWith(".schema.js")) continue;
|
|
91
108
|
|
|
92
109
|
const info = getRoute(file);
|
|
93
110
|
if (!info) continue;
|
|
94
111
|
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
seen.set(key, file);
|
|
98
|
-
|
|
99
|
-
const handler = await importDefault(file);
|
|
112
|
+
const mod = await import(pathToFileURL(file).href);
|
|
113
|
+
const handler = mod.default;
|
|
100
114
|
if (typeof handler !== "function") {
|
|
101
|
-
throw new Error(`Route
|
|
115
|
+
throw new Error(`Route in "${file}" must export a default function.`);
|
|
102
116
|
}
|
|
103
117
|
|
|
104
118
|
const schemaFile = file.replace(/\.js$/, ".schema.js");
|
|
105
119
|
let raw = {};
|
|
106
120
|
|
|
107
121
|
if (fs.existsSync(schemaFile)) {
|
|
108
|
-
const loaded = await
|
|
122
|
+
const loaded = (await import(pathToFileURL(schemaFile).href)).default;
|
|
109
123
|
if (loaded && typeof loaded === "object") raw = loaded;
|
|
110
124
|
}
|
|
111
125
|
|
|
112
|
-
let { auth, routeOptions = {}, ...schema } = raw;
|
|
126
|
+
let { auth, order = 999, routeOptions = {}, ...schema } = raw;
|
|
113
127
|
|
|
114
|
-
// Inject 'auth' config for the global authentication hook
|
|
115
128
|
if (auth !== undefined) {
|
|
116
129
|
routeOptions = {
|
|
117
130
|
...routeOptions,
|
|
@@ -119,6 +132,33 @@ export async function setupRoutes(app) {
|
|
|
119
132
|
};
|
|
120
133
|
}
|
|
121
134
|
|
|
122
|
-
|
|
135
|
+
const dir = path.dirname(file);
|
|
136
|
+
if (!groups.has(dir)) groups.set(dir, []);
|
|
137
|
+
|
|
138
|
+
groups.get(dir).push({
|
|
139
|
+
method: info.method,
|
|
140
|
+
url: info.url,
|
|
141
|
+
options: { ...routeOptions, schema },
|
|
142
|
+
handler,
|
|
143
|
+
order,
|
|
144
|
+
file
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Sort routes locally within each directory
|
|
149
|
+
const routes = [];
|
|
150
|
+
for (const list of groups.values()) {
|
|
151
|
+
list.sort((a, b) => a.order - b.order);
|
|
152
|
+
routes.push(...list);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 5. Pass Five: Register Sorted Routes
|
|
156
|
+
const seen = new Map();
|
|
157
|
+
for (const r of routes) {
|
|
158
|
+
const key = `${r.method} ${r.url}`;
|
|
159
|
+
if (seen.has(key)) throw new Error(`Duplicate route "${key}" detected.`);
|
|
160
|
+
seen.set(key, r.file);
|
|
161
|
+
|
|
162
|
+
app[r.method](r.url, r.options, r.handler);
|
|
123
163
|
}
|
|
124
164
|
}
|
package/templates/package.json
CHANGED
|
@@ -9,20 +9,5 @@
|
|
|
9
9
|
},
|
|
10
10
|
"type": "module",
|
|
11
11
|
"private": "true",
|
|
12
|
-
"dependencies": {}
|
|
13
|
-
"keywords": [
|
|
14
|
-
"fastify",
|
|
15
|
-
"backend",
|
|
16
|
-
"framework",
|
|
17
|
-
"node-server",
|
|
18
|
-
"api-server",
|
|
19
|
-
"auto-routes",
|
|
20
|
-
"automatic-docs",
|
|
21
|
-
"scalar",
|
|
22
|
-
"openapi",
|
|
23
|
-
"jwt-auth",
|
|
24
|
-
"mongodb",
|
|
25
|
-
"minimalist",
|
|
26
|
-
"zero-boilerplate"
|
|
27
|
-
]
|
|
12
|
+
"dependencies": {}
|
|
28
13
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
$id: "User",
|
|
3
|
+
summary: "User Profile",
|
|
4
|
+
description: "Internal user profile including team memberships.",
|
|
5
|
+
tags: ["User"],
|
|
6
|
+
auth: true,
|
|
7
|
+
type: "object",
|
|
8
|
+
additionalProperties: false,
|
|
9
|
+
properties: {
|
|
10
|
+
id: { type: "string" },
|
|
11
|
+
email: { type: "string" },
|
|
12
|
+
}
|
|
13
|
+
};
|