zipline/server/index.js
2022-02-20 22:01:31 -08:00

170 lines
No EOL
4.9 KiB
JavaScript

const next = require('next').default;
const defaultConfig = require('next/dist/server/config-shared').defaultConfig;
const { createServer } = require('http');
const { stat, mkdir } = require('fs/promises');
const { extname } = require('path');
const validateConfig = require('./validateConfig');
const Logger = require('../src/lib/logger');
const readConfig = require('../src/lib/readConfig');
const mimes = require('../scripts/mimes');
const { log, getStats, shouldUseYarn, getFile, migrations } = require('./util');
const { PrismaClient } = require('@prisma/client');
const { version } = require('../package.json');
const nextConfig = require('../next.config');
const serverLog = Logger.get('server');
const webLog = Logger.get('web');
serverLog.info(`starting zipline@${version} server`);
const dev = process.env.NODE_ENV === 'development';
(async () => {
try {
await run();
} catch (e) {
if (e.message && e.message.startsWith('Could not find a production')) {
webLog.error(`there is no production build - run \`${shouldUseYarn() ? 'yarn build' : 'npm build'}\``);
} else if (e.code && e.code === 'ENOENT') {
if (e.path === './.next') webLog.error(`there is no production build - run \`${shouldUseYarn() ? 'yarn build' : 'npm build'}\``);
} else {
serverLog.error(e);
process.exit(1);
}
}
})();
async function run() {
const a = readConfig();
const config = validateConfig(a);
process.env.DATABASE_URL = config.core.database_url;
await migrations();
await mkdir(config.uploader.directory, { recursive: true });
const app = next({
dir: '.',
dev,
quiet: !dev,
hostname: config.core.host,
port: config.core.port,
conf: Object.assign(defaultConfig, nextConfig),
});
await app.prepare();
const handle = app.getRequestHandler();
const prisma = new PrismaClient();
const srv = createServer(async (req, res) => {
if (req.url.startsWith('/r')) {
const parts = req.url.split('/');
if (!parts[2] || parts[2] === '') return;
let image = await prisma.image.findFirst({
where: {
OR: [
{ file: parts[2] },
{ invisible: { invis: decodeURI(parts[2]) } },
],
},
select: {
mimetype: true,
id: true,
file: true,
invisible: true,
},
});
if (!image) {
const data = await getFile(config.uploader.directory, parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
res.end(data);
} else {
const data = await getFile(config.uploader.directory, image.file);
if (!data) return app.render404(req, res);
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
res.setHeader('Content-Type', image.mimetype);
res.end(data);
}
} else if (req.url.startsWith(config.uploader.route)) {
const parts = req.url.split('/');
if (!parts[2] || parts[2] === '') return;
let image = await prisma.image.findFirst({
where: {
OR: [
{ file: parts[2] },
{ invisible: { invis: decodeURI(parts[2]) } },
],
},
select: {
mimetype: true,
id: true,
file: true,
invisible: true,
embed: true,
},
});
if (!image) {
const data = await getFile(config.uploader.directory, parts[2]);
if (!data) return app.render404(req, res);
const mimetype = mimes[extname(parts[2])] ?? 'application/octet-stream';
res.setHeader('Content-Type', mimetype);
res.end(data);
} else if (image.embed) {
handle(req, res);
} else {
const data = await getFile(config.uploader.directory, image.file);
if (!data) return app.render404(req, res);
await prisma.image.update({
where: { id: image.id },
data: { views: { increment: 1 } },
});
res.setHeader('Content-Type', image.mimetype);
res.end(data);
}
} else {
handle(req, res);
}
if (config.core.logger) log(req.url, res.statusCode);
});
srv.on('error', (e) => {
serverLog.error(e);
process.exit(1);
});
srv.on('listening', () => {
serverLog.info(`listening on ${config.core.host}:${config.core.port}`);
});
srv.listen(config.core.port, config.core.host ?? '0.0.0.0');
const stats = await getStats(prisma, config);
await prisma.stats.create({
data: {
data: stats,
},
});
setInterval(async () => {
const stats = await getStats(prisma, config);
await prisma.stats.create({
data: {
data: stats,
},
});
if (config.core.logger) serverLog.info('stats updated');
}, config.core.stats_interval * 1000);
}