Path: blob/master/src/packages/hub/servers/app/next.ts
1503 views
/*1Serve the Next.js application server, which provides:23- the share server for public_paths4- the landing pages5- ... and more?!6*/78import { Application, NextFunction, Request, Response } from "express";9import { join } from "path";1011// @ts-ignore -- TODO: typescript doesn't like @cocalc/next/init (it is a js file).12import initNextServer from "@cocalc/next/init";13import basePath from "@cocalc/backend/base-path";14import { getLogger } from "@cocalc/hub/logger";15import handleRaw from "@cocalc/next/lib/share/handle-raw";16import { callback2 } from "@cocalc/util/async-utils";17import { database } from "../database";18import createLandingRedirect from "./landing-redirect";19import shareRedirect from "./share-redirect";20import { separate_file_extension } from "@cocalc/util/misc";2122export default async function init(app: Application) {23const winston = getLogger("nextjs");2425winston.info("Initializing the nextjs server...");2627// the Hot module reloader triggers some annoying "fetch" warnings, so28// we temporarily disable all warnings, then re-enable them immediately below.29// https://www.phind.com/search?cache=3a6edffa-ce34-4d0d-b448-6ea45f32578330const originalWarningListeners = process.listeners("warning").slice();31process.removeAllListeners("warning");32const handler = await initNextServer({ basePath });33originalWarningListeners.forEach((listener) => {34process.on("warning", listener);35});3637winston.info("Initializing the nextjs share server...");38const shareServer = await runShareServer();39const shareBasePath = join(basePath, "share");4041if (shareServer) {42// We create a redirect middleware and a raw/download43// middleware, since the share server will be fully available.44// IMPORTANT: all files are also served with download:true, so that45// they don't get rendered with potentially malicious content.46// The only way we could allow this is to serve all raw content47// from a separate domain, e.g., raw.cocalc.com. That would be48// reasonable on cocalc.com, but to ensure this for all on-prem,49// etc. servers is definitely too much, so we just disable this.50// For serving actual raw content, the solution will be to use51// a vhost.5253// 1: The raw static server:54const raw = join(shareBasePath, "raw");55app.all(56join(raw, "*"),57(req: Request, res: Response, next: NextFunction) => {58// Embedding only enabled for PDF files -- see note above59const download =60separate_file_extension(req.path).ext.toLowerCase() !== "pdf";61try {62handleRaw({63...parseURL(req, raw),64req,65res,66next,67download,68});69} catch (_err) {70res.status(404).end();71}72},73);7475// 2: The download server -- just like raw, but files always get sent via download.76const download = join(shareBasePath, "download");77app.all(78join(download, "*"),79(req: Request, res: Response, next: NextFunction) => {80try {81handleRaw({82...parseURL(req, download),83req,84res,85next,86download: true,87});88} catch (_err) {89res.status(404).end();90}91},92);9394// 3: Redirects for backward compat; unfortunately there's slight95// overhead for doing this on every request.9697app.all(join(shareBasePath, "*"), shareRedirect(shareBasePath));98}99100const landingRedirect = createLandingRedirect();101app.all(join(basePath, "index.html"), landingRedirect);102app.all(join(basePath, "doc*"), landingRedirect);103app.all(join(basePath, "policies*"), landingRedirect);104105// The next.js server that serves everything else.106winston.info(107"Now using next.js packages/share handler to handle all endpoints not otherwise handled",108);109110// nextjs listens on everything else111app.all("*", handler);112}113114function parseURL(req: Request, base): { id: string; path: string } {115let url = req.url.slice(base.length + 1);116let i = url.indexOf("/");117if (i == -1) {118url = url + "/";119i = url.length - 1;120}121return { id: url.slice(0, i), path: decodeURI(url.slice(i + 1)) };122}123124async function runShareServer(): Promise<boolean> {125const { rows } = await callback2(database._query, {126query: "SELECT value FROM server_settings WHERE name='share_server'",127});128return rows.length > 0 && rows[0].value == "yes";129}130131132