aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/helpers/core-utils.ts
blob: 77547c528d7a600cea1df0045878603a178d93ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/*
  Different from 'utils' because we don't not import other PeerTube modules.
  Useful to avoid circular dependencies.
*/

import * as bcrypt from 'bcrypt'
import * as createTorrent from 'create-torrent'
import { pseudoRandomBytes } from 'crypto'
import { readdir, readFile, rename, stat, Stats, unlink, writeFile } from 'fs'
import * as mkdirp from 'mkdirp'
import { isAbsolute, join } from 'path'
import * as pem from 'pem'
import * as rimraf from 'rimraf'
import { URL } from 'url'

function sanitizeUrl (url: string) {
  const urlObject = new URL(url)

  if (urlObject.protocol === 'https:' && urlObject.port === '443') {
    urlObject.port = ''
  } else if (urlObject.protocol === 'http:' && urlObject.port === '80') {
    urlObject.port = ''
  }

  return urlObject.href.replace(/\/$/, '')
}

// Don't import remote scheme from constants because we are in core utils
function sanitizeHost (host: string, remoteScheme: string) {
  const toRemove = remoteScheme === 'https' ? 443 : 80

  return host.replace(new RegExp(`:${toRemove}$`), '')
}

function isTestInstance () {
  return process.env.NODE_ENV === 'test'
}

function root () {
  // We are in /helpers/utils.js
  const paths = [ __dirname, '..', '..' ]

  // We are under /dist directory
  if (process.mainModule.filename.endsWith('.ts') === false) {
    paths.push('..')
  }

  return join.apply(null, paths)
}

// Thanks: https://stackoverflow.com/a/12034334
function escapeHTML (stringParam) {
  const entityMap = {
    '&': '&',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
    '/': '&#x2F;',
    '`': '&#x60;',
    '=': '&#x3D;'
  }

  return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s])
}

function pageToStartAndCount (page: number, itemsPerPage: number) {
  const start = (page - 1) * itemsPerPage

  return { start, count: itemsPerPage }
}

function buildPath (path: string) {
  if (isAbsolute(path)) return path

  return join(root(), path)
}

function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
  return function promisified (): Promise<A> {
    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
      func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
    })
  }
}

// Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
  return function promisified (arg: T): Promise<A> {
    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
      func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
    })
  }
}

function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
  return function promisified (arg: T): Promise<void> {
    return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
      func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
    })
  }
}

function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
  return function promisified (arg1: T, arg2: U): Promise<A> {
    return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
      func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
    })
  }
}

function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
  return function promisified (arg1: T, arg2: U): Promise<void> {
    return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
      func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
    })
  }
}

const readFileBufferPromise = promisify1<string, Buffer>(readFile)
const unlinkPromise = promisify1WithVoid<string>(unlink)
const renamePromise = promisify2WithVoid<string, string>(rename)
const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
const readdirPromise = promisify1<string, string[]>(readdir)
const mkdirpPromise = promisify1<string, string>(mkdirp)
const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
const createTorrentPromise = promisify2<string, any, any>(createTorrent)
const rimrafPromise = promisify1WithVoid<string>(rimraf)
const statPromise = promisify1<string, Stats>(stat)

// ---------------------------------------------------------------------------

export {
  isTestInstance,
  root,
  escapeHTML,
  pageToStartAndCount,
  sanitizeUrl,
  sanitizeHost,
  buildPath,

  promisify0,
  promisify1,

  readdirPromise,
  readFileBufferPromise,
  unlinkPromise,
  renamePromise,
  writeFilePromise,
  mkdirpPromise,
  pseudoRandomBytesPromise,
  createPrivateKey,
  getPublicKey,
  bcryptComparePromise,
  bcryptGenSaltPromise,
  bcryptHashPromise,
  createTorrentPromise,
  rimrafPromise,
  statPromise
}