Update follower/following counts
[oweals/peertube.git] / server / helpers / core-utils.ts
1 /*
2   Different from 'utils' because we don't not import other PeerTube modules.
3   Useful to avoid circular dependencies.
4 */
5
6 import * as bcrypt from 'bcrypt'
7 import * as createTorrent from 'create-torrent'
8 import { pseudoRandomBytes } from 'crypto'
9 import { readdir, readFile, rename, stat, Stats, unlink, writeFile } from 'fs'
10 import * as mkdirp from 'mkdirp'
11 import { join } from 'path'
12 import * as pem from 'pem'
13 import * as rimraf from 'rimraf'
14 import { URL } from 'url'
15
16 function sanitizeUrl (url: string) {
17   const urlObject = new URL(url)
18
19   if (urlObject.protocol === 'https:' && urlObject.port === '443') {
20     urlObject.port = ''
21   } else if (urlObject.protocol === 'http:' && urlObject.port === '80') {
22     urlObject.port = ''
23   }
24
25   return urlObject.href.replace(/\/$/, '')
26 }
27
28 // Don't import remote scheme from constants because we are in core utils
29 function sanitizeHost (host: string, remoteScheme: string) {
30   const toRemove = remoteScheme === 'https' ? 443 : 80
31
32   return host.replace(new RegExp(`:${toRemove}$`), '')
33 }
34
35 function isTestInstance () {
36   return process.env.NODE_ENV === 'test'
37 }
38
39 function root () {
40   // We are in /helpers/utils.js
41   const paths = [ __dirname, '..', '..' ]
42
43   // We are under /dist directory
44   if (process.mainModule.filename.endsWith('.ts') === false) {
45     paths.push('..')
46   }
47
48   return join.apply(null, paths)
49 }
50
51 // Thanks: https://stackoverflow.com/a/12034334
52 function escapeHTML (stringParam) {
53   const entityMap = {
54     '&': '&',
55     '<': '&lt;',
56     '>': '&gt;',
57     '"': '&quot;',
58     "'": '&#39;',
59     '/': '&#x2F;',
60     '`': '&#x60;',
61     '=': '&#x3D;'
62   }
63
64   return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s])
65 }
66
67 function pageToStartAndCount (page: number, itemsPerPage: number) {
68   const start = (page - 1) * itemsPerPage
69
70   return { start, count: itemsPerPage }
71 }
72
73 function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
74   return function promisified (): Promise<A> {
75     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
76       func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
77     })
78   }
79 }
80
81 // Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
82 function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
83   return function promisified (arg: T): Promise<A> {
84     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
85       func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
86     })
87   }
88 }
89
90 function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
91   return function promisified (arg: T): Promise<void> {
92     return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
93       func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
94     })
95   }
96 }
97
98 function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
99   return function promisified (arg1: T, arg2: U): Promise<A> {
100     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
101       func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
102     })
103   }
104 }
105
106 function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
107   return function promisified (arg1: T, arg2: U): Promise<void> {
108     return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
109       func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
110     })
111   }
112 }
113
114 const readFileBufferPromise = promisify1<string, Buffer>(readFile)
115 const unlinkPromise = promisify1WithVoid<string>(unlink)
116 const renamePromise = promisify2WithVoid<string, string>(rename)
117 const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
118 const readdirPromise = promisify1<string, string[]>(readdir)
119 const mkdirpPromise = promisify1<string, string>(mkdirp)
120 const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
121 const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
122 const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
123 const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
124 const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
125 const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
126 const createTorrentPromise = promisify2<string, any, any>(createTorrent)
127 const rimrafPromise = promisify1WithVoid<string>(rimraf)
128 const statPromise = promisify1<string, Stats>(stat)
129
130 // ---------------------------------------------------------------------------
131
132 export {
133   isTestInstance,
134   root,
135   escapeHTML,
136   pageToStartAndCount,
137   sanitizeUrl,
138   sanitizeHost,
139
140   promisify0,
141   promisify1,
142
143   readdirPromise,
144   readFileBufferPromise,
145   unlinkPromise,
146   renamePromise,
147   writeFilePromise,
148   mkdirpPromise,
149   pseudoRandomBytesPromise,
150   createPrivateKey,
151   getPublicKey,
152   bcryptComparePromise,
153   bcryptGenSaltPromise,
154   bcryptHashPromise,
155   createTorrentPromise,
156   rimrafPromise,
157   statPromise
158 }