aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2026-07-02 11:10:07 -0700
committerElizabeth Alexander Hunt <me@liz.coffee>2026-07-02 11:10:07 -0700
commit2859955095020b07909b079d34be8a80e7f58ecd (patch)
treeb10ee799208a50a800fe5cee47ade46c90770d2e
parent5dbd16878546886cae5b56b67de6185bfbae0d07 (diff)
downloadmon.hatecomputers.club-2859955095020b07909b079d34be8a80e7f58ecd.tar.gz
mon.hatecomputers.club-2859955095020b07909b079d34be8a80e7f58ecd.zip
BackupHEADmain
-rw-r--r--example.tests.json357
-rw-r--r--src/canary/job.ts18
-rw-r--r--src/publisher/index.ts23
-rw-r--r--src/util/scheduler.ts28
4 files changed, 379 insertions, 47 deletions
diff --git a/example.tests.json b/example.tests.json
new file mode 100644
index 0000000..9bc0453
--- /dev/null
+++ b/example.tests.json
@@ -0,0 +1,357 @@
+{
+ "result_publishers": [
+ {
+ "type": "discord",
+ "at": "17 hours 30 minutes 0 seconds",
+ "post": {
+ "webhook": "https://discord.com/api/webhooks/1259255179161763852/V8X7V2pPbSvTv-iU2jVuT3HFANJS5dC5wJpe0IFFfDZhYa9s7a3C7N1cwGJW9zmHQ0zl",
+ "role_id": "1259291782760366080"
+ }
+ },
+ {
+ "type": "ntfy",
+ "at": "17 hours 30 minutes 0 seconds",
+ "post": {
+ "webhook": "https://ntfy.simponic.hatecomputers.club/canary"
+ }
+ }
+ ],
+ "dns": ["10.155.0.1"],
+ "http_timeout": "10 seconds",
+ "tests": [
+ {
+ "name": "internal simponic services",
+ "type": "healthcheck",
+ "job": {
+ "healthcheck_routes": [
+ "https://vaultwarden.internal.simponic.xyz",
+ "https://roundcube.internal.simponic.xyz",
+ "https://jellyfin.internal.simponic.xyz",
+ "https://scurvy.internal.simponic.xyz",
+ "https://ntfy.internal.simponic.xyz",
+ "https://pihole.internal.simponic.xyz",
+ "https://ca.internal.simponic.xyz",
+ "https://owncloud.internal.simponic.xyz",
+ "https://drone.internal.simponic.xyz",
+ "https://backups.internal.simponic.xyz"
+ ]
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "external simponic services",
+ "type": "healthcheck",
+ "job": {
+ "healthcheck_routes": [
+ "https://git.simponic.xyz",
+ "https://simponic.xyz"
+ ]
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "external mmt services",
+ "type": "healthcheck",
+ "job": {
+ "healthcheck_routes": [
+ "https://mistymountainstherapy.com",
+ "https://auth.mistymountainstherapy.com",
+ "https://mail.mistymountainstherapy.com"
+ ]
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "external hc services",
+ "type": "healthcheck",
+ "job": {
+ "healthcheck_routes": [
+ "https://hatecomputers.club",
+ "https://git.hatecomputers.club",
+ "https://auth.hatecomputers.club",
+ "https://mail.hatecomputers.club"
+ ]
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "external simponic services",
+ "type": "healthcheck",
+ "job": {
+ "healthcheck_routes": [
+ "https://vaultwarden.internal.simponic.xyz",
+ "https://roundcube.internal.simponic.xyz",
+ "https://jellyfin.internal.simponic.xyz",
+ "https://scurvy.internal.simponic.xyz",
+ "https://ntfy.internal.simponic.xyz",
+ "https://pihole.internal.simponic.xyz",
+ "https://ca.internal.simponic.xyz",
+ "https://owncloud.internal.simponic.xyz",
+ "https://drone.internal.simponic.xyz",
+ "https://backups.internal.simponic.xyz"
+ ]
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "internal dns lookups",
+ "type": "dns",
+ "job": {
+ "resolutions": {
+ "www.int.mistymountainstherapy.com.": "10.212.0.1",
+ "vaultwarden.internal.simponic.xyz.": "100.64.0.5"
+ }
+ },
+ "schedule": {
+ "every": "30 seconds",
+ "jitter": "5 seconds"
+ }
+ },
+ {
+ "name": "external dns lookup",
+ "type": "dns",
+ "job": {
+ "resolutions": {
+ "mail.simponic.xyz": "192.3.248.205"
+ },
+ "servers": ["ryo.simponic.xyz", "nijika.simponic.xyz"]
+ }
+ },
+ {
+ "name": "ping sys simponic",
+ "type": "ping",
+ "job": {
+ "hosts": [
+ "raspberrypi.sys.internal.simponic.xyz",
+ "europa.sys.internal.simponic.xyz",
+ "johan.sys.internal.simponic.xyz",
+ "nijika.sys.internal.simponic.xyz",
+ "levi.sys.internal.simponic.xyz",
+ "ryo.sys.internal.simponic.xyz",
+ "mail.sys.simponic.xyz",
+ "borg.internal.simponic.xyz"
+ ]
+ },
+ "schedule": {
+ "every": "45 seconds",
+ "jitter": "10 seconds"
+ }
+ },
+ {
+ "name": "ping HCCLUB",
+ "type": "ping",
+ "job": {
+ "hosts": [
+ "himmel.int.infra.hatecomputers.club",
+ "fern.int.infra.hatecomputers.club"
+ ]
+ },
+ "schedule": {
+ "every": "45 seconds",
+ "jitter": "10 seconds"
+ }
+ },
+ {
+ "name": "ping MMT",
+ "type": "ping",
+ "job": {
+ "hosts": [
+ "www.int.mistymountainstherapy.com",
+ "mail.int.mistymountainstherapy.com",
+ "mistymountainstherapy.com"
+ ]
+ },
+ "schedule": {
+ "every": "45 seconds",
+ "jitter": "10 seconds"
+ }
+ },
+ {
+ "name": "email from hatecomputers to simponic",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.hatecomputers.club",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.simponic.xyz",
+ "read_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from simponic to hatecomputers",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.simponic.xyz",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.hatecomputers.club",
+ "send_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from hatecomputers to mistymountains",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.hatecomputers.club",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.mistymountainstherapy.com",
+ "read_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from mistymountains to simponic",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.mistymountainstherapy.com",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.simponic.xyz",
+ "send_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from simponic to simponic",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.simponic.xyz",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.simponic.xyz",
+ "read_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from mistymountains to mistymountains",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.mistymountainstherapy.com",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.mistymountainstherapy.com",
+ "read_port": 993
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ },
+ {
+ "name": "email from hatecomputers to hatecomputers",
+ "type": "email",
+ "job": {
+ "from": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.hatecomputers.club",
+ "send_port": 465
+ },
+ "to": {
+ "email": "REDACTED",
+ "username": "REDACTED",
+ "password": "REDACTED",
+ "server": "mail.hatecomputers.club",
+ "read_port": 993
+ },
+ "readRetry": {
+ "retries": 5,
+ "interval": "10 seconds"
+ }
+ },
+ "schedule": {
+ "every": "30 minutes",
+ "jitter": "5 minutes"
+ }
+ }
+ ]
+}
diff --git a/src/canary/job.ts b/src/canary/job.ts
index 61af876..a654417 100644
--- a/src/canary/job.ts
+++ b/src/canary/job.ts
@@ -1,20 +1,6 @@
-import { D } from "../util";
-
-export interface PingJob {
- hosts: string[];
-}
-
export interface Retry {
retries: number;
- interval: D.Duration;
-}
-
-export interface HealthCheckJob {
- healthcheck_routes: string[];
-}
-
-export interface DnsJob {
- resolutions: { [key: string]: string };
+ interval: number;
}
export interface EmailInstruction {
@@ -37,5 +23,3 @@ export interface EmailJob {
to: EmailToInstruction;
readRetry: Retry;
}
-
-export type Job = EmailJob | PingJob | HealthCheckJob | DnsJob;
diff --git a/src/publisher/index.ts b/src/publisher/index.ts
deleted file mode 100644
index f37e108..0000000
--- a/src/publisher/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { D } from "../util";
-
-export enum PublisherType {
- DISCORD = "discord",
- NTFY = "ntfy",
-}
-
-export interface DiscordPost {
- webhook: string;
- role_id: string;
-}
-
-export interface NtfyPost {
- webhook: string;
-}
-
-export type PublisherPost = DiscordPost | NtfyPost;
-
-export interface Publisher {
- type: PublisherType;
- at: D.Duration;
- post: PublisherPost;
-}
diff --git a/src/util/scheduler.ts b/src/util/scheduler.ts
index 9f9f594..476aca0 100644
--- a/src/util/scheduler.ts
+++ b/src/util/scheduler.ts
@@ -1,24 +1,38 @@
import * as TE from "fp-ts/lib/TaskEither";
-import { pipe } from "fp-ts/lib/function";
-import type { Schedule } from "../canary";
+import { pipe, identity } from "fp-ts/lib/function";
+import type { Job, Schedule } from "../canary";
+import * as RA from "fp-ts/lib/ReadonlyArray";
+import * as T from "fp-ts/lib/Task";
+import type { Separated } from "fp-ts/lib/Separated";
interface ScheduledJob {
id: string;
- execute: TE.TaskEither<Error, boolean>;
+ execute: () => TE.TaskEither<Error, boolean>;
at: Date;
schedule: Schedule;
}
type SchedulerState = ReadonlyArray<ScheduledJob>;
+const executeAll = (
+ jobs: ReadonlyArray<TE.TaskEither<Error, boolean>>,
+): T.Task<Separated<ReadonlyArray<Error>, ReadonlyArray<boolean>>> =>
+ pipe(jobs, RA.wilt(T.ApplicativePar)(identity));
+
export const schedulerLoop =
(state: SchedulerState): TE.TaskEither<Error, void> =>
() => {
- const loop = (currentState: SchedulerState): TE.TaskEither<Error, void> =>
+ const loop = (
+ currentState: SchedulerState,
+ time: Date,
+ ): TE.TaskEither<Error, void> =>
pipe(
- executeDueJobs(currentState),
- delay(1000), // Delay for 1 second
- map((newState) => loop(newState)),
+ currentState,
+ RA.filter((job) => job.at <= time),
+ RA.map(({ execute }) => execute()),
+ executeAll,
+ //T.delay(1000), // Delay for 1 second
+ // map((newState) => loop(newState)),
);
return loop(state)();