-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
190 lines (167 loc) · 4.8 KB
/
index.js
File metadata and controls
190 lines (167 loc) · 4.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
global.ver = "1.0.0";
import express from "express";
import rateLimit from "express-rate-limit";
import cors from "cors";
import * as cron from "node-cron";
import swaggerJsdoc from "swagger-jsdoc";
import swaggerUi from "swagger-ui-express";
import { db } from "./app/models/index.js";
import { prettyResponse } from "./app/middleware/index.js";
import { authRoutes, userRoutes, groupRoutes, permissionRoutes } from "./app/routes/index.js";
import { css } from "./app/config/swagger.config.js";
import * as initiate from "./app/setup/index.js";
const { renewJwt: RenewJwt } = db;
import dotenv from "dotenv";
dotenv.config();
/**
* ##
* EXPRESS: app setup
* ##
*/
const app = express();
// CORS : Configure through which hosts communication with the API can be made (frontend IPs or hosts)
const corsOptions = {
origin: [
"https://your.website.tld",
"http://mirror-of.website.tld:8090"
],
};
app.use(cors(corsOptions));
// format responses in a pretty manner
// this is calling a custom module meant to normalise exchanges
app.use(prettyResponse);
// parse requests of content-type - application/json
app.use(express.json());
// return error response if parse failed
app.use((error, req, res, next) => {
if (error instanceof SyntaxError && error.status === 400 && "body" in error) {
res.sendApiResponse("NOT_ACCEPTABLE", null, "Invalid body format");
} else {
// additional error handling required when this occurs :)
console.error("---\nUNCAUGHT ERROR!!\n", error, "\n---\n");
res.sendApiResponse("FATAL", null, "UNCAUGHT ERROR");
}
next();
});
// RATE-LIMIT : limit requests of each IP
app.use(
rateLimit({
windowMs: (parseInt(process.env.REQUEST_LIMIT_ALL_WINDOW) || 5) * 60 * 1000, // 5 minutes in milliseconds or env setting
max: parseInt(process.env.REQUEST_LIMIT_ALL_MAX) || 100, // 100 requests or env setting
handler: (req, res) => {
res.sendApiResponse("TOO_MANY_REQUESTS");
},
})
);
// parse requests of content-type - application/x-www-form-urlencoded
// this is used for reading params passed in the URL, usually in GET requests when listing elements
app.use(express.urlencoded({ extended: true }));
/**
* ##
* MySQL: Connect to database and configure if first connection
* ##
*/
db.database
.sync()
.then(async () => {
await initiate.groupSetup();
await initiate.resourceSetup();
await initiate.resourcePermissionSetup();
await initiate.adminGroupPermissionSetup();
})
.catch((err) => {
console.error("Database connection failed.", err);
});
/**
* ##
* SWAGGER: Generate a swagger compatible documentation from all JSDoc annotated routes
* ##
*/
const [API_NAME, API_DESCRIPTION, API_VERSION, API_CREATOR] = [
process.env.API_NAME,
process.env.API_DESCRIPTION,
global.ver,
process.env.API_CREATOR,
];
const swg = {
definition: {
openapi: "3.0.0",
info: {
title: API_NAME,
description: API_DESCRIPTION,
version: API_VERSION,
contact: {
email: API_CREATOR,
},
license: {
name: "MIT",
url: "https://opensource.org/licenses/MIT",
},
},
components: {
securitySchemes: {
bearerAuth: {
type: "http",
scheme: "bearer",
bearerFormat: "JWT",
},
},
},
},
apis: ["./index.js", "./app/routes/*.routes.js"], // files containing annotations as below (GET /)
};
const swgOptions = {
customCss: css,
};
/**
* ##
* ROUTING: Declare the routes available on the API
* ##
*/
/**
* @openapi
* /:
* get:
* summary: to confirm the API runs (it does if you can see this)
* tags:
* - _
* responses:
* 200:
* description: Returns a confirmation message.
*/
app.get("/", (req, res) => {
res.sendApiResponse("RUNNING");
});
// API functional routes
authRoutes(app);
userRoutes(app);
groupRoutes(app);
permissionRoutes(app);
// Swagger documentation page (turned off, please check .env file at project root)
if (parseInt(process.env.SWAGGER_DOC_AVAILABLE)) {
app.use(process.env.SWAGGER_DOC_ENDPOINT, swaggerUi.serve, swaggerUi.setup(swaggerJsdoc(swg), swgOptions));
console.log(`Swagger documentation available at endpoint '${process.env.SWAGGER_DOC_ENDPOINT}'`);
}
// Fallback route
app.all("*", (req, res) => {
res.sendApiResponse("DAFUQ");
});
/**
* ##
* RUN: Make the API reachable on the specified port
* ##
*/
const PORT = parseInt(process.env.API_PORT) || 8080;
app.listen(PORT, () => {
console.log(`The API is now running on port '${PORT}'`);
});
/**
* ##
* FLUSH: Delete expired renew tokens older than 90 days every night at 2 a.m.
* ##
*/
const FLUSHRENEW_CRON = process.env.DB_FLUSH_RENEWJWT_CRON;
const FLUSHRENEW_AFTER = parseInt(process.env.DB_FLUSH_RENEWJWT_AFTER);
cron.schedule(FLUSHRENEW_CRON, () => {
RenewJwt.flushExpiredTokens(FLUSHRENEW_AFTER);
});