diff --git a/src/app.ts b/src/app.ts index bc65698..46fabd1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -53,6 +53,7 @@ import { getBranding, getBrandingByHashEndpoint } from "./routes/getBranding"; import { postBranding } from "./routes/postBranding"; import { cacheMiddlware } from "./middleware/etag"; import { hostHeader } from "./middleware/hostHeader"; +import { getBrandingStats } from "./routes/getBrandingStats"; export function createServer(callback: () => void): Server { // Create a service (the app object is just a callback). @@ -145,6 +146,8 @@ function setupRoutes(router: Router) { //send the total submissions, total views and total minutes saved router.get("/api/getTotalStats", getTotalStats); + router.get("/api/brandingStats", getBrandingStats); + router.get("/api/getUserInfo", getUserInfo); router.get("/api/userInfo", getUserInfo); diff --git a/src/routes/getBrandingStats.ts b/src/routes/getBrandingStats.ts new file mode 100644 index 0000000..1ecd9f4 --- /dev/null +++ b/src/routes/getBrandingStats.ts @@ -0,0 +1,100 @@ +/* istanbul ignore file */ + +import { db } from "../databases/databases"; +import { Request, Response } from "express"; +import axios from "axios"; +import { Logger } from "../utils/logger"; +import { getCWSUsers } from "../utils/getCWSUsers"; + +// A cache of the number of chrome web store users +let chromeUsersCache = 0; +let firefoxUsersCache = 0; + +interface DBStatsData { + userCount: number, + titles: number, + thumbnails: number, +} + +let lastFetch: DBStatsData = { + userCount: 0, + titles: 0, + thumbnails: 0 +}; + +updateExtensionUsers(); + +export async function getBrandingStats(req: Request, res: Response): Promise { + const row = await getStats(); + lastFetch = row; + + /* istanbul ignore if */ + if (!row) res.sendStatus(500); + const extensionUsers = chromeUsersCache + firefoxUsersCache; + + //send this result + res.send({ + userCount: row.userCount ?? 0, + activeUsers: extensionUsers, + titles: row.titles, + thumbnails: row.thumbnails, + }); +} + +async function getStats(): Promise { + if (db.highLoad()) { + return Promise.resolve(lastFetch); + } else { + const userCount = (await db.prepare("get", `SELECT COUNT(DISTINCT "userID") as "userCount" FROM titles`, []))?.userCount; + const titles = (await db.prepare("get", `SELECT COUNT(*) as "titles" FROM titles`, []))?.titles; + const thumbnails = (await db.prepare("get", `SELECT COUNT(*) as "thumbnails" FROM thumbnails`, []))?.thumbnails; + + return { + userCount: userCount ?? 0, + titles: titles ?? 0, + thumbnails: thumbnails ?? 0 + }; + } +} + +function updateExtensionUsers() { + const mozillaAddonsUrl = "https://addons.mozilla.org/api/v3/addons/addon/dearrow/"; + const chromeExtensionUrl = "https://chrome.google.com/webstore/detail/enamippconapkdmgfgjchkhakpfinmaj"; + const chromeExtId = "enamippconapkdmgfgjchkhakpfinmaj"; + + axios.get(mozillaAddonsUrl) + .then(res => firefoxUsersCache = res.data.average_daily_users ) + .catch( /* istanbul ignore next */ () => { + Logger.debug(`Failing to connect to ${mozillaAddonsUrl}`); + return 0; + }); + getCWSUsers(chromeExtId) + .then(res => chromeUsersCache = res) + .catch(/* istanbul ignore next */ () => + getChromeUsers(chromeExtensionUrl) + .then(res => chromeUsersCache = res) + ); +} + +/* istanbul ignore next */ +function getChromeUsers(chromeExtensionUrl: string): Promise { + return axios.get(chromeExtensionUrl) + .then(res => { + const body = res.data; + // 2021-01-05 + // [...]= 0) { + const closingQuoteIndex = body.indexOf('"', userDownloadsStartIndex + matchingStringLen); + const userDownloadsStr = body.substr(userDownloadsStartIndex + matchingStringLen, closingQuoteIndex - userDownloadsStartIndex).replace(",", "").replace(".", ""); + return parseInt(userDownloadsStr); + } + }) + .catch(/* istanbul ignore next */ () => { + Logger.debug(`Failing to connect to ${chromeExtensionUrl}`); + return 0; + }); +} \ No newline at end of file