From e3630699bd96c61b1a7a3f91d31942e7e04e609c Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 27 Oct 2023 16:13:51 +0100 Subject: [PATCH] add time range support --- app.json | 2 +- src/services/api.ts | 16 ++++++++++++++-- src/utils/stringUtils.ts | 26 ++++++++++++++++++++++++++ src/utils/timeUtils.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 src/utils/stringUtils.ts create mode 100644 src/utils/timeUtils.ts diff --git a/app.json b/app.json index 9048e89..be57f85 100644 --- a/app.json +++ b/app.json @@ -1,5 +1,5 @@ { - "name": "Sample node.js express app", + "name": "beatsaber-metrics-tracker", "repository": "https://git.fascinated.cc/Fascinated/beatsaber-metrics-tracker", "healthchecks": {} } diff --git a/src/services/api.ts b/src/services/api.ts index d6b8279..443340d 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,12 +1,13 @@ import express from "express"; import { INFLUXDB_BUCKET, InfluxQueryAPI } from "../index"; +import { formatString } from "../utils/stringUtils"; const app = express(); const port = process.env.PORT || 3000; // Query to get the player count history for tge last 24 hours in 1 hour intervals const getPlayerHistoryQuery = `from(bucket: "${INFLUXDB_BUCKET}") - |> range(start: -24h) + |> range(start: {}) |> filter(fn: (r) => r["_measurement"] == "scoresaber") |> filter(fn: (r) => r["_field"] == "value") |> filter(fn: (r) => r["type"] == "player_count") @@ -20,7 +21,18 @@ app.get("/", (req, res) => { app.get("/analytics", async (req, res) => { const before = new Date().getTime(); - const rows = await InfluxQueryAPI.collectRows(getPlayerHistoryQuery); + + const timeQuery = req.query.time || "24h"; + const timeInMs = parseTimeToMilliseconds(timeQuery.toString()); + if (timeInMs > 30 * 24 * 60 * 60 * 1000) { + return res.status(400).json({ + error: "Time range too large. Max time range is 30 days.", + }); + } + + const rows = await InfluxQueryAPI.collectRows( + formatString(getPlayerHistoryQuery, false, timeQuery) + ); let history = rows.map((row: any) => ({ time: row._time, value: row._value !== null ? row._value.toFixed(0) : null, diff --git a/src/utils/stringUtils.ts b/src/utils/stringUtils.ts new file mode 100644 index 0000000..a3aa3cd --- /dev/null +++ b/src/utils/stringUtils.ts @@ -0,0 +1,26 @@ +/** + * Formats a string with the given arguments. + * + * @param str the string to check + * @param uriEncodeStrings whether to uri encode the strings + * @param args the arguments to replace + * @returns the formatted string + */ +export function formatString( + str: string, + uriEncodeStrings: boolean, + ...args: any[] +): string { + return str.replace(/{}/g, (match) => { + // If there are no arguments, return the match + if (args.length === 0) { + return match; + } + + // Otherwise, return the next argument + if (uriEncodeStrings) { + return encodeURIComponent(String(args.shift())); + } + return String(args.shift()); + }); +} diff --git a/src/utils/timeUtils.ts b/src/utils/timeUtils.ts new file mode 100644 index 0000000..fa0ddc3 --- /dev/null +++ b/src/utils/timeUtils.ts @@ -0,0 +1,40 @@ +/** + * Parses a time string to milliseconds. + * + * @param time the time to parse + * @returns the time in milliseconds + */ +function parseTimeToMilliseconds(time: string) { + // Regular expression to match the numeric value and unit + const regex = /^(\d+)([smhdwMy])$/; + + const match = time.match(regex); + + if (!match) { + throw new Error( + "Invalid time format. Example valid formats: 1s, 5m, 2h, 3d, 1w, 2M, 1y" + ); + } + + const value = parseInt(match[1]); + const unit = match[2]; + + switch (unit) { + case "s": + return value * 1000; // seconds to milliseconds + case "m": + return value * 60 * 1000; // minutes to milliseconds + case "h": + return value * 60 * 60 * 1000; // hours to milliseconds + case "d": + return value * 24 * 60 * 60 * 1000; // days to milliseconds + case "w": + return value * 7 * 24 * 60 * 60 * 1000; // weeks to milliseconds + case "M": + return value * 30 * 24 * 60 * 60 * 1000; // months (approximate) to milliseconds + case "y": + return value * 365 * 24 * 60 * 60 * 1000; // years (approximate) to milliseconds + default: + throw new Error("Invalid time unit. Use one of: s, m, h, d, w, M, y"); + } +}