@cocalc/hub
Version:
CoCalc: Backend webserver component
137 lines • 6.74 kB
JavaScript
"use strict";
/*
The main hub express app.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const assets_1 = require("@cocalc/assets");
const base_path_1 = __importDefault(require("@cocalc/backend/base-path"));
const cdn_1 = require("@cocalc/cdn");
const virtual_hosts_1 = __importDefault(require("@cocalc/next/lib/share/virtual-hosts"));
const static_1 = require("@cocalc/static");
const compression_1 = __importDefault(require("compression"));
const cookie_parser_1 = __importDefault(require("cookie-parser"));
const express_1 = __importDefault(require("express"));
const ms_1 = __importDefault(require("ms"));
const path_1 = require("path");
const url_1 = require("url");
const analytics_1 = require("../analytics");
const health_checks_1 = require("../health-checks");
const logger_1 = require("../logger");
const proxy_1 = __importDefault(require("../proxy"));
const api_1 = __importDefault(require("./app/api"));
const app_redirect_1 = __importDefault(require("./app/app-redirect"));
const blobs_1 = __importDefault(require("./app/blobs"));
const customize_1 = __importDefault(require("./app/customize"));
const metrics_1 = require("./app/metrics");
const next_1 = __importDefault(require("./app/next"));
const set_cookies_1 = __importDefault(require("./app/set-cookies"));
const stats_1 = __importDefault(require("./app/stats"));
const database_1 = require("./database");
const http_1 = __importDefault(require("./http"));
const robots_1 = __importDefault(require("./robots"));
// Used for longterm caching of files. This should be in units of seconds.
const MAX_AGE = Math.round((0, ms_1.default)("10 days") / 1000);
const SHORT_AGE = Math.round((0, ms_1.default)("10 seconds") / 1000);
async function init(opts) {
const winston = (0, logger_1.getLogger)("express-app");
winston.info("creating express app");
// Create an express application
const app = (0, express_1.default)();
app.disable("x-powered-by"); // https://github.com/sagemathinc/cocalc/issues/6101
// healthchecks are for internal use, no basePath prefix
// they also have to come first, since e.g. the vhost depends
// on the DB, which could be down
const basicEndpoints = express_1.default.Router();
await (0, health_checks_1.setup_health_checks)({ router: basicEndpoints, db: database_1.database });
app.use(basicEndpoints);
// also, for the same reasons as above, setup the /metrics endpoint
(0, metrics_1.initMetricsEndpoint)(basicEndpoints);
// now, we build the router for all other endpoints
const router = express_1.default.Router();
// This must go very early - we handle virtual hosts, like wstein.org
// before any other routes or middleware interfere.
if (opts.nextServer) {
app.use((0, virtual_hosts_1.default)());
}
// Enable compression, as suggested by
// http://expressjs.com/en/advanced/best-practice-performance.html#use-gzip-compression
// NOTE "Express runs everything in order" --
// https://github.com/expressjs/compression/issues/35#issuecomment-77076170
app.use((0, compression_1.default)());
app.use((0, cookie_parser_1.default)());
// Install custom middleware to track response time metrics via prometheus
(0, metrics_1.setupInstrumentation)(router);
// see http://stackoverflow.com/questions/10849687/express-js-how-to-get-remote-client-address
app.enable("trust proxy");
// Various files such as the webpack static content should be cached long-term,
// and we use this function to set appropriate headers at various points below.
const cacheLongTerm = (res) => {
res.setHeader("Cache-Control", `public, max-age=${MAX_AGE}, must-revalidate'`);
res.setHeader("Expires", new Date(Date.now().valueOf() + MAX_AGE).toUTCString());
};
const cacheShortTerm = (res) => {
res.setHeader("Cache-Control", `public, max-age=${SHORT_AGE}, must-revalidate`);
res.setHeader("Expires", new Date(Date.now().valueOf() + SHORT_AGE).toUTCString());
};
router.use("/robots.txt", (0, robots_1.default)());
// setup the analytics.js endpoint
await (0, analytics_1.initAnalytics)(router, database_1.database);
(0, api_1.default)(router, opts.projectControl);
// The /static content, used by docker, development, etc.
// This is the stuff that's packaged up via webpack in packages/static.
router.use((0, path_1.join)("/static", static_1.path, "app.html"), express_1.default.static((0, path_1.join)(static_1.path, "app.html"), {
setHeaders: cacheShortTerm,
}));
router.use("/static", express_1.default.static(static_1.path, { setHeaders: cacheLongTerm }));
// Static assets that are used by the webapp, the landing page, etc.
router.use("/webapp", express_1.default.static(assets_1.path, { setHeaders: cacheLongTerm }));
// This is @cocalc/cdn – cocalc serves everything it might get from a CDN on its own.
// This is defined in the @cocalc/cdn package. See the comments in packages/cdn.
router.use("/cdn", express_1.default.static(cdn_1.path, { setHeaders: cacheLongTerm }));
// Redirect requests to /app to /static/app.html.
// TODO: this will likely go away when rewrite the landing pages to not
// redirect users to /app in the first place.
router.get("/app", (req, res) => {
// query is exactly "?key=value,key=..."
const query = (0, url_1.parse)(req.url, true).search || "";
res.redirect((0, path_1.join)(base_path_1.default, "static/app.html") + query);
});
(0, blobs_1.default)(router);
(0, set_cookies_1.default)(router);
(0, customize_1.default)(router, opts.isPersonal);
(0, stats_1.default)(router);
(0, app_redirect_1.default)(router);
if (base_path_1.default !== "/") {
app.use(base_path_1.default, router);
}
else {
app.use(router);
}
const httpServer = (0, http_1.default)({
cert: opts.cert,
key: opts.key,
app,
});
if (opts.proxyServer) {
winston.info(`initializing the http proxy server`);
(0, proxy_1.default)({
projectControl: opts.projectControl,
isPersonal: opts.isPersonal,
httpServer,
app,
});
}
// IMPORTANT:
// The nextjs server must be **LAST** (!), since it takes
// all routes not otherwise handled above.
if (opts.nextServer) {
// The Next.js server
await (0, next_1.default)(app);
}
return { httpServer, router };
}
exports.default = init;
//# sourceMappingURL=express-app.js.map