]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/helpers/core-utils.ts
replace fs by fs-extra to prevent EMFILE error
[github/Chocobozzz/PeerTube.git] / server / helpers / core-utils.ts
CommitLineData
1840c2f7
C
1/*
2 Different from 'utils' because we don't not import other PeerTube modules.
3 Useful to avoid circular dependencies.
4*/
5
6fcd19ba
C
6import * as bcrypt from 'bcrypt'
7import * as createTorrent from 'create-torrent'
3e17515e 8import { createHash, pseudoRandomBytes } from 'crypto'
c9d5c64f 9import { copyFile, readdir, readFile, rename, stat, Stats, unlink, writeFile, mkdirp } from 'fs-extra'
0b4204f9 10import { isAbsolute, join } from 'path'
e4f97bab 11import * as pem from 'pem'
8d468a16 12import * as rimraf from 'rimraf'
225a89c2 13import { URL } from 'url'
c73e83da 14import { truncate } from 'lodash'
225a89c2 15
06215f15
C
16const timeTable = {
17 ms: 1,
18 second: 1000,
19 minute: 60000,
20 hour: 3600000,
21 day: 3600000 * 24,
22 week: 3600000 * 24 * 7,
23 month: 3600000 * 24 * 30
24}
25export function parseDuration (duration: number | string): number {
26 if (typeof duration === 'number') return duration
27
28 if (typeof duration === 'string') {
29 const split = duration.match(/^([\d\.,]+)\s?(\w+)$/)
30
31 if (split.length === 3) {
32 const len = parseFloat(split[1])
33 let unit = split[2].replace(/s$/i,'').toLowerCase()
34 if (unit === 'm') {
35 unit = 'ms'
36 }
37
38 return (len || 1) * (timeTable[unit] || 0)
39 }
40 }
41
42 throw new Error('Duration could not be properly parsed')
43}
44
225a89c2
C
45function sanitizeUrl (url: string) {
46 const urlObject = new URL(url)
47
48 if (urlObject.protocol === 'https:' && urlObject.port === '443') {
49 urlObject.port = ''
50 } else if (urlObject.protocol === 'http:' && urlObject.port === '80') {
51 urlObject.port = ''
52 }
53
54 return urlObject.href.replace(/\/$/, '')
55}
56
57// Don't import remote scheme from constants because we are in core utils
58function sanitizeHost (host: string, remoteScheme: string) {
604abfbe 59 const toRemove = remoteScheme === 'https' ? 443 : 80
225a89c2
C
60
61 return host.replace(new RegExp(`:${toRemove}$`), '')
62}
1840c2f7
C
63
64function isTestInstance () {
65 return process.env.NODE_ENV === 'test'
66}
67
68function root () {
fdbda9e3
C
69 // We are in /helpers/utils.js
70 const paths = [ __dirname, '..', '..' ]
71
72 // We are under /dist directory
c1e791ba 73 if (process.mainModule && process.mainModule.filename.endsWith('.ts') === false) {
fdbda9e3
C
74 paths.push('..')
75 }
76
77 return join.apply(null, paths)
1840c2f7
C
78}
79
49347a0a
C
80// Thanks: https://stackoverflow.com/a/12034334
81function escapeHTML (stringParam) {
23e27dd5
C
82 if (!stringParam) return ''
83
49347a0a
C
84 const entityMap = {
85 '&': '&',
86 '<': '&lt;',
87 '>': '&gt;',
88 '"': '&quot;',
cf7a61b5 89 '\'': '&#39;',
49347a0a
C
90 '/': '&#x2F;',
91 '`': '&#x60;',
92 '=': '&#x3D;'
93 }
94
95 return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s])
96}
97
e4f97bab
C
98function pageToStartAndCount (page: number, itemsPerPage: number) {
99 const start = (page - 1) * itemsPerPage
100
101 return { start, count: itemsPerPage }
102}
103
0b4204f9
C
104function buildPath (path: string) {
105 if (isAbsolute(path)) return path
106
107 return join(root(), path)
108}
109
c73e83da
C
110// Consistent with .length, lodash truncate function is not
111function peertubeTruncate (str: string, maxLength: number) {
112 const options = {
113 length: maxLength
114 }
115 const truncatedStr = truncate(str, options)
116
117 // The truncated string is okay, we can return it
118 if (truncatedStr.length <= maxLength) return truncatedStr
119
120 // Lodash takes into account all UTF characters, whereas String.prototype.length does not: some characters have a length of 2
121 // We always use the .length so we need to truncate more if needed
122 options.length -= truncatedStr.length - maxLength
123 return truncate(str, options)
124}
125
990b6a0b 126function sha256 (str: string) {
3e17515e 127 return createHash('sha256').update(str).digest('hex')
990b6a0b
C
128}
129
6fcd19ba
C
130function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
131 return function promisified (): Promise<A> {
132 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
133 func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
134 })
135 }
136}
137
138// Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
139function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
140 return function promisified (arg: T): Promise<A> {
141 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
142 func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
143 })
144 }
145}
146
147function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
148 return function promisified (arg: T): Promise<void> {
149 return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
150 func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
151 })
152 }
153}
154
155function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
156 return function promisified (arg1: T, arg2: U): Promise<A> {
157 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
158 func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
159 })
160 }
161}
162
163function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
164 return function promisified (arg1: T, arg2: U): Promise<void> {
165 return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
166 func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
167 })
168 }
169}
170
0138af92 171const copyFilePromise = promisify2WithVoid<string, string>(copyFile)
6fcd19ba
C
172const readFileBufferPromise = promisify1<string, Buffer>(readFile)
173const unlinkPromise = promisify1WithVoid<string>(unlink)
174const renamePromise = promisify2WithVoid<string, string>(rename)
556ddc31 175const writeFilePromise = promisify2WithVoid<string, any>(writeFile)
6fcd19ba
C
176const readdirPromise = promisify1<string, string[]>(readdir)
177const mkdirpPromise = promisify1<string, string>(mkdirp)
c1e791ba 178// we cannot modify the Promise types, so we should make the promisify instance check mkdirp
6fcd19ba 179const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
e4f97bab
C
180const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
181const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
6fcd19ba
C
182const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
183const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
53abc4c2 184const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
6fcd19ba 185const createTorrentPromise = promisify2<string, any, any>(createTorrent)
f981dae8 186const rimrafPromise = promisify1WithVoid<string>(rimraf)
40298b02 187const statPromise = promisify1<string, Stats>(stat)
6fcd19ba 188
1840c2f7
C
189// ---------------------------------------------------------------------------
190
191export {
192 isTestInstance,
6fcd19ba 193 root,
49347a0a 194 escapeHTML,
e4f97bab 195 pageToStartAndCount,
225a89c2
C
196 sanitizeUrl,
197 sanitizeHost,
0b4204f9 198 buildPath,
c73e83da 199 peertubeTruncate,
990b6a0b 200 sha256,
6fcd19ba
C
201
202 promisify0,
203 promisify1,
e4f97bab 204
0138af92 205 copyFilePromise,
6fcd19ba 206 readdirPromise,
6fcd19ba
C
207 readFileBufferPromise,
208 unlinkPromise,
209 renamePromise,
210 writeFilePromise,
211 mkdirpPromise,
212 pseudoRandomBytesPromise,
e4f97bab
C
213 createPrivateKey,
214 getPublicKey,
6fcd19ba
C
215 bcryptComparePromise,
216 bcryptGenSaltPromise,
217 bcryptHashPromise,
f981dae8 218 createTorrentPromise,
40298b02 219 rimrafPromise,
efc32059 220 statPromise
1840c2f7 221}