This commit is contained in:
parent
a239e33a22
commit
4794bcc2f1
288
server/index.mts
288
server/index.mts
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue