refactor(server): clean up server code

This commit is contained in:
diced 2022-06-20 17:09:52 -07:00
parent 4442c85dc1
commit 195c57edc3
No known key found for this signature in database
GPG key ID: 370BD1BA142842D1
3 changed files with 190 additions and 189 deletions

View file

@ -17,7 +17,6 @@ const { rm } = require('fs/promises');
treeShaking: true, treeShaking: true,
entryPoints: [ entryPoints: [
'src/server/index.ts', 'src/server/index.ts',
'src/server/server.ts',
'src/server/util.ts', 'src/server/util.ts',
'src/lib/logger.ts', 'src/lib/logger.ts',
'src/lib/config.ts', 'src/lib/config.ts',
@ -36,6 +35,6 @@ const { rm } = require('fs/promises');
watch, watch,
incremental: watch, incremental: watch,
sourcemap: false, sourcemap: false,
minify: process.env.NODE_ENV === 'production', minify: true,
}); });
})(); })();

View file

@ -1,7 +1,192 @@
import { version } from '../../package.json'; import Router from 'find-my-way';
import next from 'next';
import { NextServer, RequestHandler } from 'next/dist/server/next';
import { Image, PrismaClient } from '@prisma/client';
import { createServer, IncomingMessage, OutgoingMessage, ServerResponse } from 'http';
import { extname } from 'path';
import { mkdir } from 'fs/promises';
import { getStats, log, migrations } from './util';
import Logger from '../lib/logger'; import Logger from '../lib/logger';
import mimes from '../../scripts/mimes';
import exts from '../../scripts/exts';
import { version } from '../../package.json';
import config from '../lib/config';
import datasource from '../lib/datasource';
Logger.get('server').info(`starting zipline@${version} server`); const logger = Logger.get('server');
logger.info(`starting zipline@${version} server`);
import Server from './server'; start();
new Server();
async function start() {
// annoy user if they didnt change secret from default "changethis"
if (config.core.secret === 'changethis') {
logger.error('Secret is not set!');
logger.error('Running Zipline as is, without a randomized secret is not recommended and leaves your instance at risk!');
logger.error('Please change your secret in the config file or environment variables.');
logger.error('The config file is located at `config.toml`, or if using docker-compose you can change the variables in the `docker-compose.yml` file.');
logger.error('It is recomended to use a secret that is alphanumeric and randomized. A way you can generate this is through a password manager you may have.');
process.exit(1);
};
const dev = process.env.NODE_ENV === 'development';
process.env.DATABASE_URL = config.core.database_url;
await migrations();
const prisma = new PrismaClient();
if (config.datasource.type === 'local') {
await mkdir(config.datasource.local.directory, { recursive: true });
}
const nextServer = next({
dir: '.',
dev,
quiet: !dev,
hostname: config.core.host,
port: config.core.port,
});
const handle = nextServer.getRequestHandler();
const router = Router({
defaultRoute: (req, res) => {
handle(req, res);
},
});
router.on('GET', config.uploader.route === '/' ? '/:id(^[^\\.]+\\.[^\\.]+)' : `${config.uploader.route}/:id`, async (req, res, params) => {
const image = await prisma.image.findFirst({
where: {
OR: [
{ file: params.id },
{ invisible: { invis: decodeURI(params.id) } },
],
},
});
console.log(image);
if (!image) await rawFile(req, res, nextServer, params.id);
if (image.password) await handle(req, res);
else if (image.embed) await handle(req, res);
else await fileDb(req, res, nextServer, prisma, handle, image);
});
router.on('GET', '/r/:id', async (req, res, params) => {
const image = await prisma.image.findFirst({
where: {
OR: [
{ file: params.id },
{ invisible: { invis: decodeURI(params.id) } },
],
},
});
if (!image) await rawFile(req, res, nextServer, params.id);
if (image.password) await handle(req, res);
else await rawFileDb(req, res, nextServer, prisma, image);
});
await nextServer.prepare();
const http = createServer((req, res) => {
router.lookup(req, res);
if (config.core.logger) log(req.url);
});
http.on('error', (e) => {
logger.error(e);
process.exit(1);
});
http.on('listening', () => {
logger.info(`Listening on ${config.core.host}:${config.core.port}`);
});
http.listen(config.core.port, config.core.host ?? '0.0.0.0');
stats(prisma);
}
async function rawFile(
req: IncomingMessage,
res: OutgoingMessage,
nextServer: NextServer,
id: string,
) {
const data = datasource.get(id);
if (!data) return nextServer.render404(req, res as ServerResponse);
const mimetype = mimes[extname(id)] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
data.pipe(res);
data.on('error', () => nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
}
async function rawFileDb(
req: IncomingMessage,
res: OutgoingMessage,
nextServer: NextServer,
prisma: PrismaClient,
image: Image,
) {
const data = datasource.get(image.file);
if (!data) return nextServer.render404(req, res as ServerResponse);
res.setHeader('Content-Type', image.mimetype);
data.pipe(res);
data.on('error', () => nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
}
async function fileDb(
req: IncomingMessage,
res: OutgoingMessage,
nextServer: NextServer,
prisma: PrismaClient,
handle: RequestHandler,
image: Image,
) {
const ext = image.file.split('.').pop();
if (Object.keys(exts).includes(ext)) return handle(req, res as ServerResponse);
const data = datasource.get(image.file);
if (!data) return this.nextServer.render404(req, res as ServerResponse);
res.setHeader('Content-Type', image.mimetype);
data.pipe(res);
data.on('error', () => nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
}
async function stats(prisma: PrismaClient) {
const stats = await getStats(prisma, datasource);
await prisma.stats.create({
data: {
data: stats,
},
});
setInterval(async () => {
const stats = await getStats(prisma, datasource);
await prisma.stats.create({
data: {
data: stats,
},
});
if (config.core.logger) logger.info('stats updated');
}, config.core.stats_interval * 1000);
}

View file

@ -1,183 +0,0 @@
import Router from 'find-my-way';
import { NextServer, RequestHandler } from 'next/dist/server/next';
import { Image, PrismaClient } from '@prisma/client';
import { createServer, IncomingMessage, OutgoingMessage, Server as HttpServer, ServerResponse } from 'http';
import next from 'next';
import config from '../lib/config';
import datasource from '../lib/datasource';
import { getStats, log, migrations } from './util';
import { mkdir } from 'fs/promises';
import Logger from '../lib/logger';
import mimes from '../../scripts/mimes';
import { extname } from 'path';
import exts from '../../scripts/exts';
const serverLog = Logger.get('server');
export default class Server {
public router: Router.Instance<Router.HTTPVersion.V1>;
public nextServer: NextServer;
public handle: RequestHandler;
public prisma: PrismaClient;
private http: HttpServer;
public constructor() {
this.start();
}
private async start() {
// annoy user if they didnt change secret from default "changethis"
if (config.core.secret === 'changethis') {
serverLog.error('Secret is not set!');
serverLog.error('Running Zipline as is, without a randomized secret is not recommended and leaves your instance at risk!');
serverLog.error('Please change your secret in the config file or environment variables.');
serverLog.error('The config file is located at `config.toml`, or if using docker-compose you can change the variables in the `docker-compose.yml` file.');
serverLog.error('It is recomended to use a secret that is alphanumeric and randomized. A way you can generate this is through a password manager you may have.');
process.exit(1);
};
const dev = process.env.NODE_ENV === 'development';
process.env.DATABASE_URL = config.core.database_url;
await migrations();
this.prisma = new PrismaClient();
if (config.datasource.type === 'local') {
await mkdir(config.datasource.local.directory, { recursive: true });
}
this.nextServer = next({
dir: '.',
dev,
quiet: !dev,
hostname: config.core.host,
port: config.core.port,
});
this.handle = this.nextServer.getRequestHandler();
this.router = Router({
defaultRoute: (req, res) => {
this.handle(req, res);
},
});
this.router.on('GET', config.uploader.route === '/' ? '/:id(^[^\\.]+\\.[^\\.]+)' : `${config.uploader.route}/:id`, async (req, res, params) => {
const image = await this.prisma.image.findFirst({
where: {
OR: [
{ file: params.id },
{ invisible: { invis: decodeURI(params.id) } },
],
},
});
console.log(image);
if (!image) await this.rawFile(req, res, params.id);
if (image.password) await this.handle(req, res);
else if (image.embed) await this.handle(req, res);
else await this.fileDb(req, res, image);
});
this.router.on('GET', '/r/:id', async (req, res, params) => {
const image = await this.prisma.image.findFirst({
where: {
OR: [
{ file: params.id },
{ invisible: { invis: decodeURI(params.id) } },
],
},
});
if (!image) await this.rawFile(req, res, params.id);
if (image.password) await this.handle(req, res);
else await this.rawFileDb(req, res, image);
});
await this.nextServer.prepare();
this.http = createServer((req, res) => {
this.router.lookup(req, res);
if (config.core.logger) log(req.url);
});
this.http.on('error', (e) => {
serverLog.error(e);
process.exit(1);
});
this.http.on('listening', () => {
serverLog.info(`listening on ${config.core.host}:${config.core.port}`);
});
this.http.listen(config.core.port, config.core.host ?? '0.0.0.0');
this.stats();
}
private async rawFile(req: IncomingMessage, res: OutgoingMessage, id: string) {
const data = datasource.get(id);
if (!data) return this.nextServer.render404(req, res as ServerResponse);
const mimetype = mimes[extname(id)] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
data.pipe(res);
data.on('error', () => this.nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
}
private async rawFileDb(req: IncomingMessage, res: OutgoingMessage, image: Image) {
const data = datasource.get(image.file);
if (!data) return this.nextServer.render404(req, res as ServerResponse);
res.setHeader('Content-Type', image.mimetype);
data.pipe(res);
data.on('error', () => this.nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
await this.prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
}
private async fileDb(req: IncomingMessage, res: OutgoingMessage, image: Image) {
const ext = image.file.split('.').pop();
if (Object.keys(exts).includes(ext)) return this.handle(req, res as ServerResponse);
const data = datasource.get(image.file);
if (!data) return this.nextServer.render404(req, res as ServerResponse);
res.setHeader('Content-Type', image.mimetype);
data.pipe(res);
data.on('error', () => this.nextServer.render404(req, res as ServerResponse));
data.on('end', () => res.end());
await this.prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
}
private async stats() {
const stats = await getStats(this.prisma, datasource);
await this.prisma.stats.create({
data: {
data: stats,
},
});
setInterval(async () => {
const stats = await getStats(this.prisma, datasource);
await this.prisma.stats.create({
data: {
data: stats,
},
});
if (config.core.logger) serverLog.info('stats updated');
}, config.core.stats_interval * 1000);
}
}