From 8554b72b0b0fde59fa800e3f75c39df0e9bb74da Mon Sep 17 00:00:00 2001 From: tiennm99 Date: Sat, 9 May 2026 22:30:48 +0700 Subject: [PATCH] refactor: drop Mongo class discriminator + delete src/models/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inline trivial factory bodies into the repos and scrapers that used them. The class:/_id: fields were Java-Mongo parity artifacts that nothing in this codebase reads — Redis docs with the old fields still parse fine; the next write drops them. --- src/api/apple-scraper.js | 7 +---- src/api/google-scraper.js | 7 +---- src/models/admin.js | 23 --------------- src/models/apple-app.js | 5 ---- src/models/google-app.js | 5 ---- src/models/group.js | 46 ------------------------------ src/repository/admin-repository.js | 32 +++++++++------------ src/repository/group-repository.js | 36 ++++++++++++++--------- 8 files changed, 38 insertions(+), 123 deletions(-) delete mode 100644 src/models/admin.js delete mode 100644 src/models/apple-app.js delete mode 100644 src/models/google-app.js delete mode 100644 src/models/group.js diff --git a/src/api/apple-scraper.js b/src/api/apple-scraper.js index f36e5d5..ce85197 100644 --- a/src/api/apple-scraper.js +++ b/src/api/apple-scraper.js @@ -1,7 +1,5 @@ import store from 'app-store-scraper'; -import { newAppleApp } from '../models/apple-app.js'; -// Mirrors Java AppStoreScraper (api/apple/AppStoreScraper.java). // Calls the `app-store-scraper` npm lib directly (no HTTP roundtrip). export function buildAppleRequestByTrackId(id, country) { @@ -20,9 +18,6 @@ export function createAppleScraper(config, repository) { return store.app(req); } - // rawApp returns a JSON-text representation of the parsed object so the - // /rawappleapp command and any other text consumers stay parity-compatible - // with the previous HTTP-text response. async function rawApp(req) { return JSON.stringify(await app(req)); } @@ -30,7 +25,7 @@ export function createAppleScraper(config, repository) { async function cache(resp) { if (!resp || !resp.appId) return; try { - await repo.save(newAppleApp(resp.appId, resp, Date.now())); + await repo.save({ _id: resp.appId, app: resp, millis: Date.now() }); } catch (err) { logger.warn({ appId: resp.appId, err: err.message }, 'failed to cache apple app'); } diff --git a/src/api/google-scraper.js b/src/api/google-scraper.js index 572aa39..2ad8aae 100644 --- a/src/api/google-scraper.js +++ b/src/api/google-scraper.js @@ -1,7 +1,5 @@ import gplay from 'google-play-scraper'; -import { newGoogleApp } from '../models/google-app.js'; -// Mirrors Java GooglePlayScraper (api/google/GooglePlayScraper.java). // Calls the `google-play-scraper` npm lib directly (no HTTP roundtrip). export function buildGoogleRequest(appId, country) { @@ -16,9 +14,6 @@ export function createGoogleScraper(config, repository) { return gplay.app(req); } - // rawApp returns a JSON-text representation of the parsed object so the - // /rawgoogleapp command and any other text consumers stay parity-compatible - // with the previous HTTP-text response. async function rawApp(req) { return JSON.stringify(await app(req)); } @@ -28,7 +23,7 @@ export function createGoogleScraper(config, repository) { const id = resp.appId || fallbackId; if (!id) return; try { - await repo.save(newGoogleApp(id, resp, Date.now())); + await repo.save({ _id: id, app: resp, millis: Date.now() }); } catch (err) { logger.warn({ appId: id, err: err.message }, 'failed to cache google app'); } diff --git a/src/models/admin.js b/src/models/admin.js deleted file mode 100644 index 2193e6a..0000000 --- a/src/models/admin.js +++ /dev/null @@ -1,23 +0,0 @@ -// Admin singleton document — Java parity (_id="admin", class="Admin"). -export const ADMIN_ID = 'admin'; - -export function newAdmin() { - return { _id: ADMIN_ID, class: 'Admin', groups: [] }; -} - -export function adminAddGroup(admin, groupId) { - if (admin.groups.includes(groupId)) return false; - admin.groups.push(groupId); - return true; -} - -export function adminRemoveGroup(admin, groupId) { - const i = admin.groups.indexOf(groupId); - if (i < 0) return false; - admin.groups.splice(i, 1); - return true; -} - -export function adminHasGroup(admin, groupId) { - return admin.groups.includes(groupId); -} diff --git a/src/models/apple-app.js b/src/models/apple-app.js deleted file mode 100644 index d4f9dc4..0000000 --- a/src/models/apple-app.js +++ /dev/null @@ -1,5 +0,0 @@ -// AppleApp cache entry — Java parity (_id=appId, class="AppleApp"). -// TTL is enforced by Upstash Redis EX, so no isExpired helper. -export function newAppleApp(appId, response, millis) { - return { _id: appId, class: 'AppleApp', app: response, millis }; -} diff --git a/src/models/google-app.js b/src/models/google-app.js deleted file mode 100644 index 3285c99..0000000 --- a/src/models/google-app.js +++ /dev/null @@ -1,5 +0,0 @@ -// GoogleApp cache entry — Java parity (_id=appId, class="GoogleApp"). -// TTL is enforced by Upstash Redis EX, so no isExpired helper. -export function newGoogleApp(appId, response, millis) { - return { _id: appId, class: 'GoogleApp', app: response, millis }; -} diff --git a/src/models/group.js b/src/models/group.js deleted file mode 100644 index 00038c8..0000000 --- a/src/models/group.js +++ /dev/null @@ -1,46 +0,0 @@ -// Group document — Java parity (_id is string form of Telegram chat ID). -export function groupIdToKey(groupId) { - return String(groupId); -} - -export function groupKeyToId(key) { - return Number(key); -} - -export function newGroup(groupId) { - return { - _id: groupIdToKey(groupId), - class: 'Group', - appleApps: [], - googleApps: [], - }; -} - -function addApp(list, appId, country) { - if (list.some((a) => a.appId === appId)) return false; - list.push({ appId, country }); - return true; -} - -function removeApp(list, appId) { - const i = list.findIndex((a) => a.appId === appId); - if (i < 0) return false; - list.splice(i, 1); - return true; -} - -export function groupAddAppleApp(group, appId, country) { - return addApp(group.appleApps, appId, country); -} - -export function groupRemoveAppleApp(group, appId) { - return removeApp(group.appleApps, appId); -} - -export function groupAddGoogleApp(group, appId, country) { - return addApp(group.googleApps, appId, country); -} - -export function groupRemoveGoogleApp(group, appId) { - return removeApp(group.googleApps, appId); -} diff --git a/src/repository/admin-repository.js b/src/repository/admin-repository.js index 7264367..804fc48 100644 --- a/src/repository/admin-repository.js +++ b/src/repository/admin-repository.js @@ -1,48 +1,44 @@ import { getJson, putJson } from './upstash.js'; -import { - ADMIN_ID, - adminAddGroup, - adminHasGroup, - adminRemoveGroup, - newAdmin, -} from '../models/admin.js'; -// Upstash-backed admin singleton — Java parity at the document level -// (logical key 'admin' holds the same shape Mongo stored at _id="admin"). -// The physical Redis key carries the configured KEY_PREFIX (handled by adapter). +const ADMIN_KEY = 'admin'; + +// Upstash-backed admin singleton. Holds the authorized chat ID allowlist. export function createAdminRepository(handle) { async function init() { - const existing = await getJson(handle, ADMIN_ID); + const existing = await getJson(handle, ADMIN_KEY); if (existing) return; - await save(newAdmin()); + await save({ _id: ADMIN_KEY, groups: [] }); } async function getAdmin() { - const doc = await getJson(handle, ADMIN_ID); - return doc ?? newAdmin(); + const doc = await getJson(handle, ADMIN_KEY); + return doc ?? { _id: ADMIN_KEY, groups: [] }; } async function save(admin) { - await putJson(handle, ADMIN_ID, admin); + await putJson(handle, ADMIN_KEY, admin); } async function addGroup(groupId) { const admin = await getAdmin(); - if (!adminAddGroup(admin, groupId)) return false; + if (admin.groups.includes(groupId)) return false; + admin.groups.push(groupId); await save(admin); return true; } async function removeGroup(groupId) { const admin = await getAdmin(); - if (!adminRemoveGroup(admin, groupId)) return false; + const i = admin.groups.indexOf(groupId); + if (i < 0) return false; + admin.groups.splice(i, 1); await save(admin); return true; } async function hasGroup(groupId) { const admin = await getAdmin(); - return adminHasGroup(admin, groupId); + return admin.groups.includes(groupId); } async function getAllGroups() { diff --git a/src/repository/group-repository.js b/src/repository/group-repository.js index 1643d58..097a6f3 100644 --- a/src/repository/group-repository.js +++ b/src/repository/group-repository.js @@ -1,18 +1,13 @@ import { del, getJson, putJson } from './upstash.js'; -import { - groupAddAppleApp, - groupAddGoogleApp, - groupIdToKey, - groupRemoveAppleApp, - groupRemoveGoogleApp, - newGroup, -} from '../models/group.js'; // Upstash-backed per-group state. Logical key shape: `group:{chatId}`. -// Physical key gets KEY_PREFIX prepended by the adapter. export function createGroupRepository(handle) { function key(groupId) { - return `group:${groupIdToKey(groupId)}`; + return `group:${String(groupId)}`; + } + + function newGroup(groupId) { + return { _id: String(groupId), appleApps: [], googleApps: [] }; } async function exists(groupId) { @@ -38,6 +33,19 @@ export function createGroupRepository(handle) { await del(handle, key(groupId)); } + function addApp(list, appId, country) { + if (list.some((a) => a.appId === appId)) return false; + list.push({ appId, country }); + return true; + } + + function removeApp(list, appId) { + const i = list.findIndex((a) => a.appId === appId); + if (i < 0) return false; + list.splice(i, 1); + return true; + } + async function mutateAndSave(groupId, mutator) { const group = await getGroup(groupId); if (!mutator(group)) return false; @@ -52,12 +60,12 @@ export function createGroupRepository(handle) { initGroup, deleteGroup, addAppleApp: (groupId, appId, country) => - mutateAndSave(groupId, (g) => groupAddAppleApp(g, appId, country)), + mutateAndSave(groupId, (g) => addApp(g.appleApps, appId, country)), removeAppleApp: (groupId, appId) => - mutateAndSave(groupId, (g) => groupRemoveAppleApp(g, appId)), + mutateAndSave(groupId, (g) => removeApp(g.appleApps, appId)), addGoogleApp: (groupId, appId, country) => - mutateAndSave(groupId, (g) => groupAddGoogleApp(g, appId, country)), + mutateAndSave(groupId, (g) => addApp(g.googleApps, appId, country)), removeGoogleApp: (groupId, appId) => - mutateAndSave(groupId, (g) => groupRemoveGoogleApp(g, appId)), + mutateAndSave(groupId, (g) => removeApp(g.googleApps, appId)), }; }