Openstatus
www.openstatus.dev
1import { db, eq } from "@openstatus/db";
2import { type WorkspacePlan, workspace } from "@openstatus/db/src/schema";
3import { env } from "../env";
4
5import readline from "node:readline";
6
7// Function to prompt user for confirmation
8const askConfirmation = async (question: string): Promise<boolean> => {
9 const rl = readline.createInterface({
10 input: process.stdin,
11 output: process.stdout,
12 });
13
14 return new Promise((resolve) => {
15 rl.question(`${question} (y/n): `, (answer) => {
16 rl.close();
17 resolve(answer.trim().toLowerCase() === "y");
18 });
19 });
20};
21
22/**
23 * Calculates the unix timestamp in milliseconds for a given number of days in the past.
24 * @param days The number of days to subtract from the current date.
25 * @returns The calculated unix timestamp in milliseconds.
26 */
27function calculatePastTimestamp(days: number) {
28 const date = new Date();
29 date.setDate(date.getDate() - days);
30 const timestamp = date.getTime();
31 console.log(`${days}d back: ${timestamp}`);
32 return timestamp;
33}
34
35/**
36 * Get the array of workspace IDs for a given plan.
37 * @param plan The plan to filter by.
38 * @returns The array of workspace IDs.
39 */
40async function getWorkspaceIdsByPlan(plan: WorkspacePlan) {
41 const workspaces = await db
42 .select()
43 .from(workspace)
44 .where(eq(workspace.plan, plan))
45 .all();
46 const workspaceIds = workspaces.map((w) => w.id);
47 console.log(`${plan}: ${workspaceIds}`);
48 return workspaceIds;
49}
50
51/**
52 *
53 * @param timestamp timestamp to delete logs before (in milliseconds)
54 * @param workspaceIds array of workspace IDs to delete logs for
55 * @param reverse allows to NOT delete the logs for the given workspace IDs
56 * @returns
57 */
58async function deleteLogs(
59 timestamp: number,
60 workspaceIds: number[],
61 reverse = false,
62) {
63 const response = await fetch(
64 "https://api.tinybird.co/v0/datasources/ping_response__v8/delete",
65 {
66 method: "POST",
67 headers: {
68 "Content-Type": "application/x-www-form-urlencoded",
69 Authorization: `Bearer ${env().TINY_BIRD_API_KEY}`,
70 },
71 body: new URLSearchParams({
72 delete_condition: `timestamp <= ${timestamp} AND ${reverse ? "NOT" : ""} arrayExists(x -> x IN (${workspaceIds.join(", ")}), [workspaceId])`,
73 }),
74 },
75 );
76 const json = await response.json();
77 console.log(json);
78
79 return json;
80}
81
82async function main() {
83 // check if the script is running in production
84 console.log(`DATABASE_URL: ${env().DATABASE_URL}`);
85
86 const isConfirmed = await askConfirmation(
87 "Are you sure you want to run this script?",
88 );
89
90 if (!isConfirmed) {
91 console.log("Script execution cancelled.");
92 return;
93 }
94
95 const lastTwoWeeks = calculatePastTimestamp(14);
96 const lastThreeMonths = calculatePastTimestamp(90);
97 const lastYear = calculatePastTimestamp(365);
98 // const _lastTwoYears = calculatePastTimestamp(730);
99
100 const starters = await getWorkspaceIdsByPlan("starter");
101 const teams = await getWorkspaceIdsByPlan("team");
102 // const pros = await getWorkspaceIdsByPlan("pro");
103
104 // all other workspaces, we need to 'reverse' the deletion here to NOT include those workspaces
105 const rest = [...starters, ...teams];
106
107 deleteLogs(lastTwoWeeks, rest, true);
108 deleteLogs(lastThreeMonths, starters);
109 deleteLogs(lastYear, teams);
110 // deleteLogs(lastYear, pros);
111}
112
113/**
114 * REMINDER: do it manually (to avoid accidental deletion on dev mode)
115 * Within the app/workflows folder, run the following command:
116 * $ bun src/scripts/tinybird.ts
117 */
118
119// main().catch(console.error);