timbru31/spigot-anti-piracy-backend

View on GitHub
src/app.ts

Summary

Maintainability
A
1 hr
Test Coverage
A
97%
#!/usr/bin/env node

import { promises } from 'fs';
import { join } from 'path';

import * as Koa from 'koa';
import bodyParser = require('koa-bodyparser');
import * as Router from '@koa/router';

import { AddressInfo } from 'net';
import { createLogger, format, transports } from 'winston';

interface IRequestBody {
    user_id?: string;
    userId?: string;
    plugin?: string;
    port?: string;
}

export const app = new Koa();
app.proxy = Boolean(process.env.PROXY) || false;

const router = new Router();

const logger = createLogger({
    format: format.combine(format.timestamp(), format.splat(), format.json()),
});

logger.add(
    new transports.File({
        filename: process.env.LOG_FILE || join(__dirname, 'request.log'),
        maxFiles: 5,
        maxsize: 5000000,
        tailable: true,
    }),
);

if (process.env.NODE_ENV !== 'test') {
    logger.add(
        new transports.Console({
            format: format.simple(),
        }),
    );
}

router.post('/', async (ctx, next) => {
    const body = ctx.request.body as IRequestBody;
    if (!ctx.request.body || (!body.user_id && !body.userId)) {
        return (ctx.status = 400);
    }
    await handleAuthRequest(ctx);

    await next();
});

async function handleAuthRequest(ctx: Router.RouterContext) {
    const request = ctx.request;
    const body = request.body as IRequestBody;
    const userId = body.user_id! || body.userId!;
    const ip = request.ip;
    const blacklisted = await isUserBlacklisted(userId);
    const response = {
        blacklisted,
    };
    if (blacklisted) {
        ctx.status = 401;
    }
    logger.info(
        'request from %s for user %s --> blacklisted %s',
        ip,
        userId,
        blacklisted,
        {
            blacklisted,
            ip,
            plugin: body.plugin,
            port: request.headers['bukkit-server-port'] || body.port,
            userId,
        },
    );
    ctx.set('Content-Type', 'application/json');
    ctx.body = JSON.stringify(response);
}

async function isUserBlacklisted(userId: string) {
    const bannedFileLocation =
        process.env.BLACKLISTED_USERS_FILE ||
        join(__dirname, 'banned_users.txt');
    try {
        const bannedUsersFile = await promises.readFile(
            bannedFileLocation,
            'utf-8',
        );
        if (!bannedUsersFile) {
            return false;
        }
        const bannedUsers =
            (bannedUsersFile.toString().match(/[^\r\n]+/g) as string[]) || [];
        return bannedUsers.includes(userId);
    } catch {
        return false;
    }
}

app.use(bodyParser());
app.use(router.routes());
app.use(router.allowedMethods());

const server = app.listen(process.env.PORT || 3000, () => {
    const loggerFileLocation =
        process.env.LOG_FILE || join(__dirname, 'request.log');
    const bannedFileLocation =
        process.env.BLACKLISTED_USERS_FILE ||
        join(__dirname, 'banned_users.txt');
    logger.info(
        'Spigot Anti Piracy Backend listening at http://%s:%s',
        (server.address() as AddressInfo).address,
        (server.address() as AddressInfo).port,
    );
    logger.info('Logging to %s', loggerFileLocation);
    logger.info('Using %s for blacklisted users', bannedFileLocation);
});