UNPKG

smc-hub

Version:

CoCalc: Backend webserver component

157 lines (133 loc) 4.23 kB
/* * This file is part of CoCalc: Copyright © 2020 Sagemath, Inc. * License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details */ /* Router for public share server. */ import "./jsdom-support"; import * as os_path from "path"; import { callback } from "awaiting"; import express from "express"; import { get_public_paths, PublicPaths } from "./public-paths"; import { AuthorInfo } from "./authors"; import { SettingsDAO } from "./settings"; import { handle_share_css } from "./handle-share-css"; import { handle_share_listing } from "./handle-share-listing"; import { handle_user_request } from "./handle-user-request"; import { handle_path_request } from "./handle-path-request"; import { path as WEBAPP_PATH } from "webapp-lib"; import * as util from "./util"; import { Database } from "./types"; import { PostgreSQL } from "../postgres/types"; import base_path from "smc-util-node/base-path"; import getLogger from "../logger"; export function share_router(opts: { database: Database; path: string }) { let dbg; const author_info: AuthorInfo = new AuthorInfo(opts.database); const settings_dao: SettingsDAO = new SettingsDAO( opts.database as any as PostgreSQL ); if ((global as any).window != null) { (global as any).window["app_base_path"] = base_path; } const logger = getLogger("share-router"); dbg = logger.debug.bind(logger); logger.info("base_path = ", base_path); logger.info("path = ", opts.path); function log_ip(req): void { const ip_addresses = req.headers["x-forwarded-for"] || req.connection.remoteAddress; dbg(`remote='${ip_addresses}' requests url='${req.url}'`); } if (opts.path.indexOf("[project_id]") === -1) { // VERY BAD situation throw Error(`opts.path (='${opts.path}')must contain '[project_id]'`); } function path_to_files(project_id: string): string { return util.path_to_files(opts.path, project_id); } let ready_queue: Function[] = []; dbg("getting_public_paths"); let public_paths: PublicPaths; async function init_public_paths(): Promise<void> { try { public_paths = await get_public_paths(opts.database); } catch (err) { // This is fatal and should be impossible... dbg("get_public_paths - ERROR", err); return; } dbg("got_public_paths - initialized"); const v = ready_queue; ready_queue = []; for (const cb of v) { cb(); } } // do not await since we want all the code below to run first. init_public_paths(); async function ready(): Promise<void> { if (public_paths != null) return; // wait until public_paths is ready. await callback((cb) => ready_queue.push(cb)); } const router = express.Router(); for (const name of ["favicon-32x32.png", "cocalc-icon.svg"]) { router.use( `/${name}`, express.static(os_path.join(WEBAPP_PATH, name), { immutable: true, maxAge: 86000000, }) ); } // TODO: serve from static file when/if it gets at all big; or from some refactor // of our existing css. That said, our aim for the share server is extreme cleanliness // and simplicity, so what we want may be different from cocalc interactive. router.get("/share.css", handle_share_css); router.get("/", async (req, res) => { log_ip(req); await ready(); handle_share_listing({ public_paths, settings_dao, req, res }); }); router.get("/users/:account_id", async (req, res) => { log_ip(req); await ready(); handle_user_request({ public_paths, author_info, settings_dao, req, res, }); }); // Always use raw under the /raw URL. router.get("/raw/:id/*?", async (req, res) => { log_ip(req); await ready(); handle_path_request({ author_info, settings_dao, public_paths, req, res, viewer: "raw", path_to_files, }); }); router.get("/:id/*?", async (req, res) => { log_ip(req); await ready(); handle_path_request({ author_info, settings_dao, public_paths, req, res, path_to_files, }); }); router.get("*", (req, res) => res.send(`unknown path='${req.path}'`)); return router; }