diff --git a/src/advertising/external-ads.ts b/src/advertising/external-ads.ts index f59a5cf..75373d8 100644 --- a/src/advertising/external-ads.ts +++ b/src/advertising/external-ads.ts @@ -1,6 +1,6 @@ import { satisfies as versionSatisfiesRange } from 'semver'; -import { objectStorageMethodsFactory, redisClient } from '../redis'; +import { objectStorageMethodsFactory, setStorageMethodsFactory } from '../redis'; /** Style properties names that are likely to be unnecessary for banners are skipped */ export const stylePropsNames = [ @@ -176,6 +176,7 @@ const PERMANENT_NATIVE_AD_PLACES_RULES_KEY = 'permanent_native_ad_places_rules'; const REPLACE_ADS_URLS_BLACKLIST_KEY = 'replace_ads_urls_blacklist'; const ELEMENTS_TO_HIDE_OR_REMOVE_KEY = 'elements_to_hide_or_remove'; const AD_PROVIDERS_CATEGORIES_KEY = 'ad_providers_categories'; +const HYPELAB_CAMPAIGNS_BLACKLIST_KEY = 'hypelab_campaigns_blacklist'; export const adPlacesRulesMethods = objectStorageMethodsFactory(AD_PLACES_RULES_KEY, []); @@ -210,13 +211,8 @@ export const adProvidersCategoriesMethods = objectStorageMethodsFactory redisClient.smembers(AD_PROVIDERS_ALL_SITES_KEY); - -export const addAdProvidersForAllSites = async (providers: string[]) => - redisClient.sadd(AD_PROVIDERS_ALL_SITES_KEY, ...providers); - -export const removeAdProvidersForAllSites = async (providers: string[]) => - redisClient.srem(AD_PROVIDERS_ALL_SITES_KEY, ...providers); +export const adProvidersForAllSitesMethods = setStorageMethodsFactory(AD_PROVIDERS_ALL_SITES_KEY); +export const hypelabCampaignsBlacklistMethods = setStorageMethodsFactory(HYPELAB_CAMPAIGNS_BLACKLIST_KEY); const FALLBACK_VERSION = '0.0.0'; diff --git a/src/index.ts b/src/index.ts index 35f8f26..fa0c07e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ import { getPlatforms } from './notifications/utils/get-platforms.util'; import { redisClient } from './redis'; import { evmRouter } from './routers/evm'; import { adRulesRouter } from './routers/slise-ad-rules'; +import { templeWalletAdsRouter } from './routers/temple-wallet-ads'; import { getTkeyStats } from './tkey-stats'; import { getABData } from './utils/ab-test'; import { cancelAliceBobOrder } from './utils/alice-bob/cancel-alice-bob-order'; @@ -343,6 +344,8 @@ app.use('/api/slise-ad-rules', adRulesRouter); app.use('/api/evm', evmRouter); +app.use('/api/temple-wallet-ads', templeWalletAdsRouter); + app.post('/api/magic-square-quest/start', async (req, res) => { try { await startMagicSquareQuest(req.body); diff --git a/src/redis.ts b/src/redis.ts index 0451742..b178b03 100644 --- a/src/redis.ts +++ b/src/redis.ts @@ -30,3 +30,9 @@ export const objectStorageMethodsFactory = (storageKey: string, fallba ), removeValues: (keys: string[]) => redisClient.hdel(storageKey, ...keys) }); + +export const setStorageMethodsFactory = (storageKey: string) => ({ + addValues: (values: string[]) => redisClient.sadd(storageKey, ...values), + removeValues: (values: string[]) => redisClient.srem(storageKey, ...values), + getAllValues: () => redisClient.smembers(storageKey) +}); diff --git a/src/routers/slise-ad-rules/providers.ts b/src/routers/slise-ad-rules/providers.ts index 5567565..cbbe99b 100644 --- a/src/routers/slise-ad-rules/providers.ts +++ b/src/routers/slise-ad-rules/providers.ts @@ -2,9 +2,7 @@ import { Request, Router } from 'express'; import { identity } from 'lodash'; import { - addAdProvidersForAllSites, - getAdProvidersForAllSites, - removeAdProvidersForAllSites, + adProvidersForAllSitesMethods, adProvidersMethods, adProvidersByDomainRulesMethods, AdProviderSelectorsRule, @@ -12,8 +10,11 @@ import { AdProvidersByDomainRule, adProvidersCategoriesMethods } from '../../advertising/external-ads'; -import { basicAuth } from '../../middlewares/basic-auth.middleware'; -import { addObjectStorageMethodsToRouter, withBodyValidation, withExceptionHandler } from '../../utils/express-helpers'; +import { + addObjectStorageMethodsToRouter, + addSetStorageMethodsToRouter, + withExceptionHandler +} from '../../utils/express-helpers'; import { isDefined, transformValues } from '../../utils/helpers'; import { nonEmptyStringsListSchema, @@ -204,35 +205,13 @@ export const adProvidersRouter = Router(); * '500': * $ref: '#/components/responses/ErrorResponse' */ -adProvidersRouter - .route('/all-sites') - .get( - withExceptionHandler(async (_req, res) => { - const providers = await getAdProvidersForAllSites(); - - res.status(200).header('Cache-Control', 'public, max-age=300').send(providers); - }) - ) - .post( - basicAuth, - withExceptionHandler( - withBodyValidation(nonEmptyStringsListSchema, async (req, res) => { - const providersAddedCount = await addAdProvidersForAllSites(req.body); - - res.status(200).send({ message: `${providersAddedCount} providers have been added` }); - }) - ) - ) - .delete( - basicAuth, - withExceptionHandler( - withBodyValidation(nonEmptyStringsListSchema, async (req, res) => { - const providersRemovedCount = await removeAdProvidersForAllSites(req.body); - - res.status(200).send({ message: `${providersRemovedCount} providers have been removed` }); - }) - ) - ); +addSetStorageMethodsToRouter(adProvidersRouter, { + path: '/all-sites', + methods: adProvidersForAllSitesMethods, + arrayValidationSchema: nonEmptyStringsListSchema, + successfulAdditionMessage: addedEntriesCount => `${addedEntriesCount} providers have been added`, + successfulRemovalMessage: removedEntriesCount => `${removedEntriesCount} providers have been removed` +}); /** * @swagger diff --git a/src/routers/temple-wallet-ads.ts b/src/routers/temple-wallet-ads.ts new file mode 100644 index 0000000..7aa54b2 --- /dev/null +++ b/src/routers/temple-wallet-ads.ts @@ -0,0 +1,88 @@ +import { Router } from 'express'; + +import { hypelabCampaignsBlacklistMethods } from '../advertising/external-ads'; +import { addSetStorageMethodsToRouter } from '../utils/express-helpers'; +import { nonEmptyStringsListSchema } from '../utils/schemas'; + +export const templeWalletAdsRouter = Router(); + +/** + * @swagger + * tags: + * name: Temple Wallet Ads + * /api/temple-wallet-ads/hypelab-campaigns-blacklist: + * get: + * summary: Get the list of blacklisted Hypelab campaigns slugs + * tags: + * - Temple Wallet Ads + * responses: + * '200': + * description: List of blacklisted Hypelab campaigns slugs + * content: + * application/json: + * schema: + * type: array + * items: + * type: string + * example: + * - '3896abb03b' + * '500': + * $ref: '#/components/responses/ErrorResponse' + * post: + * summary: Add Hypelab campaigns slugs to the blacklist + * tags: + * - Temple Wallet Ads + * security: + * - basicAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: array + * items: + * type: string + * example: + * - '3896abb03b' + * responses: + * '200': + * $ref: '#/components/responses/SuccessResponse' + * '400': + * $ref: '#/components/responses/ErrorResponse' + * '401': + * $ref: '#/components/responses/UnauthorizedError' + * '500': + * $ref: '#/components/responses/ErrorResponse' + * delete: + * summary: Remove Hypelab campaigns slugs from the blacklist + * tags: + * - Temple Wallet Ads + * security: + * - basicAuth: [] + * requestBody: + * required: true + * content: + * application/json: + * schema: + * type: array + * items: + * type: string + * example: + * - '3896abb03b' + * responses: + * '200': + * $ref: '#/components/responses/SuccessResponse' + * '400': + * $ref: '#/components/responses/ErrorResponse' + * '401': + * $ref: '#/components/responses/UnauthorizedError' + * '500': + * $ref: '#/components/responses/ErrorResponse' + */ +addSetStorageMethodsToRouter(templeWalletAdsRouter, { + path: '/hypelab-campaigns-blacklist', + methods: hypelabCampaignsBlacklistMethods, + arrayValidationSchema: nonEmptyStringsListSchema, + successfulAdditionMessage: slugs => `Added ${slugs} slugs to the blacklist`, + successfulRemovalMessage: slugs => `Removed ${slugs} slugs from the blacklist` +}); diff --git a/src/utils/express-helpers.ts b/src/utils/express-helpers.ts index 9ad300f..ae8ff1e 100644 --- a/src/utils/express-helpers.ts +++ b/src/utils/express-helpers.ts @@ -13,6 +13,12 @@ interface ObjectStorageMethods { removeValues: (keys: string[]) => Promise; } +interface SetStorageMethods { + addValues: (values: string[]) => Promise; + removeValues: (values: string[]) => Promise; + getAllValues: () => Promise; +} + type TypedBodyRequestHandler = ( req: Request, unknown, T>, res: Response, @@ -103,6 +109,45 @@ interface ObjectStorageMethodsEntrypointsConfig ValueResponse; } +interface SetStorageMethodsEntrypointsConfig { + path: string; + methods: SetStorageMethods; + arrayValidationSchema: IArraySchema; + successfulAdditionMessage: (addedEntriesCount: number) => string; + successfulRemovalMessage: (removedEntriesCount: number) => string; +} + +export const addSetStorageMethodsToRouter = (router: Router, config: SetStorageMethodsEntrypointsConfig) => { + const { path, methods, arrayValidationSchema, successfulAdditionMessage, successfulRemovalMessage } = config; + + router + .route(path) + .get( + withExceptionHandler(async (_req, res) => { + res + .status(200) + .header('Cache-Control', 'public, max-age=300') + .send(await methods.getAllValues()); + }) + ) + .post( + basicAuth, + withExceptionHandler( + withBodyValidation(arrayValidationSchema, async (req, res) => { + res.status(200).send({ message: successfulAdditionMessage(await methods.addValues(req.body)) }); + }) + ) + ) + .delete( + basicAuth, + withExceptionHandler( + withBodyValidation(arrayValidationSchema, async (req, res) => { + res.status(200).send({ message: successfulRemovalMessage(await methods.removeValues(req.body)) }); + }) + ) + ); +}; + export const addObjectStorageMethodsToRouter = < StoredValue, ObjectResponse = Record,