refactor(server): clean up server code
This commit is contained in:
parent
4442c85dc1
commit
195c57edc3
3 changed files with 190 additions and 189 deletions
|
@ -17,7 +17,6 @@ const { rm } = require('fs/promises');
|
|||
treeShaking: true,
|
||||
entryPoints: [
|
||||
'src/server/index.ts',
|
||||
'src/server/server.ts',
|
||||
'src/server/util.ts',
|
||||
'src/lib/logger.ts',
|
||||
'src/lib/config.ts',
|
||||
|
@ -36,6 +35,6 @@ const { rm } = require('fs/promises');
|
|||
watch,
|
||||
incremental: watch,
|
||||
sourcemap: false,
|
||||
minify: process.env.NODE_ENV === 'production',
|
||||
minify: true,
|
||||
});
|
||||
})();
|
|
@ -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 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';
|
||||
new Server();
|
||||
start();
|
||||
|
||||
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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue