feat: Initial clean release of CHM
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
一些检查失败了
automerge / automerge (push) Has been cancelled
react-native-android-build-apk / AssembleRelease (push) Has been cancelled
Build macOS M1 / Main (push) Has been cancelled
Joplin Continuous Integration / Main (macos-15-intel) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / Main (ubuntu-22.04-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-11-vs2026-arm) (push) Has been cancelled
Joplin Continuous Integration / Main (windows-2025) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04) (push) Has been cancelled
Joplin Continuous Integration / ServerDockerImage (ubuntu-22.04-arm) (push) Has been cancelled
Joplin UI tests / Main (ubuntu-22.04) (push) Has been cancelled
Joplin UI tests / Main (windows-2025) (push) Has been cancelled
Joplin UI tests / Mobile (push) Has been cancelled
Close stale issues / ProcessStaleIssues (push) Has been cancelled
这个提交包含在:
@@ -0,0 +1,103 @@
|
||||
import selfsigned from 'selfsigned';
|
||||
import https from 'node:https';
|
||||
import http from 'node:http';
|
||||
|
||||
const startListening = (server: https.Server|http.Server) => {
|
||||
const listeningPromise = new Promise<void>((resolve, reject) => {
|
||||
const onError = (error: Error) => {
|
||||
server.off('listening', onListening);
|
||||
reject(error);
|
||||
};
|
||||
const onListening = () => {
|
||||
resolve();
|
||||
server.off('error', onError);
|
||||
};
|
||||
|
||||
server.once('listening', onListening);
|
||||
server.once('error', onError);
|
||||
});
|
||||
// Arbitrary port
|
||||
server.listen(0, 'localhost');
|
||||
|
||||
return listeningPromise;
|
||||
};
|
||||
|
||||
interface Options {
|
||||
https: boolean;
|
||||
}
|
||||
|
||||
let caIndex = 0;
|
||||
const createLocalhostCerts = async () => {
|
||||
// Using the same common name for multiple root CAs breaks certificate handling.
|
||||
// Change the CA name to allow running multiple localhost https servers with different CAs:
|
||||
const caCommonName = `ca${++caIndex}.localhost`;
|
||||
const rootCa = await selfsigned.generate(
|
||||
[{ name: 'commonName', value: caCommonName }],
|
||||
{
|
||||
extensions: [
|
||||
{
|
||||
name: 'basicConstraints',
|
||||
cA: true,
|
||||
critical: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
);
|
||||
const localhostCert = await selfsigned.generate(
|
||||
[{ name: 'commonName', value: 'localhost' }],
|
||||
{
|
||||
algorithm: 'sha256',
|
||||
extensions: [
|
||||
{ name: 'basicConstraints', cA: false, critical: true },
|
||||
{ name: 'keyUsage', digitalSignature: true, critical: true },
|
||||
{ name: 'extKeyUsage', serverAuth: true, clientAuth: true },
|
||||
{ name: 'subjectAltName', altNames: [
|
||||
{ type: 2, value: 'localhost' },
|
||||
{ type: 2, value: 'www.localhost' },
|
||||
{ type: 7, value: '127.0.0.1' },
|
||||
] },
|
||||
],
|
||||
ca: { key: rootCa.private, cert: rootCa.cert },
|
||||
},
|
||||
);
|
||||
|
||||
return { rootCa: rootCa.cert, localhost: localhostCert };
|
||||
};
|
||||
|
||||
const createLocalhostServer = async (requestHandler: http.RequestListener, { https: useHttps }: Options) => {
|
||||
// See https://github.com/jfromaniello/selfsigned#custom-extensions
|
||||
const keys = useHttps ? await createLocalhostCerts() : undefined;
|
||||
|
||||
const server = keys ? https.createServer({
|
||||
key: keys.localhost.private,
|
||||
cert: keys.localhost.cert,
|
||||
}, requestHandler) : http.createServer(requestHandler);
|
||||
|
||||
await startListening(server);
|
||||
|
||||
const address = server.address();
|
||||
if (typeof address === 'string') {
|
||||
throw new Error('Unexpected server.address() return type (expected AddressInfo)');
|
||||
}
|
||||
|
||||
return {
|
||||
baseUrl: `${useHttps ? 'https:' : 'http:'}//localhost:${address.port}`,
|
||||
port: address.port,
|
||||
cert: keys?.rootCa,
|
||||
server,
|
||||
|
||||
[Symbol.asyncDispose]() {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
server.close((error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default createLocalhostServer;
|
||||
在新工单中引用
屏蔽一个用户