Skip to content

Commit c96f886

Browse files
committed
feat: memory to mem-cho
1 parent f8df930 commit c96f886

File tree

16 files changed

+1356
-182
lines changed

16 files changed

+1356
-182
lines changed

app.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import registerListeners from "./listeners";
33
import { setUpDailyGreeting } from "./intervals/morning";
44
import { setUpUptimeChecker } from "./intervals/uptimeChecker";
55
import { env } from "./env";
6+
import { trackedSitesTable } from "./db/schema";
7+
import { db } from "./db";
68

79
/** Initialization */
810
const app = new App({
@@ -47,15 +49,31 @@ registerListeners(app);
4749
}
4850
});
4951

50-
setUpUptimeChecker((msg) =>
51-
app.client.chat
52-
.postMessage({
53-
text: msg,
54-
channel: env.CMUEATS_CHANNEL_ID,
55-
})
56-
.catch(app.logger.error)
52+
setUpUptimeChecker(
53+
(msg, channelId) =>
54+
app.client.chat
55+
.postMessage({
56+
text: msg,
57+
channel: channelId,
58+
})
59+
.catch(app.logger.error),
60+
db
5761
);
5862
} catch (error) {
5963
app.logger.error("Unable to start App", error);
6064
}
6165
})();
66+
67+
async function seedDb() {
68+
const existingData = await db.select().from(trackedSitesTable);
69+
if (existingData.length) return;
70+
for (const site of env.MONITORED_URLS) {
71+
await db.insert(trackedSitesTable).values({
72+
display_name: site.url,
73+
channel_to_notify: "C093QH5PNH4",
74+
should_ping: !site.doNotPing,
75+
url: site.url,
76+
});
77+
}
78+
}
79+
seedDb();

db/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import "dotenv/config";
2+
import { drizzle } from "drizzle-orm/node-postgres";
3+
import { env } from "../env";
4+
import * as schema from "./schema";
5+
6+
export const db = drizzle(env.DATABASE_URL, { schema });
7+
export type dbType = typeof db;

db/schema.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { sql } from "drizzle-orm";
2+
import {
3+
boolean,
4+
doublePrecision,
5+
integer,
6+
pgTable,
7+
text,
8+
timestamp,
9+
uuid,
10+
} from "drizzle-orm/pg-core";
11+
export const trackedSitesTable = pgTable("sites", {
12+
id: integer().primaryKey().generatedAlwaysAsIdentity(),
13+
display_name: text().notNull(),
14+
url: text().notNull(),
15+
channel_to_notify: text().notNull(),
16+
should_ping: boolean().notNull(),
17+
actively_tracked: boolean().default(true).notNull(),
18+
date_added: timestamp({ withTimezone: true, mode: "date" })
19+
.notNull()
20+
.defaultNow(),
21+
});
22+
export const uptimeRecordsTable = pgTable("records", {
23+
id: uuid()
24+
.default(sql`uuid_generate_v7()`)
25+
.primaryKey()
26+
.defaultRandom(),
27+
site_id: integer().references(() => trackedSitesTable.id, {
28+
onDelete: "cascade",
29+
}),
30+
up: boolean().notNull(),
31+
details: text(),
32+
response_time_ms: doublePrecision(),
33+
time_checked: timestamp({ withTimezone: true, mode: "date" })
34+
.notNull()
35+
.defaultNow(),
36+
});

docker/docker-compose.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: mem-cho-db
2+
3+
services:
4+
postgres:
5+
image: postgres:17.5
6+
container_name: mem-cho-db
7+
environment:
8+
POSTGRES_USER: postgres
9+
POSTGRES_PASSWORD: donotuseinproduction
10+
ports:
11+
- 127.0.0.1:5439:5432
12+
volumes:
13+
- postgres_data:/var/lib/postgresql/data
14+
15+
volumes:
16+
postgres_data:

drizzle.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import "dotenv/config";
2+
import { defineConfig } from "drizzle-kit";
3+
import { env } from "./env";
4+
export default defineConfig({
5+
out: "./drizzle",
6+
schema: "./db/schema.ts",
7+
dialect: "postgresql",
8+
dbCredentials: {
9+
url: env.DATABASE_URL,
10+
},
11+
});

drizzle/0000_bored_iron_fist.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
CREATE TABLE "sites" (
2+
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "sites_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
3+
"display_name" text NOT NULL,
4+
"url" text NOT NULL,
5+
"channel_to_notify" text NOT NULL,
6+
"should_ping" boolean NOT NULL,
7+
"actively_tracked" boolean DEFAULT true NOT NULL,
8+
"date_added" timestamp with time zone DEFAULT now() NOT NULL
9+
);
10+
--> statement-breakpoint
11+
CREATE TABLE "records" (
12+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
13+
"site_id" integer,
14+
"up" boolean NOT NULL,
15+
"details" text,
16+
"response_time_ms" double precision,
17+
"time_checked" timestamp with time zone DEFAULT now() NOT NULL
18+
);
19+
--> statement-breakpoint
20+
ALTER TABLE "records" ADD CONSTRAINT "records_site_id_sites_id_fk" FOREIGN KEY ("site_id") REFERENCES "public"."sites"("id") ON DELETE cascade ON UPDATE no action;

