From b211106695bb82f6c32e53306081b5262c3d109d Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Thu, 24 Mar 2022 13:36:47 +0100 Subject: Support video views/viewers stats in server * Add "currentTime" and "event" body params to view endpoint * Merge watching and view endpoints * Introduce WatchAction AP activity * Add tables to store viewer information of local videos * Add endpoints to fetch video views/viewers stats of local videos * Refactor views/viewers handlers * Support "views" and "viewers" counters for both VOD and live videos --- server/helpers/geo-ip.ts | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 server/helpers/geo-ip.ts (limited to 'server/helpers/geo-ip.ts') diff --git a/server/helpers/geo-ip.ts b/server/helpers/geo-ip.ts new file mode 100644 index 000000000..4ba7011c2 --- /dev/null +++ b/server/helpers/geo-ip.ts @@ -0,0 +1,78 @@ +import { pathExists, writeFile } from 'fs-extra' +import maxmind, { CountryResponse, Reader } from 'maxmind' +import { join } from 'path' +import { CONFIG } from '@server/initializers/config' +import { logger, loggerTagsFactory } from './logger' +import { isBinaryResponse, peertubeGot } from './requests' + +const lTags = loggerTagsFactory('geo-ip') + +const mmbdFilename = 'dbip-country-lite-latest.mmdb' +const mmdbPath = join(CONFIG.STORAGE.BIN_DIR, mmbdFilename) + +export class GeoIP { + private static instance: GeoIP + + private reader: Reader + + private constructor () { + } + + async safeCountryISOLookup (ip: string): Promise { + if (CONFIG.GEO_IP.ENABLED === false) return null + + await this.initReaderIfNeeded() + + try { + const result = this.reader.get(ip) + if (!result) return null + + return result.country.iso_code + } catch (err) { + logger.error('Cannot get country from IP.', { err }) + + return null + } + } + + async updateDatabase () { + if (CONFIG.GEO_IP.ENABLED === false) return + + const url = CONFIG.GEO_IP.COUNTRY.DATABASE_URL + + logger.info('Updating GeoIP database from %s.', url, lTags()) + + const gotOptions = { context: { bodyKBLimit: 200_000 }, responseType: 'buffer' as 'buffer' } + + try { + const gotResult = await peertubeGot(url, gotOptions) + + if (!isBinaryResponse(gotResult)) { + throw new Error('Not a binary response') + } + + await writeFile(mmdbPath, gotResult.body) + + // Reini reader + this.reader = undefined + + logger.info('GeoIP database updated %s.', mmdbPath, lTags()) + } catch (err) { + logger.error('Cannot update GeoIP database from %s.', url, { err, ...lTags() }) + } + } + + private async initReaderIfNeeded () { + if (!this.reader) { + if (!await pathExists(mmdbPath)) { + await this.updateDatabase() + } + + this.reader = await maxmind.open(mmdbPath) + } + } + + static get Instance () { + return this.instance || (this.instance = new this()) + } +} -- cgit v1.2.3