diff --git a/src/packages/frontend/client/api.ts b/src/packages/frontend/client/api.ts index f6edb459fea..44b9983a865 100644 --- a/src/packages/frontend/client/api.ts +++ b/src/packages/frontend/client/api.ts @@ -76,7 +76,7 @@ async function callApi( } if (typeof json == "object" && json.errors) { // This is what happens when the api request fails due to schema validation issues. - // I.e., this is soemthing we only see in dev mode since the schema stuff is disabled in production. + // I.e., this is something we only see in dev mode since the schema stuff is disabled in production. throw Error( `API Schema Error: ${json.message} ${JSON.stringify(json.errors)}`, ); diff --git a/src/packages/hub/package.json b/src/packages/hub/package.json index f7247e7597d..85618989ff2 100644 --- a/src/packages/hub/package.json +++ b/src/packages/hub/package.json @@ -28,7 +28,7 @@ "cookies": "^0.8.0", "cors": "^2.8.5", "debug": "^4.4.0", - "express": "^4.21.2", + "express": "^5.1.0", "formidable": "^3.5.4", "http-proxy-3": "^1.20.8", "lodash": "^4.17.21", @@ -47,7 +47,7 @@ }, "devDependenicesNotes": "For license and size reasons, we make @cocalc/crm a dev dependency so it is NOT installed unless explicitly installed as a separate step.", "devDependencies": { - "@types/express": "^4.17.21", + "@types/express": "^5.0.3", "@types/node": "^18.16.14", "coffeescript": "^2.5.1" }, diff --git a/src/packages/hub/proxy/index.ts b/src/packages/hub/proxy/index.ts index 073fb617c24..3a0c1f84203 100644 --- a/src/packages/hub/proxy/index.ts +++ b/src/packages/hub/proxy/index.ts @@ -4,6 +4,7 @@ */ import { Application } from "express"; + import getLogger from "../logger"; import initRequest from "./handle-request"; import initUpgrade from "./handle-upgrade"; @@ -20,19 +21,38 @@ interface Options { proxyConat: boolean; } +// UUID regex pattern for project ID validation +const UUID_REGEX = + /^[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}$/; + +/** + * Middleware to validate that the project_id route parameter is a valid UUID. + * If valid, continues to next middleware. If invalid, skips to next route. + */ +function uuidMiddleware(req, _res, next) { + if (UUID_REGEX.test(req.params.project_id)) { + return next(); + } + // Not a valid project ID UUID: skip to next route + return next("route"); +} + export default function initProxy(opts: Options) { - const proxy_regexp = `^${ - base_path.length <= 1 ? "" : base_path - }\/[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\/*`; - logger.info("creating proxy server with proxy_regexp", proxy_regexp); + const prefix = base_path.length <= 1 ? "" : base_path; + const routePath = `${prefix}/:project_id/{*splat}`; + logger.info("creating proxy server with route pattern", routePath); // tcp connections: const handleProxy = initRequest(opts); + // Create regex for upgrade handler (still needed for WebSocket matching) + const proxy_regexp = `^${prefix}\/[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}\/.*`; + // websocket upgrades: const handleUpgrade = initUpgrade(opts, proxy_regexp); - opts.app.all(proxy_regexp, handleProxy); + // Use Express 5 path syntax with UUID validation middleware + opts.app.all(routePath, uuidMiddleware, handleProxy); opts.httpServer.on("upgrade", handleUpgrade); } diff --git a/src/packages/hub/servers/app/app-redirect.ts b/src/packages/hub/servers/app/app-redirect.ts index a9957e2cbce..325a70a91d4 100644 --- a/src/packages/hub/servers/app/app-redirect.ts +++ b/src/packages/hub/servers/app/app-redirect.ts @@ -4,8 +4,9 @@ This redirect is *undone* in @cocalc/frontend/client/handle-hash-url.ts */ -import { join } from "path"; +import { join } from "node:path"; import { Router } from "express"; + import basePath from "@cocalc/backend/base-path"; import { getLogger } from "@cocalc/hub/logger"; import { APP_ROUTES } from "@cocalc/util/routing/app"; @@ -14,7 +15,8 @@ export default function init(router: Router) { const winston = getLogger("app-redirect"); const v: string[] = []; for (const path of APP_ROUTES) { - v.push(`/${path}*`); + v.push(`/${path}`); + v.push(`/${path}/{*splat}`); } router.get(v, (req, res) => { winston.debug(req.url); diff --git a/src/packages/hub/servers/app/blobs.ts b/src/packages/hub/servers/app/blobs.ts index 204cbdc76ba..72f10e77ee8 100644 --- a/src/packages/hub/servers/app/blobs.ts +++ b/src/packages/hub/servers/app/blobs.ts @@ -8,7 +8,7 @@ import { getLogger } from "@cocalc/hub/logger"; const logger = getLogger("hub:servers:app:blobs"); export default function init(router: Router) { // return uuid-indexed blobs (mainly used for graphics) - router.get("/blobs/*", async (req, res) => { + router.get("/blobs/{*splat}", async (req, res) => { logger.debug(`${JSON.stringify(req.query)}, ${req.path}`); const uuid = `${req.query.uuid}`; if (req.headers["if-none-match"] === uuid) { diff --git a/src/packages/hub/servers/app/next.ts b/src/packages/hub/servers/app/next.ts index b7233942d80..d9eccfd64b4 100644 --- a/src/packages/hub/servers/app/next.ts +++ b/src/packages/hub/servers/app/next.ts @@ -11,6 +11,7 @@ import { join } from "path"; // @ts-ignore -- TODO: typescript doesn't like @cocalc/next/init (it is a js file). import initNextServer from "@cocalc/next/init"; + import basePath from "@cocalc/backend/base-path"; import { getLogger } from "@cocalc/hub/logger"; import handleRaw from "@cocalc/next/lib/share/handle-raw"; @@ -54,7 +55,7 @@ export default async function init(app: Application) { // 1: The raw static server: const raw = join(shareBasePath, "raw"); app.all( - join(raw, "*"), + join(raw, "{*splat}"), (req: Request, res: Response, next: NextFunction) => { // Embedding only enabled for PDF files -- see note above const download = @@ -76,7 +77,7 @@ export default async function init(app: Application) { // 2: The download server -- just like raw, but files always get sent via download. const download = join(shareBasePath, "download"); app.all( - join(download, "*"), + join(download, "{*splat}"), (req: Request, res: Response, next: NextFunction) => { try { handleRaw({ @@ -95,13 +96,16 @@ export default async function init(app: Application) { // 3: Redirects for backward compat; unfortunately there's slight // overhead for doing this on every request. - app.all(join(shareBasePath, "*"), shareRedirect(shareBasePath)); + app.all(join(shareBasePath), shareRedirect(shareBasePath)); + app.all(join(shareBasePath, "{*splat}"), shareRedirect(shareBasePath)); } const landingRedirect = createLandingRedirect(); app.all(join(basePath, "index.html"), landingRedirect); - app.all(join(basePath, "doc*"), landingRedirect); - app.all(join(basePath, "policies*"), landingRedirect); + app.all(join(basePath, "doc"), landingRedirect); + app.all(join(basePath, "doc", "{*splat}"), landingRedirect); + app.all(join(basePath, "policies"), landingRedirect); + app.all(join(basePath, "policies", "{*splat}"), landingRedirect); // The next.js server that serves everything else. winston.info( @@ -109,7 +113,7 @@ export default async function init(app: Application) { ); // nextjs listens on everything else - app.all("*", handler); + app.all("{*splat}", handler); } function parseURL(req: Request, base): { id: string; path: string } { diff --git a/src/packages/next/lib/init.js b/src/packages/next/lib/init.js index 0c2a0ff0639..24406ff8aed 100644 --- a/src/packages/next/lib/init.js +++ b/src/packages/next/lib/init.js @@ -78,6 +78,17 @@ async function init({ basePath }) { winston.info("ready to handle requests:"); return (req, res) => { winston.http(`req.url=${req.url}`); + // Express 5 compatibility: Make req.query writable for Next.js + // Next.js's apiResolver tries to set req.query, but Express 5 makes it read-only + if (req.query !== undefined) { + const queryValue = req.query; + Object.defineProperty(req, "query", { + value: queryValue, + writable: true, + enumerable: true, + configurable: true, + }); + } handle(req, res); }; } diff --git a/src/packages/next/package.json b/src/packages/next/package.json index 085d094ccee..8fcf5cb6270 100644 --- a/src/packages/next/package.json +++ b/src/packages/next/package.json @@ -75,7 +75,7 @@ "basic-auth": "^2.0.1", "csv-stringify": "^6.3.0", "dayjs": "^1.11.11", - "express": "^4.21.2", + "express": "^5.1.0", "lodash": "^4.17.21", "lru-cache": "^7.18.3", "ms": "2.1.2", @@ -98,7 +98,7 @@ "devDependencies": { "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.3.0", - "@types/express": "^4.17.21", + "@types/express": "^5.0.3", "@types/node": "^18.16.14", "@types/react": "^19.1.10", "@uiw/react-textarea-code-editor": "^3.1.1", diff --git a/src/packages/pnpm-lock.yaml b/src/packages/pnpm-lock.yaml index 05896b2d99a..4e8a39ed423 100644 --- a/src/packages/pnpm-lock.yaml +++ b/src/packages/pnpm-lock.yaml @@ -779,8 +779,8 @@ importers: specifier: ^4.4.0 version: 4.4.1 express: - specifier: ^4.21.2 - version: 4.21.2 + specifier: ^5.1.0 + version: 5.1.0 formidable: specifier: ^3.5.4 version: 3.5.4 @@ -828,8 +828,8 @@ importers: version: 2.26.1 devDependencies: '@types/express': - specifier: ^4.17.21 - version: 4.17.23 + specifier: ^5.0.3 + version: 5.0.3 '@types/node': specifier: ^18.16.14 version: 18.19.122 @@ -970,8 +970,8 @@ importers: specifier: ^1.11.11 version: 1.11.13 express: - specifier: ^4.21.2 - version: 4.21.2 + specifier: ^5.1.0 + version: 5.1.0 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -1034,8 +1034,8 @@ importers: specifier: ^16.3.0 version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@types/express': - specifier: ^4.17.21 - version: 4.17.23 + specifier: ^5.0.3 + version: 5.0.3 '@types/node': specifier: ^18.16.14 version: 18.19.122 @@ -1050,7 +1050,7 @@ importers: version: 15.5.2(@swc/helpers@0.5.15)(webpack-hot-middleware@2.26.1) node-mocks-http: specifier: ^1.14.1 - version: 1.17.2(@types/express@4.17.23)(@types/node@18.19.122) + version: 1.17.2(@types/express@5.0.3)(@types/node@18.19.122) project: dependencies: @@ -1112,11 +1112,11 @@ importers: specifier: ^1.1.3 version: 1.2.0 express: - specifier: ^4.21.2 - version: 4.21.2 + specifier: ^5.1.0 + version: 5.1.0 express-rate-limit: specifier: ^7.4.0 - version: 7.5.1(express@4.21.2) + version: 7.5.1(express@5.1.0) get-port: specifier: ^5.1.1 version: 5.1.1 @@ -1158,8 +1158,8 @@ importers: specifier: ^1.19.5 version: 1.19.6 '@types/express': - specifier: ^4.17.21 - version: 4.17.23 + specifier: ^5.0.3 + version: 5.0.3 '@types/lodash': specifier: ^4.14.202 version: 4.17.20 @@ -1293,8 +1293,8 @@ importers: specifier: ^2.1.5 version: 2.1.5 express: - specifier: ^4.21.2 - version: 4.21.2 + specifier: ^5.1.0 + version: 5.1.0 express-session: specifier: ^1.18.1 version: 1.18.2 @@ -1408,8 +1408,8 @@ importers: specifier: ^2.1.6 version: 2.1.6 '@types/express': - specifier: ^4.17.21 - version: 4.17.23 + specifier: ^5.0.3 + version: 5.0.3 '@types/express-session': specifier: ^1.18.0 version: 1.18.2 @@ -4221,12 +4221,18 @@ packages: '@types/express-serve-static-core@4.19.6': resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} + '@types/express-serve-static-core@5.0.7': + resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + '@types/express-session@1.18.2': resolution: {integrity: sha512-k+I0BxwVXsnEU2hV77cCobC08kIsn4y44C3gC0b46uxZVMaXA04lSPgRLR/bSL2w0t0ShJiG8o4jPzRG/nscFg==} '@types/express@4.17.23': resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + '@types/express@5.0.3': + resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/formidable@3.4.5': resolution: {integrity: sha512-s7YPsNVfnsng5L8sKnG/Gbb2tiwwJTY1conOkJzTMRvJAlLFW1nEua+ADsJQu8N1c0oTHx9+d5nqg10WuT9gHQ==} @@ -4766,6 +4772,10 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + access-control@1.0.1: resolution: {integrity: sha512-H5aqjkogmFxfaOrfn/e42vyspHVXuJ8er63KuljJXpOyJ1ZO/U5CrHfO8BLKIy2w7mBM02L5quL0vbfQqrGQbA==} @@ -5178,6 +5188,10 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + body-parser@2.2.0: + resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + engines: {node: '>=18'} + bonjour-service@1.3.0: resolution: {integrity: sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==} @@ -5639,6 +5653,10 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -5656,6 +5674,10 @@ packages: cookie-signature@1.0.7: resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} @@ -6699,6 +6721,10 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} + express@5.1.0: + resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==} + engines: {node: '>= 18'} + exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} @@ -6807,6 +6833,10 @@ packages: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -6890,6 +6920,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + from2@2.3.0: resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} @@ -7729,6 +7763,9 @@ packages: resolution: {integrity: sha512-GljRxhWvlCNRfZyORiH77FwdFwGcMO620o37EOYC0ORWdq+WYNVqW0w2Juzew4M+L81l6/QS3t5gkkihyRqv9w==} engines: {node: '>=0.10.0'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-redirect@1.0.0: resolution: {integrity: sha512-cr/SlUEe5zOGmzvj9bUyC4LVvkNVAXu4GytXLNMr1pny+a65MpQ9IJzFHD5vi7FyJgb4qt27+eS3TuQnqB+RQw==} engines: {node: '>=0.10.0'} @@ -8531,6 +8568,10 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + memfs@4.17.2: resolution: {integrity: sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==} engines: {node: '>= 4.0.0'} @@ -8552,6 +8593,10 @@ packages: merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge-options@2.0.0: resolution: {integrity: sha512-S7xYIeWHl2ZUKF7SDeBhGg6rfv5bKxVBdk95s/I7wVF8d+hjLSztJ/B271cnUiF6CAFduEQ5Zn3HYwAjT16DlQ==} engines: {node: '>=8'} @@ -8604,6 +8649,10 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.1: + resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} + engines: {node: '>= 0.6'} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -8770,6 +8819,10 @@ packages: resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -9627,6 +9680,10 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + raw-loader@0.5.1: resolution: {integrity: sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==} @@ -10221,6 +10278,10 @@ packages: roughjs@4.6.6: resolution: {integrity: sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==} + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -10349,6 +10410,10 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + send@1.2.0: + resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==} + engines: {node: '>= 18'} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -10360,6 +10425,10 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + serve-static@2.2.0: + resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} + engines: {node: '>= 18'} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -11077,6 +11146,10 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + type@2.7.3: resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} @@ -14499,9 +14572,16 @@ snapshots: '@types/range-parser': 1.2.7 '@types/send': 0.17.5 + '@types/express-serve-static-core@5.0.7': + dependencies: + '@types/node': 18.19.122 + '@types/qs': 6.14.0 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.5 + '@types/express-session@1.18.2': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/express@4.17.23': dependencies: @@ -14510,6 +14590,12 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.8 + '@types/express@5.0.3': + dependencies: + '@types/body-parser': 1.19.6 + '@types/express-serve-static-core': 5.0.7 + '@types/serve-static': 1.15.8 + '@types/formidable@3.4.5': dependencies: '@types/node': 18.19.122 @@ -14652,24 +14738,24 @@ snapshots: '@types/passport-google-oauth20@2.0.16': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/passport': 1.0.17 '@types/passport-oauth2': 1.8.0 '@types/passport-oauth2@1.8.0': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/oauth': 0.9.6 '@types/passport': 1.0.17 '@types/passport-strategy@0.2.38': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/passport': 1.0.17 '@types/passport@1.0.17': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/pbf@3.0.5': {} @@ -14734,7 +14820,7 @@ snapshots: '@types/serve-index@1.9.4': dependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/serve-static@1.15.8': dependencies: @@ -15084,6 +15170,11 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + accepts@2.0.0: + dependencies: + mime-types: 3.0.1 + negotiator: 1.0.0 + access-control@1.0.1: dependencies: millisecond: 0.1.2 @@ -15570,6 +15661,20 @@ snapshots: transitivePeerDependencies: - supports-color + body-parser@2.2.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.1 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + bonjour-service@1.3.0: dependencies: fast-deep-equal: 3.1.3 @@ -16078,6 +16183,10 @@ snapshots: dependencies: safe-buffer: 5.2.1 + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + content-type@1.0.5: {} convert-source-map@2.0.0: {} @@ -16091,6 +16200,8 @@ snapshots: cookie-signature@1.0.7: {} + cookie-signature@1.2.2: {} + cookie@0.4.2: {} cookie@0.7.1: {} @@ -17322,9 +17433,9 @@ snapshots: jest-mock: 30.0.5 jest-util: 30.0.5 - express-rate-limit@7.5.1(express@4.21.2): + express-rate-limit@7.5.1(express@5.1.0): dependencies: - express: 4.21.2 + express: 5.1.0 express-session@1.18.2: dependencies: @@ -17375,6 +17486,38 @@ snapshots: transitivePeerDependencies: - supports-color + express@5.1.0: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.0 + fresh: 2.0.0 + http-errors: 2.0.0 + merge-descriptors: 2.0.0 + mime-types: 3.0.1 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.14.0 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.0 + serve-static: 2.2.0 + statuses: 2.0.1 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + exsolve@1.0.7: {} ext@1.7.0: @@ -17483,6 +17626,17 @@ snapshots: transitivePeerDependencies: - supports-color + finalhandler@2.1.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -17566,6 +17720,8 @@ snapshots: fresh@0.5.2: {} + fresh@2.0.0: {} + from2@2.3.0: dependencies: inherits: 2.0.4 @@ -18583,6 +18739,8 @@ snapshots: is-primitive@3.0.1: {} + is-promise@4.0.0: {} + is-redirect@1.0.0: {} is-regex@1.2.1: @@ -19661,6 +19819,8 @@ snapshots: media-typer@0.3.0: {} + media-typer@1.1.0: {} + memfs@4.17.2: dependencies: '@jsonjoy.com/json-pack': 1.2.0(tslib@2.8.1) @@ -19703,6 +19863,8 @@ snapshots: merge-descriptors@1.0.3: {} + merge-descriptors@2.0.0: {} + merge-options@2.0.0: dependencies: is-plain-obj: 2.1.0 @@ -19770,6 +19932,10 @@ snapshots: dependencies: mime-db: 1.52.0 + mime-types@3.0.1: + dependencies: + mime-db: 1.54.0 + mime@1.6.0: {} mime@3.0.0: {} @@ -19909,6 +20075,8 @@ snapshots: negotiator@0.6.4: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} netmask@2.0.2: {} @@ -20005,7 +20173,7 @@ snapshots: process: 0.11.10 uuid: 9.0.1 - node-mocks-http@1.17.2(@types/express@4.17.23)(@types/node@18.19.122): + node-mocks-http@1.17.2(@types/express@5.0.3)(@types/node@18.19.122): dependencies: accepts: 1.3.8 content-disposition: 0.5.4 @@ -20018,7 +20186,7 @@ snapshots: range-parser: 1.2.1 type-is: 1.6.18 optionalDependencies: - '@types/express': 4.17.23 + '@types/express': 5.0.3 '@types/node': 18.19.122 node-releases@2.0.19: {} @@ -20879,6 +21047,13 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + raw-loader@0.5.1: {} raw-loader@4.0.2(webpack@5.100.1): @@ -21674,6 +21849,16 @@ snapshots: points-on-curve: 0.2.0 points-on-path: 0.2.1 + router@2.2.0: + dependencies: + debug: 4.4.1 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + transitivePeerDependencies: + - supports-color + rrweb-cssom@0.8.0: {} run-applescript@7.0.0: {} @@ -21809,6 +21994,22 @@ snapshots: transitivePeerDependencies: - supports-color + send@1.2.0: + dependencies: + debug: 4.4.1 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.0 + mime-types: 3.0.1 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -21834,6 +22035,15 @@ snapshots: transitivePeerDependencies: - supports-color + serve-static@2.2.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.0 + transitivePeerDependencies: + - supports-color + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -22634,6 +22844,12 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.1 + type@2.7.3: {} typed-array-buffer@1.0.3: diff --git a/src/packages/project/directory-listing.ts b/src/packages/project/directory-listing.ts index 77a68bc1fac..f4704f4244e 100644 --- a/src/packages/project/directory-listing.ts +++ b/src/packages/project/directory-listing.ts @@ -24,7 +24,7 @@ export default function init(): Router { const base = "/.smc/directory_listing/"; const router = Router(); - router.get(base + "*", async (req, res) => { + router.get(base + "{*splat}", async (req, res) => { // decodeURIComponent because decodeURI(misc.encode_path('asdf/te #1/')) != 'asdf/te #1/' // https://github.com/sagemathinc/cocalc/issues/2400 const path = decodeURIComponent(req.path.slice(base.length).trim()); diff --git a/src/packages/project/http-api/server.ts b/src/packages/project/http-api/server.ts index 1540429e55f..7ed6b5f858a 100644 --- a/src/packages/project/http-api/server.ts +++ b/src/packages/project/http-api/server.ts @@ -74,8 +74,8 @@ function configure(server: express.Application, dbg: Function): void { } }; - server.get("/api/v1/*", handler); - server.post("/api/v1/*", handler); + server.get("/api/v1/{*splat}", handler); + server.post("/api/v1/{*splat}", handler); } function rateLimit(server: express.Application): void { diff --git a/src/packages/project/package.json b/src/packages/project/package.json index 10e0384a01a..f85d739b11d 100644 --- a/src/packages/project/package.json +++ b/src/packages/project/package.json @@ -40,7 +40,7 @@ "compression": "^1.7.4", "daemonize-process": "^3.0.0", "diskusage": "^1.1.3", - "express": "^4.21.2", + "express": "^5.1.0", "express-rate-limit": "^7.4.0", "get-port": "^5.1.1", "lodash": "^4.17.21", @@ -57,7 +57,7 @@ }, "devDependencies": { "@types/body-parser": "^1.19.5", - "@types/express": "^4.17.21", + "@types/express": "^5.0.3", "@types/lodash": "^4.14.202", "@types/node": "^18.16.14", "@types/primus": "^7.3.9", diff --git a/src/packages/server/package.json b/src/packages/server/package.json index fd47f76d1fc..d9188c8d362 100644 --- a/src/packages/server/package.json +++ b/src/packages/server/package.json @@ -82,7 +82,7 @@ "cookies": "^0.8.0", "dayjs": "^1.11.11", "dot-object": "^2.1.5", - "express": "^4.21.2", + "express": "^5.1.0", "express-session": "^1.18.1", "google-auth-library": "^9.4.1", "googleapis": "^137.1.0", @@ -127,7 +127,7 @@ "@types/async": "^2.0.43", "@types/cloudflare": "^2.7.11", "@types/dot-object": "^2.1.6", - "@types/express": "^4.17.21", + "@types/express": "^5.0.3", "@types/express-session": "^1.18.0", "@types/jest": "^30.0.0", "@types/lodash": "^4.14.202",