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