"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CodeServerRouteWrapper = void 0;
const logger_1 = require("@coder/logger");
const express = __importStar(require("express"));
const util_1 = require("../../common/util");
const cli_1 = require("../cli");
const constants_1 = require("../constants");
const http_1 = require("../http");
const socket_1 = require("../socket");
const util_2 = require("../util");
const wsRouter_1 = require("../wsRouter");
const errors_1 = require("./errors");
class CodeServerRouteWrapper {
    constructor() {
        this._wsRouterWrapper = (0, wsRouter_1.Router)();
        this._socketProxyProvider = new socket_1.SocketProxyProvider();
        this.router = express.Router();
        //#region Route Handlers
        this.$root = async (req, res, next) => {
            const isAuthenticated = await (0, http_1.authenticated)(req);
            if (!isAuthenticated) {
                const to = (0, http_1.self)(req);
                return (0, http_1.redirect)(req, res, "login", {
                    to: to !== "/" ? to : undefined,
                });
            }
            const { query, folder } = await req.settings.read();
            if (query) {
                // Ew means the workspace was closed so clear the last folder/workspace.
                if (req.query.ew) {
                    delete query.folder;
                    delete query.workspace;
                }
                if (folder === query.folder) {
                    delete query.folder;
                }
                // Redirect to the last folder/workspace if nothing else is opened.
                if (!req.query.folder &&
                    !req.query.workspace &&
                    (query.folder || query.workspace) &&
                    !req.args["ignore-last-opened"] // This flag disables this behavior.
                ) {
                    const to = (0, http_1.self)(req);
                    return (0, http_1.redirect)(req, res, to, {
                        folder: query.folder,
                        workspace: query.workspace,
                    });
                }
            }
            // Store the query parameters so we can use them on the next load.  This
            // also allows users to create functionality around query parameters.
            await req.settings.write({ query: req.query });
            next();
        };
        this.$proxyRequest = async (req, res, next) => {
            // We allow certain errors to propagate so that other routers may handle requests
            // outside VS Code
            const requestErrorHandler = (error) => {
                if (error instanceof Error && ["EntryNotFound", "FileNotFound", "HttpError"].includes(error.message)) {
                    next();
                }
                (0, errors_1.errorHandler)(error, req, res, next);
            };
            req.once("error", requestErrorHandler);
            this._codeServerMain.handleRequest(req, res);
        };
        this.$proxyWebsocket = async (req) => {
            const wrappedSocket = await this._socketProxyProvider.createProxy(req.ws);
            this._codeServerMain.handleUpgrade(req, wrappedSocket);
            req.ws.resume();
        };
        //#endregion
        /**
         * Fetches a code server instance asynchronously to avoid an initial memory overhead.
         */
        this.ensureCodeServerLoaded = async (req, _res, next) => {
            if (this._codeServerMain) {
                // Already loaded...
                return next();
            }
            // Create the server...
            const { args } = req;
            // See ../../../lib/vscode/src/vs/server/node/server.main.ts:72.
            const createVSServer = await (0, util_2.loadAMDModule)("vs/server/node/server.main", "createServer");
            try {
                this._codeServerMain = await createVSServer(null, {
                    ...(await (0, cli_1.toCodeArgs)(args)),
                    // TODO: Make the browser helper script work.
                    "without-connection-token": true,
                    "without-browser-env-var": true,
                });
            }
            catch (error) {
                (0, util_1.logError)(logger_1.logger, "CodeServerRouteWrapper", error);
                if (constants_1.isDevMode) {
                    return next(new Error((error instanceof Error ? error.message : error) + " (VS Code may still be compiling)"));
                }
                return next(error);
            }
            return next();
        };
        this.router.get("/", this.ensureCodeServerLoaded, this.$root);
        this.router.all("*", http_1.ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyRequest);
        this._wsRouterWrapper.ws("/*", http_1.ensureAuthenticated, this.ensureCodeServerLoaded, this.$proxyWebsocket);
    }
    get wsRouter() {
        return this._wsRouterWrapper.router;
    }
    dispose() {
        this._codeServerMain?.dispose();
        this._socketProxyProvider.stop();
    }
}
exports.CodeServerRouteWrapper = CodeServerRouteWrapper;
//# sourceMappingURL=vscode.js.map