Skip to content
This repository was archived by the owner on Apr 11, 2026. It is now read-only.

Commit 019497a

Browse files
committed
feat(api): add analytics
1 parent d82dd55 commit 019497a

7 files changed

Lines changed: 59 additions & 3 deletions

File tree

api/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,15 @@
66
"dev": "BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD=1 bun with-env bun --watch src/index.ts",
77
"start": "bun with-env bun src/index.ts",
88
"typecheck": "tsc --noEmit",
9-
"db:push": "bun with-env drizzle-kit push",
9+
"db:push": "bun with-env drizzle-kit push && bun with-env bun src/utils/db/postpush.ts",
1010
"db:studio": "bun with-env drizzle-kit studio",
1111
"with-env": "dotenv -e ../.env --"
1212
},
1313
"dependencies": {
1414
"@hono/trpc-server": "^0.3.2",
1515
"@hono/zod-openapi": "^0.15.1",
1616
"@trpc/server": "^10.45.2",
17+
"cron": "^3.1.7",
1718
"drizzle-orm": "^0.32.1",
1819
"hono": "^4.5.2",
1920
"postgres": "^3.4.4",

api/src/analytics.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { CronJob } from "cron";
2+
import { db } from "./utils/db";
3+
import { analytics } from "./utils/db/schema";
4+
5+
export async function createAnalyticsCronJob() {
6+
new CronJob(
7+
"* * * * *",
8+
async () => {
9+
const currentDate = new Date();
10+
const channels = await db.query.channels.findMany();
11+
await db.insert(analytics).values(
12+
channels.map((channel) => ({
13+
timestamp: currentDate,
14+
guildId: channel.guildId,
15+
channelId: channel.id,
16+
count: channel.count,
17+
}))
18+
);
19+
},
20+
null,
21+
true,
22+
"Africa/Abidjan"
23+
);
24+
}

api/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import { Hono } from "hono";
44
import { appRouter } from "./router";
55
import { createContext } from "./utils/trpc";
66
import { restRouter } from "./rest";
7+
import { createAnalyticsCronJob } from "./analytics";
78

89
const app = new Hono();
910

11+
createAnalyticsCronJob();
12+
1013
app.get("/", (c) => {
1114
return c.json({
1215
message: "Hello World",

api/src/utils/db/postpush.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { sql } from "drizzle-orm";
2+
import { db } from ".";
3+
4+
async function main() {
5+
try {
6+
await db.execute(
7+
sql`SELECT create_hypertable('analytics', by_range('timestamp'))`
8+
);
9+
} catch (err) {
10+
console.log(
11+
"An error occured while creating hypertable. Hypertable probably already exists."
12+
);
13+
}
14+
process.exit(0);
15+
}
16+
17+
main();

api/src/utils/db/schema.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { relations } from "drizzle-orm";
2-
import { integer, pgTable, text } from "drizzle-orm/pg-core";
2+
import { integer, pgTable, text, timestamp } from "drizzle-orm/pg-core";
33

44
export const guilds = pgTable("guilds", {
55
id: text("id").primaryKey(),
@@ -25,3 +25,14 @@ export const channelRelations = relations(channels, ({ one }) => ({
2525
references: [guilds.id],
2626
}),
2727
}));
28+
29+
export const analytics = pgTable("analytics", {
30+
timestamp: timestamp("timestamp", { withTimezone: true }).primaryKey(),
31+
guildId: text("guild_id")
32+
.notNull()
33+
.references(() => guilds.id),
34+
channelId: text("channel_id")
35+
.notNull()
36+
.references(() => channels.id),
37+
count: integer("count").notNull().default(0),
38+
});

bun.lockb

1 KB
Binary file not shown.

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
db:
3-
image: postgres:16
3+
image: timescale/timescaledb-ha:pg16
44
restart: always
55
environment:
66
POSTGRES_PASSWORD: ${DATABASE_PASSWORD}

0 commit comments

Comments
 (0)