Bumped to version v0.0.14-alpha
[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 { isAbsolute, 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   if (!stringParam) return ''
54
55   const entityMap = {
56     '&': '&',
57     '<': '&lt;',
58     '>': '&gt;',
59     '"': '&quot;',
60     "'": '&#39;',
61     '/': '&#x2F;',
62     '`': '&#x60;',
63     '=': '&#x3D;'
64   }
65
66   return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s])
67 }
68
69 function pageToStartAndCount (page: number, itemsPerPage: number) {
70   const start = (page - 1) * itemsPerPage
71
72   return { start, count: itemsPerPage }
73 }
74
75 function buildPath (path: string) {
76   if (isAbsolute(path)) return path
77
78   return join(root(), path)
79 }
80
81 function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
82   return function promisified (): Promise<A> {
83     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
84       func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
85     })
86   }
87 }
88
89 // Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
90 function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
91   return function promisified (arg: T): Promise<A> {
92     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
93       func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
94     })
95   }
96 }
97
98 function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
99   return function promisified (arg: T): Promise<void> {
100     return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
101       func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
102     })
103   }
104 }
105
106 function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
107   return function promisified (arg1: T, arg2: U): Promise<A> {
108     return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
109       func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
110     })
111   }
112 }
113
114 function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
115   return function promisified (arg1: T, arg2: U): Promise<void> {
116     return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
117       func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
118     })
119   }
120 }
121
122 const readFileBufferPromise = promisify1<string, Buffer>(readFile)
123 const unlinkPromise = promisify1WithVoid<string>(unlink)
124 const renamePromise = promisify2WithVoid<string, string>(rename)
125 const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
126 const readdirPromise = promisify1<string, string[]>(readdir)
127 const mkdirpPromise = promisify1<string, string>(mkdirp)
128 const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
129 const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
130 const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
131 const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
132 const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
133 const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
134 const createTorrentPromise = promisify2<string, any, any>(createTorrent)
135 const rimrafPromise = promisify1WithVoid<string>(rimraf)
136 const statPromise = promisify1<string, Stats>(stat)
137
138 // ---------------------------------------------------------------------------
139
140 export {
141   isTestInstance,
142   root,
143   escapeHTML,
144   pageToStartAndCount,
145   sanitizeUrl,
146   sanitizeHost,
147   buildPath,
148
149   promisify0,
150   promisify1,
151
152   readdirPromise,
153   readFileBufferPromise,
154   unlinkPromise,
155   renamePromise,
156   writeFilePromise,
157   mkdirpPromise,
158   pseudoRandomBytesPromise,
159   createPrivateKey,
160   getPublicKey,
161   bcryptComparePromise,
162   bcryptGenSaltPromise,
163   bcryptHashPromise,
164   createTorrentPromise,
165   rimrafPromise,
166   statPromise
167 }