-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
server.js
148 lines (129 loc) · 4.81 KB
/
server.js
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
const express = require('express');
const fs = require('fs');
const path = require('path');
const cors = require('cors');
const historyApiFallback = require('connect-history-api-fallback');
require('dotenv').config();
const app = express();
const port = process.env.PORT || 3000; // The port to run the server on
const API_DIR = '/api'; // Name of the dir containing the lambda functions
const dirPath = path.join(__dirname, API_DIR); // Path to the lambda functions dir
const guiPath = path.join(__dirname, 'build');
const placeholderFilePath = path.join(__dirname, 'public', 'placeholder.html');
const handlers = {}; // Will store list of API endpoints
process.env.WC_SERVER = 'true'; // Tells middleware to return in non-lambda mode
// Enable CORS
app.use(cors({
origin: process.env.API_CORS_ORIGIN || '*',
}));
// Read and register each API function as an Express routes
fs.readdirSync(dirPath, { withFileTypes: true })
.filter(dirent => dirent.isFile() && dirent.name.endsWith('.js'))
.forEach(dirent => {
const routeName = dirent.name.split('.')[0];
const route = `${API_DIR}/${routeName}`;
const handler = require(path.join(dirPath, dirent.name));
handlers[route] = handler;
app.get(route, async (req, res) => {
try {
await handler(req, res);
} catch (err) {
res.status(500).json({ error: err.message });
}
});
});
// Create a single API endpoint to execute all lambda functions
app.get('/api', async (req, res) => {
const results = {};
const { url } = req.query;
const maxExecutionTime = process.env.API_TIMEOUT_LIMIT || 20000;
const executeHandler = async (handler, req, res) => {
return new Promise(async (resolve, reject) => {
try {
const mockRes = {
status: (statusCode) => mockRes,
json: (body) => resolve({ body }),
};
await handler({ ...req, query: { url } }, mockRes);
} catch (err) {
reject(err);
}
});
};
const timeout = (ms, jobName = null) => {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(
`Timed out after ${ms/1000} seconds${jobName ? `, when executing ${jobName}` : ''}`
));
}, ms);
});
};
const handlerPromises = Object.entries(handlers).map(async ([route, handler]) => {
const routeName = route.replace(`${API_DIR}/`, '');
try {
const result = await Promise.race([
executeHandler(handler, req, res),
timeout(maxExecutionTime, routeName)
]);
results[routeName] = result.body;
} catch (err) {
results[routeName] = { error: err.message };
}
});
await Promise.all(handlerPromises);
res.json(results);
});
// Handle SPA routing
app.use(historyApiFallback({
rewrites: [
{ from: /^\/api\/.*$/, to: (context) => context.parsedUrl.path },
]
}));
// Serve up the GUI - if build dir exists, and GUI feature enabled
if (process.env.DISABLE_GUI && process.env.DISABLE_GUI !== 'false') {
app.get('*', async (req, res) => {
const placeholderContent = await fs.promises.readFile(placeholderFilePath, 'utf-8');
const htmlContent = placeholderContent.replace(
'<!-- CONTENT -->',
'Web-Check API is up and running!<br />Access the endpoints at '
+'<a href="/api"><code>/api</code></a>'
);
res.status(500).send(htmlContent);
});
} else if (!fs.existsSync(guiPath)) {
app.get('*', async (req, res) => {
const placeholderContent = await fs.promises.readFile(placeholderFilePath, 'utf-8');
const htmlContent = placeholderContent.replace(
'<!-- CONTENT -->',
'Looks like the GUI app has not yet been compiled.<br /> ' +
'Run <code>yarn build</code> to continue, then restart the server.'
);
res.status(500).send(htmlContent);
});
} else { // GUI enabled, and build files present, let's go!!
app.use(express.static(guiPath));
}
app.use((req, res, next) => {
res.status(404).sendFile(path.join(__dirname, 'public', 'error.html'));
});
// Print nice welcome message to user
const printMessage = () => {
console.log(
`\x1b[36m\n` +
' __ __ _ ___ _ _ \n' +
' \\ \\ / /__| |__ ___ / __| |_ ___ __| |__\n' +
' \\ \\/\\/ / -_) \'_ \\___| (__| \' \\/ -_) _| / /\n' +
' \\_/\\_/\\___|_.__/ \\___|_||_\\___\\__|_\\_\\\n' +
`\x1b[0m\n`,
`\x1b[1m\x1b[32m🚀 Web-Check is up and running at http://localhost:${port} \x1b[0m\n\n`,
`\x1b[2m\x1b[36m🛟 For documentation and support, visit the GitHub repo: ` +
`https://github.com/lissy93/web-check \n`,
`💖 Found Web-Check useful? Consider sponsoring us on GitHub ` +
`to help fund maintenance & development.\x1b[0m`
);
};
// Create server
app.listen(port, () => {
printMessage();
});