Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions bin/smee.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ const { values: options } = parseArgs({
type: "string",
short: "t",
},
healthcheck: {
type: "string",
short: "h",
},
maxPingDifference: {
type: "string",
short: "m",
default: "60",
},
path: {
type: "string",
short: "P",
Expand All @@ -46,13 +55,16 @@ if (options.help) {
console.log(`Usage: smee [options]

Options:
-v, --version Display the version number
-u, --url <url> URL of the webhook proxy service. Default: https://smee.io/new
-t, --target <target> Full URL (including protocol and path) of the target service the events will forwarded to.
Default: http://127.0.0.1:PORT/PATH
-p, --port <n> Local HTTP server port. Default: 3000
-P, --path <path> URL path to post proxied requests to. Default: "/"
-h, --help Display this help message`);
-v, --version Display the version number
-u, --url <url> URL of the webhook proxy service. Default: https://smee.io/new
-t, --target <target> Full URL (including protocol and path) of the target service the events will forwarded to.
Default: http://127.0.0.1:PORT/PATH
-h, --healthcheck <interval> Perform health checks based on received pings at specified intervals (in seconds)
-m, --max-ping-difference <seconds> The maximum difference between the last ping and the current time (in seconds) before the client is considered unhealthy.
Default: 60
-p, --port <n> Local HTTP server port. Default: 3000
-P, --path <path> URL path to post proxied requests to. Default: "/"
-h, --help Display this help message`);
} else if (options.version) {
console.log(version);
} else {
Expand All @@ -61,8 +73,15 @@ Options:

async function setup() {
const source = options.url ?? (await Client.createChannel());
const healthcheck = Number.parseInt(options.healthcheck, 10);
const maxPingDifference = Number.parseInt(options.maxPingDifference, 10);

const client = new Client({ source, target });
const client = new Client({
source,
target,
healthcheck,
maxPingDifference,
});
client.start();
}

Expand Down
30 changes: 30 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ type Severity = "info" | "error";
interface Options {
source: string;
target: string;
healthcheck: number;
maxPingDifference: number;
logger?: Pick<Console, Severity>;
fetch?: any;
}
Expand All @@ -23,18 +25,26 @@ const proxyAgent = new EnvHttpProxyAgent();
class Client {
#source: string;
#target: string;
healthcheck: number;
maxPingDifference: number;
#lastPing: number;
#fetch: typeof undiciFetch;
#logger: Pick<Console, Severity>;
#events!: EventSource;

constructor({
source,
target,
healthcheck,
maxPingDifference,
logger = console,
fetch = undiciFetch,
}: Options) {
this.#source = source;
this.#target = target;
this.healthcheck = healthcheck;
this.maxPingDifference = maxPingDifference;
this.#lastPing = Date.now();
this.#logger = logger!;
this.#fetch = fetch;

Expand Down Expand Up @@ -99,6 +109,11 @@ class Client {
this.#logger.info("Connected", this.#events.url);
}

onping() {
this.#logger.info(`Received a ping on ${new Date().toISOString()}`);
this.#lastPing = Date.now();
}

onerror(err: ErrorEvent) {
this.#logger.error(err);
}
Expand All @@ -125,6 +140,21 @@ class Client {
events.addEventListener("open", this.onopen.bind(this));
events.addEventListener("error", this.onerror.bind(this));

if (this.healthcheck) {
events.addEventListener("ping", this.onping.bind(this));

setInterval(() => {
const difference = (Date.now() - this.#lastPing) / 1000;

if (difference > this.maxPingDifference) {
this.#logger.error(
`Maximum ping difference exceeded. (Difference: ${difference.toFixed(4)}s, Maximum Allowed: ${this.maxPingDifference}s)`,
);
process.exit(1);
}
}, this.healthcheck * 1000);
}

this.#logger.info(`Forwarding ${this.#source} to ${this.#target}`);
this.#events = events;

Expand Down