This commit is contained in:
Leandro Facchinetti 2022-11-07 19:40:58 +00:00
parent a239e33a22
commit 4794bcc2f1
1 changed files with 128 additions and 160 deletions

View File

@ -212,7 +212,7 @@ await commander.program
processType, processType,
processNumber, processNumber,
}: { }: {
processType: "main" | "server" | "worker"; processType: "main" | "web" | "email";
processNumber: string; processNumber: string;
} }
) => { ) => {
@ -241,18 +241,26 @@ await commander.program
}); });
const application = { const application = {
name: "courselore", name: "kill-the-newsletter",
version, version,
process: { process: {
id: Math.random().toString(36).slice(2), id: Math.random().toString(36).slice(2),
type: processType, type: processType,
number: number: (typeof processNumber === "string"
typeof processNumber === "string" ? Number(processNumber)
? Number(processNumber) : undefined) as number,
: undefined,
}, },
configuration: (await import(url.pathToFileURL(configuration).href)) configuration: (await import(url.pathToFileURL(configuration).href))
.default, .default as {
hostname: string;
dataDirectory: string;
administratorEmail: string;
environment: "production" | "development" | "other";
tunnel: boolean;
alternativeHostnames: string[];
hstsPreload: boolean;
caddy: string;
},
static: JSON.parse( static: JSON.parse(
await fs.readFile( await fs.readFile(
new URL("../static/paths.json", import.meta.url), new URL("../static/paths.json", import.meta.url),
@ -260,37 +268,25 @@ await commander.program
) )
), ),
ports: { ports: {
server: lodash.times( web: lodash.times(
os.cpus().length, os.cpus().length,
(processNumber) => 6000 + processNumber (processNumber) => 6000 + processNumber
), ),
serverEvents: lodash.times(
os.cpus().length,
(processNumber) => 7000 + processNumber
),
workerEvents: lodash.times(
os.cpus().length,
(processNumber) => 8000 + processNumber
),
}, },
addresses: { web: express(),
canonicalHostname: "courselore.org", email: "TODO",
metaCourseloreInvitation: "https://meta.courselore.org", };
tryHostname: "try.courselore.org",
},
server: express() as any,
serverEvents: express() as any,
workerEvents: express() as any,
} as Application;
application.configuration.environment ??= "production"; application.configuration.environment ??= "production";
application.configuration.demonstration ??=
application.configuration.environment !== "production";
application.configuration.tunnel ??= false; application.configuration.tunnel ??= false;
application.configuration.alternativeHostnames ??= []; application.configuration.alternativeHostnames ??= [];
application.configuration.hstsPreload ??= false; application.configuration.hstsPreload ??= false;
application.configuration.caddy ??= caddyfile``; application.configuration.caddy ??= caddyfile``;
application.web.get("/", (request, response) => {
response.send("TODO");
});
// #!/usr/bin/env node // #!/usr/bin/env node
// import path from "path"; // import path from "path";
@ -814,25 +810,26 @@ await commander.program
const childProcesses = new Set<ExecaChildProcess>(); const childProcesses = new Set<ExecaChildProcess>();
let restartChildProcesses = true; let restartChildProcesses = true;
for (const execaArguments of [ for (const execaArguments of [
...["server", "worker"].flatMap((processType) => ...Object.entries({ web: os.cpus().length, email: 1 }).flatMap(
lodash.times(os.cpus().length, (processNumber) => ({ ([processType, processCount]) =>
file: process.argv[0], lodash.times(processCount, (processNumber) => ({
arguments: [ file: process.argv[0],
process.argv[1], arguments: [
"--process-type", process.argv[1],
processType, "--process-type",
"--process-number", processType,
processNumber, "--process-number",
configuration, processNumber,
], configuration,
options: { ],
preferLocal: true, options: {
stdio: "inherit", preferLocal: true,
...(application.configuration.environment === "production" stdio: "inherit",
? { env: { NODE_ENV: "production" } } ...(application.configuration.environment === "production"
: {}), ? { env: { NODE_ENV: "production" } }
}, : {}),
})) },
}))
), ),
{ {
file: "caddy", file: "caddy",
@ -842,114 +839,100 @@ await commander.program
stdout: "ignore", stdout: "ignore",
stderr: "ignore", stderr: "ignore",
input: caddyfile` input: caddyfile`
{ {
admin off admin off
${ ${
application.configuration.environment === "production" application.configuration.environment === "production"
? `email ${application.configuration.administratorEmail}` ? `email ${application.configuration.administratorEmail}`
: `local_certs` : `local_certs`
}
} }
}
(common) { (common) {
header Cache-Control no-store header Cache-Control no-store
header Content-Security-Policy "default-src https://${ header Content-Security-Policy "default-src https://${
application.configuration.hostname application.configuration.hostname
}/ 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'none'; object-src 'none'" }/ 'unsafe-inline' 'unsafe-eval'; frame-ancestors 'none'; object-src 'none'"
header Cross-Origin-Embedder-Policy require-corp header Cross-Origin-Embedder-Policy require-corp
header Cross-Origin-Opener-Policy same-origin header Cross-Origin-Opener-Policy same-origin
header Cross-Origin-Resource-Policy same-origin header Cross-Origin-Resource-Policy same-origin
header Referrer-Policy no-referrer header Referrer-Policy no-referrer
header Strict-Transport-Security "max-age=31536000; includeSubDomains${ header Strict-Transport-Security "max-age=31536000; includeSubDomains${
application.configuration.hstsPreload ? `; preload` : `` application.configuration.hstsPreload ? `; preload` : ``
}" }"
header X-Content-Type-Options nosniff header X-Content-Type-Options nosniff
header Origin-Agent-Cluster "?1" header Origin-Agent-Cluster "?1"
header X-DNS-Prefetch-Control off header X-DNS-Prefetch-Control off
header X-Frame-Options DENY header X-Frame-Options DENY
header X-Permitted-Cross-Domain-Policies none header X-Permitted-Cross-Domain-Policies none
header -Server header -Server
header -X-Powered-By header -X-Powered-By
header X-XSS-Protection 0 header X-XSS-Protection 0
header Permissions-Policy "interest-cohort=()" header Permissions-Policy "interest-cohort=()"
encode zstd gzip encode zstd gzip
} }
${[ ${[
application.configuration.tunnel ...(application.configuration.tunnel
? [] ? []
: [application.configuration.hostname], : [application.configuration.hostname]),
...application.configuration.alternativeHostnames, ...application.configuration.alternativeHostnames,
] ]
.map((hostname) => `http://${hostname}`) .map((hostname) => `http://${hostname}`)
.join(", ")} { .join(", ")} {
import common
redir https://{host}{uri} 308
handle_errors {
import common import common
redir https://{host}{uri} 308
handle_errors {
import common
}
} }
}
${ ${
application.configuration.alternativeHostnames.length > 0 application.configuration.alternativeHostnames.length > 0
? caddyfile` ? caddyfile`
${application.configuration.alternativeHostnames ${application.configuration.alternativeHostnames
.map((hostname) => `https://${hostname}`) .map((hostname) => `https://${hostname}`)
.join(", ")} { .join(", ")} {
import common
redir https://${
application.configuration.hostname
}{uri} 307
handle_errors {
import common import common
redir https://${
application.configuration.hostname
}{uri} 307
handle_errors {
import common
}
} }
} `
` : ``
: `` }
}
http${application.configuration.tunnel ? `` : `s`}://${ http${application.configuration.tunnel ? `` : `s`}://${
application.configuration.hostname application.configuration.hostname
} { } {
route {
import common
route { route {
root * ${JSON.stringify( import common
url.fileURLToPath( route {
new URL("../static/", import.meta.url) root * ${JSON.stringify(
) url.fileURLToPath(
)} new URL("../static/", import.meta.url)
@file_exists file )
route @file_exists { )}
header Cache-Control "public, max-age=31536000, immutable" @file_exists file
file_server route @file_exists {
header Cache-Control "public, max-age=31536000, immutable"
file_server
}
} }
reverse_proxy ${application.ports.web
.map((port) => `127.0.0.1:${port}`)
.join(" ")}
} }
route /files/* { handle_errors {
root * ${JSON.stringify( import common
path.resolve(application.configuration.dataDirectory)
)}
@file_exists file
route @file_exists {
header Cache-Control "private, max-age=31536000, immutable"
@must_be_downloaded not path *.png *.jpg *.jpeg *.gif *.mp3 *.mp4 *.m4v *.ogg *.mov *.mpeg *.avi *.pdf *.txt
header @must_be_downloaded Content-Disposition attachment
@may_be_embedded_in_other_sites path *.png *.jpg *.jpeg *.gif *.mp3 *.mp4 *.m4v *.ogg *.mov *.mpeg *.avi *.pdf
header @may_be_embedded_in_other_sites Cross-Origin-Resource-Policy cross-origin
file_server
}
} }
reverse_proxy ${application.ports.server
.map((port) => `127.0.0.1:${port}`)
.join(" ")}
} }
handle_errors {
import common
}
}
${application.configuration.caddy} ${application.configuration.caddy}
`, `,
}, },
}, },
]) ])
@ -979,37 +962,22 @@ await commander.program
break; break;
} }
case "server": { case "web": {
const serverApplication = application.server; const webApplication = application.web;
const eventsApplication = application.serverEvents; webApplication.emit("start");
serverApplication.emit("start"); const server = webApplication.listen(
eventsApplication.emit("start"); application.ports.web[application.process.number],
const server = serverApplication.listen(
application.ports.server[application.process.number],
"127.0.0.1"
);
const events = eventsApplication.listen(
application.ports.serverEvents[application.process.number],
"127.0.0.1" "127.0.0.1"
); );
await stop; await stop;
server.close(); server.close();
events.close(); webApplication.emit("stop");
serverApplication.emit("stop");
eventsApplication.emit("stop");
break; break;
} }
case "worker": { case "email": {
const eventsApplication = application.workerEvents; // TODO
eventsApplication.emit("start");
const events = eventsApplication.listen(
application.ports.workerEvents[application.process.number],
"127.0.0.1"
);
await stop; await stop;
events.close();
eventsApplication.emit("stop");
break; break;
} }
} }