drizzle/meta/0000_snapshot.json

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
{
2+
"id": "9c43d7b9-380d-4531-9c3c-06584c4d242d",
3+
"prevId": "00000000-0000-0000-0000-000000000000",
4+
"version": "7",
5+
"dialect": "postgresql",
6+
"tables": {
7+
"public.sites": {
8+
"name": "sites",
9+
"schema": "",
10+
"columns": {
11+
"id": {
12+
"name": "id",
13+
"type": "integer",
14+
"primaryKey": true,
15+
"notNull": true,
16+
"identity": {
17+
"type": "always",
18+
"name": "sites_id_seq",
19+
"schema": "public",
20+
"increment": "1",
21+
"startWith": "1",
22+
"minValue": "1",
23+
"maxValue": "2147483647",
24+
"cache": "1",
25+
"cycle": false
26+
}
27+
},
28+
"display_name": {
29+
"name": "display_name",
30+
"type": "text",
31+
"primaryKey": false,
32+
"notNull": true
33+
},
34+
"url": {
35+
"name": "url",
36+
"type": "text",
37+
"primaryKey": false,
38+
"notNull": true
39+
},
40+
"channel_to_notify": {
41+
"name": "channel_to_notify",
42+
"type": "text",
43+
"primaryKey": false,
44+
"notNull": true
45+
},
46+
"should_ping": {
47+
"name": "should_ping",
48+
"type": "boolean",
49+
"primaryKey": false,
50+
"notNull": true
51+
},
52+
"actively_tracked": {
53+
"name": "actively_tracked",
54+
"type": "boolean",
55+
"primaryKey": false,
56+
"notNull": true,
57+
"default": true
58+
},
59+
"date_added": {
60+
"name": "date_added",
61+
"type": "timestamp with time zone",
62+
"primaryKey": false,
63+
"notNull": true,
64+
"default": "now()"
65+
}
66+
},
67+
"indexes": {},
68+
"foreignKeys": {},
69+
"compositePrimaryKeys": {},
70+
"uniqueConstraints": {},
71+
"policies": {},
72+
"checkConstraints": {},
73+
"isRLSEnabled": false
74+
},
75+
"public.records": {
76+
"name": "records",
77+
"schema": "",
78+
"columns": {
79+
"id": {
80+
"name": "id",
81+
"type": "uuid",
82+
"primaryKey": true,
83+
"notNull": true,
84+
"default": "gen_random_uuid()"
85+
},
86+
"site_id": {
87+
"name": "site_id",
88+
"type": "integer",
89+
"primaryKey": false,
90+
"notNull": false
91+
},
92+
"up": {
93+
"name": "up",
94+
"type": "boolean",
95+
"primaryKey": false,
96+
"notNull": true
97+
},
98+
"details": {
99+
"name": "details",
100+
"type": "text",
101+
"primaryKey": false,
102+
"notNull": false
103+
},
104+
"response_time_ms": {
105+
"name": "response_time_ms",
106+
"type": "double precision",
107+
"primaryKey": false,
108+
"notNull": false
109+
},
110+
"time_checked": {
111+
"name": "time_checked",
112+
"type": "timestamp with time zone",
113+
"primaryKey": false,
114+
"notNull": true,
115+
"default": "now()"
116+
}
117+
},
118+
"indexes": {},
119+
"foreignKeys": {
120+
"records_site_id_sites_id_fk": {
121+
"name": "records_site_id_sites_id_fk",
122+
"tableFrom": "records",
123+
"tableTo": "sites",
124+
"columnsFrom": [
125+
"site_id"
126+
],
127+
"columnsTo": [
128+
"id"
129+
],
130+
"onDelete": "cascade",
131+
"onUpdate": "no action"
132+
}
133+
},
134+
"compositePrimaryKeys": {},
135+
"uniqueConstraints": {},
136+
"policies": {},
137+
"checkConstraints": {},
138+
"isRLSEnabled": false
139+
}
140+
},
141+
"enums": {},
142+
"schemas": {},
143+
"sequences": {},
144+
"roles": {},
145+
"policies": {},
146+
"views": {},
147+
"_meta": {
148+
"columns": {},
149+
"schemas": {},
150+
"tables": {}
151+
}
152+
}

drizzle/meta/_journal.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"version": "7",
3+
"dialect": "postgresql",
4+
"entries": [
5+
{
6+
"idx": 0,
7+
"version": "7",
8+
"when": 1756759256390,
9+
"tag": "0000_bored_iron_fist",
10+
"breakpoints": true
11+
}
12+
]
13+
}

env.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ const envSchema = z.object({
3030
ALERT_THRESHOLD_MS: z.coerce.number().default(60 * 1000),
3131
PING_THRESHOLD_MS: z.coerce.number().default(5 * 60 * 1000),
3232
HOST_PLATFORM: z.string().default("unspecified"),
33+
DATABASE_URL: z.string().url(),
3334
});
3435
export const env = envSchema.parse(process.env);

intervals/morning.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ const greetings = [
1414
"yawwn good morning cmueats!",
1515
"Good morning!",
1616
"New day, new life!",
17+
"aeeeeee",
18+
"why do we exist, honestly",
19+
"life is probably meaningless, oh well",
20+
"does true love exist?",
1721
];
1822
export const scheduleNextGreeting = (
1923
sendMessage: (msg: string) => Promise<unknown>,
@@ -23,11 +27,11 @@ export const scheduleNextGreeting = (
2327
console.log(
2428
`Scheduling morning message for ${nextMorningTime}. Current time: ${currentTime}`
2529
);
26-
setTimeout(() => {
30+
setTimeout(async () => {
2731
sendMessage(
2832
`${
2933
greetings[Math.floor(Math.random() * greetings.length)]
30-
}\n\n${getWebsiteStatusString()}`
34+
}\n\n${await getWebsiteStatusString()}`
3135
);
3236
scheduleNextGreeting(sendMessage, nextMorningTime.plus({ days: 1 })); // this actually accounts for DST properly
3337
}, nextMorningTime.diff(currentTime).toMillis());

0 commit comments

Comments
 (0)