From 2e1a63e291f4aaf46a51f5804942a189d7c5d48e Mon Sep 17 00:00:00 2001 From: Paul Popus Date: Fri, 11 Apr 2025 21:55:13 +0100 Subject: [PATCH 1/2] fix: add protocol checks for HMR in getPayload --- packages/payload/src/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/payload/src/index.ts b/packages/payload/src/index.ts index 2c720cc6812..1a14aedb5f7 100644 --- a/packages/payload/src/index.ts +++ b/packages/payload/src/index.ts @@ -927,6 +927,10 @@ export const getPayload = async ( process.env.DISABLE_PAYLOAD_HMR !== 'true' ) { try { + const hasHTTPS = + process.argv.includes('--experimental-https') ?? + process.env.__NEXT_ASSET_PREFIX?.includes('https:') + const protocol = hasHTTPS ? 'wss' : 'ws' const port = process.env.PORT || '3000' const path = '/_next/webpack-hmr' @@ -934,7 +938,7 @@ export const getPayload = async ( const prefix = process.env.__NEXT_ASSET_PREFIX ?? '' cached.ws = new WebSocket( - process.env.PAYLOAD_HMR_URL_OVERRIDE ?? `ws://localhost:${port}${prefix}${path}`, + process.env.PAYLOAD_HMR_URL_OVERRIDE ?? `${protocol}://localhost:${port}${prefix}${path}`, ) cached.ws.onmessage = (event) => { From fbc47d624b6af33dfa2a461a941d771de7b94789 Mon Sep 17 00:00:00 2001 From: Paul Popus Date: Fri, 11 Apr 2025 21:55:52 +0100 Subject: [PATCH 2/2] add support in dev mode for https dev using `pnpm dev:https` --- .gitignore | 2 +- package.json | 2 ++ test/dev.ts | 52 ++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 42146b6a63b..f31f028d50b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,7 @@ test-results /migrations .localstack .turbo - +dev-certs meta_client.json meta_server.json meta_index.json diff --git a/package.json b/package.json index 3fe296d29a1..4d655284192 100644 --- a/package.json +++ b/package.json @@ -61,10 +61,12 @@ "clean:build:allowtgz": "node ./scripts/delete-recursively.js 'media/' '**/dist/' '**/.cache/' '**/.next/' '**/.turbo/' '**/tsconfig.tsbuildinfo' '**/meta_*.json'", "clean:cache": "node ./scripts/delete-recursively.js node_modules/.cache! packages/payload/node_modules/.cache! .next/*", "dev": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts", + "dev:generate-certs": "mkdir test/dev-certs && openssl req -x509 -newkey rsa:2048 -nodes -keyout ./test/dev-certs/localhost-key.pem -out ./test/dev-certs/localhost-cert.pem -days 365 -subj '/CN=localhost'", "dev:generate-db-schema": "pnpm runts ./test/generateDatabaseSchema.ts", "dev:generate-graphql-schema": "pnpm runts ./test/generateGraphQLSchema.ts", "dev:generate-importmap": "pnpm runts ./test/generateImportMap.ts", "dev:generate-types": "pnpm runts ./test/generateTypes.ts", + "dev:https": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --experimental-https", "dev:postgres": "cross-env PAYLOAD_DATABASE=postgres pnpm runts ./test/dev.ts", "dev:prod": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --prod", "dev:prod:memorydb": "cross-env NODE_OPTIONS=--no-deprecation tsx ./test/dev.ts --prod --start-memory-db", diff --git a/test/dev.ts b/test/dev.ts index ca9629ced1c..72b68ac525f 100644 --- a/test/dev.ts +++ b/test/dev.ts @@ -1,12 +1,15 @@ import nextEnvImport from '@next/env' import chalk from 'chalk' -import { createServer } from 'http' +import { readFileSync } from 'fs' +import { createServer as createServerHTTP } from 'http' +import { createServer as createServerHTTPS } from 'https' import minimist from 'minimist' import nextImport from 'next' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import open from 'open' +import { join } from 'path' import { loadEnv } from 'payload/node' import { parse } from 'url' @@ -32,11 +35,25 @@ if (shouldStartMemoryDB) { process.env.START_MEMORY_DB = 'true' } +const https = process.argv.includes('--experimental-https') + loadEnv() const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) +let httpsOptions = {} + +if (https) { + const key = readFileSync(join(dirname, 'dev-certs', 'localhost-key.pem')) + const cert = readFileSync(join(dirname, 'dev-certs', 'localhost-cert.pem')) + + httpsOptions = { + key, + cert, + } +} + const { _: [_testSuiteArg = '_community'], ...args @@ -83,7 +100,7 @@ if (args.o) { const findOpenPort = (startPort: number): Promise => { return new Promise((resolve, reject) => { - const server = createServer() + const server = https ? createServerHTTPS() : createServerHTTP() server.listen(startPort, () => { console.log(`✓ Running on port ${startPort}`) server.close(() => resolve(startPort)) @@ -97,6 +114,7 @@ const findOpenPort = (startPort: number): Promise => { }) } +const protocol = https ? 'https' : 'http' const port = process.env.PORT ? Number(process.env.PORT) : 3000 const availablePort = await findOpenPort(port) @@ -111,6 +129,11 @@ const app = nextImport({ hostname: 'localhost', port: availablePort, dir: rootDir, + ...(https + ? { + experimentalHttpsServer: httpsOptions, + } + : {}), }) const handle = app.getRequestHandler() @@ -120,20 +143,29 @@ let resolveServer: () => void const serverPromise = new Promise((res) => (resolveServer = res)) void app.prepare().then(() => { - createServer(async (req, res) => { - const parsedUrl = parse(req.url || '', true) - await handle(req, res, parsedUrl) - }).listen(availablePort, () => { - resolveServer() - }) + if (https) { + createServerHTTPS(httpsOptions, async (req, res) => { + const parsedUrl = parse(req.url || '', true) + await handle(req, res, parsedUrl) + }).listen(availablePort, () => { + resolveServer() + }) + } else { + createServerHTTP(async (req, res) => { + const parsedUrl = parse(req.url || '', true) + await handle(req, res, parsedUrl) + }).listen(availablePort, () => { + resolveServer() + }) + } }) await serverPromise process.env.PAYLOAD_DROP_DATABASE = process.env.PAYLOAD_DROP_DATABASE === 'false' ? 'false' : 'true' // fetch the admin url to force a render -void fetch(`http://localhost:${availablePort}${adminRoute}`) -void fetch(`http://localhost:${availablePort}/api/access`) +void fetch(`${protocol}://localhost:${availablePort}${adminRoute}`) +void fetch(`${protocol}://localhost:${availablePort}/api/access`) // This ensures that the next-server process is killed when this process is killed and doesn't linger around. process.on('SIGINT', () => { if (child) {