Skip to content

Commit

Permalink
refactor: use fastify (#661)
Browse files Browse the repository at this point in the history
  • Loading branch information
trim21 authored Dec 6, 2023
1 parent 6d05954 commit a570469
Show file tree
Hide file tree
Showing 12 changed files with 1,301 additions and 2,367 deletions.
3,113 changes: 1,047 additions & 2,066 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@
"@emotion/babel-plugin": "^11.11.0",
"@emotion/css": "^11.11.0",
"@emotion/react": "^11.11.1",
"@fastify/express": "^2.3.0",
"@fastify/static": "^6.10.2",
"@lingui/loader": "^3.17.2",
"@lingui/react": "^3.17.2",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
Expand All @@ -92,7 +94,6 @@
"@types/cookie-parser": "^1.4.3",
"@types/create-torrent": "^5.0.0",
"@types/d3": "^7.4.0",
"@types/debug": "^4.1.8",
"@types/express": "^4.17.17",
"@types/fs-extra": "^9.0.13",
"@types/geoip-country": "^4.0.0",
Expand All @@ -104,7 +105,7 @@
"@types/node": "^12.20.55",
"@types/parse-torrent": "^5.8.4",
"@types/passport": "^1.0.12",
"@types/passport-jwt": "^3.0.8",
"@types/passport-jwt": "^3.0.9",
"@types/react": "^18.2.11",
"@types/react-dom": "^18.2.4",
"@types/react-measure": "^2.0.8",
Expand Down Expand Up @@ -136,8 +137,6 @@
"d3-scale": "^4.0.2",
"d3-selection": "^3.0.0",
"d3-shape": "^3.2.0",
"debug": "^4.3.4",
"esbuild-jest": "^0.5.0",
"eslint": "^8.42.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^17.0.0",
Expand All @@ -149,6 +148,8 @@
"express-rate-limit": "^6.7.0",
"fast-json-patch": "^3.1.1",
"fast-sort": "^3.4.0",
"fastify": "^4.21.0",
"fastify-type-provider-zod": "^1.1.9",
"feedsub": "^0.7.8",
"file-loader": "^6.2.0",
"form-data": "^4.0.0",
Expand All @@ -159,6 +160,7 @@
"html-webpack-plugin": "^5.5.3",
"http-errors": "^2.0.0",
"jest": "^28.1.3",
"jest-esbuild": "^0.3.0",
"js-file-download": "^0.4.12",
"jsonwebtoken": "^9.0.0",
"lodash": "^4.17.21",
Expand All @@ -176,7 +178,6 @@
"postcss": "^8.4.24",
"postcss-loader": "^7.3.3",
"prettier": "^2.8.8",
"promise": "^8.3.0",
"react": "^18.2.0",
"react-dev-utils": "^12.0.1",
"react-dom": "^18.2.0",
Expand Down
2 changes: 1 addition & 1 deletion server/.jest/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module.exports = {
transform: {
// transform ESM only package to CommonJS
'^.+\\.(t|j)sx?$': [
'esbuild-jest',
'jest-esbuild',
{
format: 'cjs',
},
Expand Down
129 changes: 0 additions & 129 deletions server/app.ts

This file was deleted.

7 changes: 2 additions & 5 deletions server/bin/start.ts
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import chalk from 'chalk';

import enforcePrerequisites from './enforce-prerequisites';
import migrateData from './migrations/run';
import startWebServer from './web-server';

if (process.env.NODE_ENV == 'production') {
// Catch unhandled rejections and exceptions
Expand All @@ -26,11 +27,7 @@ if (process.env.NODE_ENV == 'production') {

enforcePrerequisites()
.then(migrateData)
.then(() => {
// We do this because we don't want the side effects of importing server functions before migration is completed.
const startWebServer = require('./web-server').default; // eslint-disable-line @typescript-eslint/no-var-requires
return startWebServer();
})
.then(startWebServer)
.catch((error) => {
console.log(chalk.red('Failed to start Flood:'));
console.trace(error);
Expand Down
105 changes: 25 additions & 80 deletions server/bin/web-server.ts
Original file line number Diff line number Diff line change
@@ -1,106 +1,51 @@
import chalk from 'chalk';
import debug from 'debug';
import fastify from 'fastify';
import fs from 'fs';
import http from 'http';
import https from 'https';

import app from '../app';
import type {FastifyInstance} from 'fastify';
import type {Http2SecureServer} from 'http2';
import type {Server} from 'http';

import config from '../../config';
import constructRoutes from '../routes';
import packageJSON from '../../package.json';

const debugFloodServer = debug('flood:server');

// Normalize a port into a number, string, or false.
const normalizePort = (val: string | number): string | number => {
const port = parseInt(val as string, 10);

// Named pipe.
if (Number.isNaN(port)) {
return val;
}

// Port number.
if (port >= 0) {
return port;
}

console.error('Unexpected port or pipe');
process.exit(1);
};

const startWebServer = () => {
const port = normalizePort(config.floodServerPort);
const host = config.floodServerHost;
const useSSL = config.ssl ?? false;

app.set('port', port);
app.set('host', host);
const startWebServer = async () => {
const {ssl = false, floodServerHost: host, floodServerPort: port} = config;

// Create HTTP or HTTPS server.
let server: http.Server | https.Server;
let instance: FastifyInstance<Http2SecureServer> | FastifyInstance<Server>;

if (useSSL) {
if (ssl) {
if (!config.sslKey || !config.sslCert) {
console.error('Cannot start HTTPS server, `sslKey` or `sslCert` is missing in config.js.');
process.exit(1);
}

server = https.createServer(
{
instance = fastify({
bodyLimit: 100 * 1024 * 1024,
trustProxy: 'loopback',
http2: true,
https: {
allowHTTP1: true,
key: fs.readFileSync(config.sslKey),
cert: fs.readFileSync(config.sslCert),
},
app,
);
});
} else {
server = http.createServer(app);
instance = fastify({
bodyLimit: 100 * 1024 * 1024,
trustProxy: 'loopback',
});
}

const handleError = (error: NodeJS.ErrnoException) => {
if (error.syscall !== 'listen') {
throw error;
}

const bind = typeof port === 'string' ? `Pipe ${port}` : `Port ${port}`;
await constructRoutes(instance as FastifyInstance);

// Handle specific listen errors with friendly messages.
switch (error.code) {
case 'EACCES':
console.error(`${bind} requires elevated privileges`);
process.exit(1);
case 'EADDRINUSE':
console.error(`${bind} is already in use`);
process.exit(1);
default:
throw error;
}
};

// Event listener for HTTP server "listening" event.
const handleListening = () => {
const addr = server.address();
if (addr == null) {
console.error('Unable to get listening address.');
process.exit(1);
}
const bind = typeof addr === 'string' ? `pipe ${addr}` : `port ${addr.port}`;
debugFloodServer(`Listening on ${bind}`);
};

// Listen on provided port, on all network interfaces.
if (typeof port === 'string') {
server.listen(port);
await instance.listen({path: port});
} else {
server.listen(port, host);
await instance.listen({port, host});
}

server.on('error', handleError);
server.on('listening', handleListening);
process.on('exit', () => {
server.close();
});

const address = chalk.underline(typeof port === 'string' ? port : `${useSSL ? 'https' : 'http'}://${host}:${port}`);
const address = chalk.underline(`${ssl ? 'https' : 'http'}://${host}:${port}`);

console.log(chalk.green(`Flood server ${packageJSON.version} starting on ${address}\n`));

Expand Down
18 changes: 15 additions & 3 deletions server/routes/api/auth.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import crypto from 'crypto';
import fastify from 'fastify';
import supertest from 'supertest';

import {AccessLevel} from '../../../shared/schema/constants/Auth';

import app from '../../app';
import constructRoutes from '..';
import {getAuthToken} from '../../util/authUtil';

import type {
Expand All @@ -13,8 +14,6 @@ import type {
} from '../../../shared/schema/api/auth';
import type {ClientConnectionSettings} from '../../../shared/schema/ClientConnectionSettings';

const request = supertest(app);

const testConnectionSettings: ClientConnectionSettings = {
client: 'rTorrent',
type: 'socket',
Expand All @@ -38,6 +37,19 @@ const testNonAdminUser = {
} as const;
let testNonAdminUserToken = '';

const app = fastify({disableRequestLogging: true, logger: false});
let request: supertest.SuperTest<supertest.Test>;

beforeAll(async () => {
await constructRoutes(app);
await app.ready();
request = supertest(app.server);
});

afterAll(async () => {
await app.close();
});

describe('GET /api/auth/verify (initial)', () => {
it('Verify without credential', (done) => {
request
Expand Down
Loading

0 comments on commit a570469

Please sign in to comment.