"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
require("storyboard-preset-console");
const express = require("express");
const bodyParser = require("body-parser");
const morgan = require("morgan");
const boombox_1 = require("./endpoints/boombox");
const authenticator_1 = require("./features/authenticator");
const endpoints_1 = require("./endpoints");
const Stream = require("stream");
const config_1 = require("./config");
const storyboard_1 = require("storyboard");
const request = require("request");
const http_event_1 = require("./http-event");
const http = require("http");
const pkg = require('../../package.json');
const modelsPkg = require('../../node_modules/compactd-models/package.json');
// const Ddos = require('ddos');
class CompactdApplication {
    constructor(host = config_1.default.get('ip'), port = config_1.default.get('port')) {
        this.app = express();
        this.port = port;
        this.auth = new authenticator_1.default('instance', config_1.default.get('secret'));
        this.host = host;
    }
    setupCouchDB() {
        // this.app.use('/database', expressProxy(
        //   config.get('couchHost') + ':' + config.get('couchPort'), {
        //   proxyReqOptDecorator: this.auth.proxyRequestDecorator()
        // }));
        this.app.all('/database/*', bodyParser.urlencoded({ extended: true }), bodyParser.json(), (req, res) => __awaiter(this, void 0, void 0, function* () {
            // req.pause();
            const headers = yield this.auth.proxyRequestDecorator()({ headers: Object.assign({}, req.headers) }, req);
            const remoteUrl = req.url.slice(10);
            const [db] = remoteUrl.split('/');
            if (!CompactdApplication.ALLOWED_DATABASES.includes(db)) {
                return res.status(403).send({
                    error: 'Database ' + db + ' not allowed'
                });
            }
            const opts = Object.assign(Object.assign({ method: req.method, url: `http://${config_1.default.get('couchHost')}:${config_1.default.get('couchPort')}/${remoteUrl}` }, headers), req.method !== 'GET' ? { body: JSON.stringify(req.body) } : {});
            storyboard_1.mainStory.info('http', `${req.method} ${req.url} -> http://${config_1.default.get('couchHost')}:${config_1.default.get('couchPort')}/${remoteUrl}`, {
                attach: opts,
                attachLevel: 'trace'
            });
            const remoteReq = request(opts).pipe(res);
        }));
    }
    start() {
        return new Promise((resolve, reject) => {
            this.setupCouchDB();
            this.configure();
            this.route();
            const server = http.createServer(this.app);
            http_event_1.default.attach(server, this.auth);
            server.listen(this.port, this.host, () => {
                storyboard_1.mainStory.info('http', `Express listening on ${this.host}:${this.port} `);
                resolve();
            });
        });
    }
    unprotectedEndpoints() {
        boombox_1.player(this.app);
    }
    /**
     * Configure express app by adding middlewares
     */
    configure() {
        // this.app.use(ddos.express);
        class MorganStream extends Stream.Writable {
            _write(chunk, enc, next) {
                const str = chunk.toString();
                if (str && str.length) {
                    storyboard_1.mainStory.info('http', str.replace('\n', ''));
                }
                next();
            }
        }
        this.app.use(morgan(':method :url :status - :response-time ms', {
            stream: new MorganStream()
        }));
        this.app.use(bodyParser.urlencoded({ extended: true }));
        this.app.use(bodyParser.json());
        this.app.post('/api/sessions', this.auth.signinUser());
        this.app.post('/api/users', this.auth.signupUser());
        this.app.get('/api/status', (req, res) => {
            const status = {
                versions: {
                    server: pkg.version,
                    models: modelsPkg.version
                },
                user: undefined
            };
            try {
                const [bearer, token] = req.header('Authorization').split(' ');
                const user = this.auth.verifySession(token);
                const unused = user.toLowerCase(); // Check is user isnt undefined, otherwise throw error
                status.user = user;
            }
            catch (err) {
                status.user = null;
            }
            finally {
                res.send(status);
            }
        });
        this.unprotectedEndpoints();
        this.app.use('/api*', this.auth.requireAuthentication());
    }
    /**
     * Creates the *protected* routes (under api only)
     */
    route() {
        endpoints_1.default(this.app);
    }
}
CompactdApplication.ALLOWED_DATABASES = ['libraries', 'artworks', 'tracks', 'files', 'artists', 'albums', 'trackers'];
exports.CompactdApplication = CompactdApplication;
//# sourceMappingURL=CompactdApplication.js.map