"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const path = require("path");
const fs = require("fs");
const envPaths = require("env-paths");
const mockttp_1 = require("mockttp");
const async_mutex_1 = require("async-mutex");
const update_1 = require("@oclif/plugin-update/lib/commands/update");
const api_server_1 = require("./api-server");
const browsers_1 = require("./browsers");
const error_tracking_1 = require("./error-tracking");
const constants_1 = require("./constants");
const util_1 = require("./util");
const shutdown_1 = require("./shutdown");
const certificates_1 = require("./certificates");
const APP_NAME = "HTTP Toolkit";
async function generateHTTPSConfig(configPath) {
    const keyPath = path.join(configPath, 'ca.key');
    const certPath = path.join(configPath, 'ca.pem');
    const [certContent] = await Promise.all([
        util_1.readFile(certPath, 'utf8').then((certContent) => {
            checkCertExpiry(certContent);
            return certContent;
        }),
        util_1.checkAccess(keyPath, fs.constants.R_OK),
    ]).catch(async () => {
        // Cert doesn't exist, or is too close/past expiry. Generate a new one:
        const newCertPair = await mockttp_1.generateCACertificate({
            commonName: APP_NAME + ' CA'
        });
        return Promise.all([
            util_1.writeFile(certPath, newCertPair.cert).then(() => newCertPair.cert),
            util_1.writeFile(keyPath, newCertPair.key)
        ]);
    });
    return {
        keyPath,
        certPath,
        certContent,
        keyLength: 2048 // Reasonably secure keys please
    };
}
function checkCertExpiry(certContents) {
    const remainingLifetime = certificates_1.getTimeToCertExpiry(certificates_1.parseCert(certContents));
    if (remainingLifetime < 1000 * 60 * 60 * 48) { // Next two days
        console.warn('Certificate expires soon - it must be regenerated');
        throw new Error('Certificate regeneration required');
    }
}
async function runHTK(options = {}) {
    const startTime = Date.now();
    shutdown_1.registerShutdownHandler();
    const configPath = options.configPath || envPaths('httptoolkit', { suffix: '' }).config;
    await util_1.ensureDirectoryExists(configPath);
    await browsers_1.checkBrowserConfig(configPath);
    const configCheckTime = Date.now();
    console.log('Config checked in', configCheckTime - startTime, 'ms');
    const httpsConfig = await generateHTTPSConfig(configPath);
    const certSetupTime = Date.now();
    console.log('Certificates setup in', certSetupTime - configCheckTime, 'ms');
    // Start a Mockttp standalone server
    const standalone = mockttp_1.getStandalone({
        serverDefaults: {
            cors: false,
            recordTraffic: false,
            https: httpsConfig // Use our HTTPS config for HTTPS MITMs.
        },
        corsOptions: {
            strict: true,
            origin: constants_1.ALLOWED_ORIGINS,
            maxAge: 86400 // Cache CORS responses for as long as possible
        }
    });
    await standalone.start({
        port: 45456,
        host: '127.0.0.1'
    });
    const standaloneSetupTime = Date.now();
    console.log('Standalone server started in', standaloneSetupTime - certSetupTime, 'ms');
    // Start the HTK server API
    const apiServer = new api_server_1.HttpToolkitServerApi({
        appName: APP_NAME,
        configPath,
        authToken: options.authToken,
        https: httpsConfig
    });
    const updateMutex = new async_mutex_1.Mutex();
    apiServer.on('update-requested', () => {
        updateMutex.runExclusive(() => update_1.default.run(['stable'])
            .catch((error) => {
            // Received successful update that wants to restart the server
            if (error.code === 'EEXIT') {
                // Block future update checks for one hour.
                // If we don't, we'll redownload the same update again every check.
                // We don't want to block it completely though, in case this server
                // stays open for a very long time.
                return util_1.delay(1000 * 60 * 60);
            }
            console.log(error);
            error_tracking_1.reportError('Failed to check for updates');
        }));
    });
    await apiServer.start();
    console.log('Server started in', Date.now() - standaloneSetupTime, 'ms');
    console.log('Total startup took', Date.now() - startTime, 'ms');
}
exports.runHTK = runHTK;
//# sourceMappingURL=index.js.map