geo-ip.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import { pathExists, writeFile } from 'fs-extra'
  2. import maxmind, { CountryResponse, Reader } from 'maxmind'
  3. import { join } from 'path'
  4. import { CONFIG } from '@server/initializers/config'
  5. import { logger, loggerTagsFactory } from './logger'
  6. import { isBinaryResponse, peertubeGot } from './requests'
  7. const lTags = loggerTagsFactory('geo-ip')
  8. const mmbdFilename = 'dbip-country-lite-latest.mmdb'
  9. const mmdbPath = join(CONFIG.STORAGE.BIN_DIR, mmbdFilename)
  10. export class GeoIP {
  11. private static instance: GeoIP
  12. private reader: Reader<CountryResponse>
  13. private constructor () {
  14. }
  15. async safeCountryISOLookup (ip: string): Promise<string> {
  16. if (CONFIG.GEO_IP.ENABLED === false) return null
  17. await this.initReaderIfNeeded()
  18. try {
  19. const result = this.reader.get(ip)
  20. if (!result) return null
  21. return result.country.iso_code
  22. } catch (err) {
  23. logger.error('Cannot get country from IP.', { err })
  24. return null
  25. }
  26. }
  27. async updateDatabase () {
  28. if (CONFIG.GEO_IP.ENABLED === false) return
  29. const url = CONFIG.GEO_IP.COUNTRY.DATABASE_URL
  30. logger.info('Updating GeoIP database from %s.', url, lTags())
  31. const gotOptions = { context: { bodyKBLimit: 200_000 }, responseType: 'buffer' as 'buffer' }
  32. try {
  33. const gotResult = await peertubeGot(url, gotOptions)
  34. if (!isBinaryResponse(gotResult)) {
  35. throw new Error('Not a binary response')
  36. }
  37. await writeFile(mmdbPath, gotResult.body)
  38. // Reinit reader
  39. this.reader = undefined
  40. logger.info('GeoIP database updated %s.', mmdbPath, lTags())
  41. } catch (err) {
  42. logger.error('Cannot update GeoIP database from %s.', url, { err, ...lTags() })
  43. }
  44. }
  45. private async initReaderIfNeeded () {
  46. if (!this.reader) {
  47. if (!await pathExists(mmdbPath)) {
  48. await this.updateDatabase()
  49. }
  50. this.reader = await maxmind.open(mmdbPath)
  51. }
  52. }
  53. static get Instance () {
  54. return this.instance || (this.instance = new this())
  55. }
  56. }