mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
115 lines
3.7 KiB
JavaScript
115 lines
3.7 KiB
JavaScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
/* eslint-disable no-console */
|
|
|
|
// A tiny file server that serves the contents of the `asset` directory for use by tests for things like link previews
|
|
// and Markdown images that need an external server which the MM server can contact.
|
|
//
|
|
// Run standalone with `npm run start:file-server-mock`, or fork it from a spec. When forked with an IPC channel,
|
|
// it posts a {type: 'listening', port} message once it is ready so the parent can learn the port (use PORT=0 to get
|
|
// an ephemeral one).
|
|
|
|
const {createServer} = require('http');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const PORT = Number(process.env.PORT) || 3011;
|
|
const ASSET_DIR = process.env.ASSET_DIR || path.join(__dirname, 'asset');
|
|
|
|
if (process.argv[2]) {
|
|
process.title = process.argv[2];
|
|
}
|
|
|
|
const CONTENT_TYPES = {
|
|
'.png': 'image/png',
|
|
'.jpg': 'image/jpeg',
|
|
'.jpeg': 'image/jpeg',
|
|
'.gif': 'image/gif',
|
|
'.svg': 'image/svg+xml',
|
|
'.txt': 'text/plain; charset=utf-8',
|
|
'.html': 'text/html; charset=utf-8',
|
|
'.json': 'application/json',
|
|
'.zip': 'application/zip',
|
|
};
|
|
|
|
function setCorsHeaders(res) {
|
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
|
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Range');
|
|
}
|
|
|
|
const server = createServer((req, res) => {
|
|
const method = req.method;
|
|
let requestPath;
|
|
try {
|
|
requestPath = decodeURIComponent(req.url.split('?')[0]);
|
|
} catch (err) {
|
|
console.log(`[mock-file-server] Invalid request URL ${req.url.split('?')[0]}:`, err);
|
|
res.writeHead(400);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
// Handle CORS preflight
|
|
if (method === 'OPTIONS') {
|
|
setCorsHeaders(res);
|
|
res.writeHead(204);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
console.log(`[mock-file-server] ${method} ${requestPath}`);
|
|
|
|
if (method !== 'GET' && method !== 'HEAD') {
|
|
res.writeHead(405);
|
|
res.end('Method not allowed');
|
|
return;
|
|
}
|
|
|
|
// Resolve the requested file within ASSET_DIR, rejecting anything that escapes it (path traversal).
|
|
const relativePath = requestPath.replace(/^\/+/, '');
|
|
const filePath = path.join(ASSET_DIR, relativePath);
|
|
if (filePath !== ASSET_DIR && !filePath.startsWith(ASSET_DIR + path.sep)) {
|
|
res.writeHead(403);
|
|
res.end('Forbidden');
|
|
return;
|
|
}
|
|
|
|
fs.stat(filePath, (err, stats) => {
|
|
if (err || !stats.isFile()) {
|
|
console.log(`[mock-file-server] 404: ${requestPath}`);
|
|
res.writeHead(404);
|
|
res.end('Not found');
|
|
return;
|
|
}
|
|
|
|
setCorsHeaders(res);
|
|
const ext = path.extname(filePath).toLowerCase();
|
|
res.setHeader('Content-Type', CONTENT_TYPES[ext] || 'application/octet-stream');
|
|
res.setHeader('Content-Length', stats.size);
|
|
|
|
if (method === 'HEAD') {
|
|
res.writeHead(200);
|
|
res.end();
|
|
return;
|
|
}
|
|
|
|
res.writeHead(200);
|
|
fs.createReadStream(filePath).pipe(res);
|
|
});
|
|
});
|
|
|
|
// Bind to all interfaces, not just loopback, so the Mattermost server container can reach this
|
|
// over the Docker network in `testcontainers` mode.
|
|
server.listen(PORT, () => {
|
|
const address = server.address();
|
|
const actualPort = typeof address === 'object' && address ? address.port : PORT;
|
|
console.log(`File server serving ${ASSET_DIR} on port ${actualPort}!`);
|
|
|
|
// Let the parent process know the server is ready and which port it's on
|
|
if (process.send) {
|
|
process.send({type: 'listening', port: actualPort});
|
|
}
|
|
});
|