]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/core-utils.ts
00bc0bdda71ab861af0120ee4a6e0a3ad9571473
[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 { URL } from 'url'
12 import { truncate } from 'lodash'
13 import { exec } from 'child_process'
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 isProdInstance () {
68 return process.env.NODE_ENV === 'production'
69 }
70
71 function root () {
72 // We are in /helpers/utils.js
73 const paths = [ __dirname, '..', '..' ]
74
75 // We are under /dist directory
76 if (process.mainModule && process.mainModule.filename.endsWith('.ts') === false) {
77 paths.push('..')
78 }
79
80 return join.apply(null, paths)
81 }
82
83 // Thanks: https://stackoverflow.com/a/12034334
84 function escapeHTML (stringParam) {
85 if (!stringParam) return ''
86
87 const entityMap = {
88 '&': '&',
89 '<': '&lt;',
90 '>': '&gt;',
91 '"': '&quot;',
92 '\'': '&#39;',
93 '/': '&#x2F;',
94 '`': '&#x60;',
95 '=': '&#x3D;'
96 }
97
98 return String(stringParam).replace(/[&<>"'`=\/]/g, s => entityMap[s])
99 }
100
101 function pageToStartAndCount (page: number, itemsPerPage: number) {
102 const start = (page - 1) * itemsPerPage
103
104 return { start, count: itemsPerPage }
105 }
106
107 function buildPath (path: string) {
108 if (isAbsolute(path)) return path
109
110 return join(root(), path)
111 }
112
113 // Consistent with .length, lodash truncate function is not
114 function peertubeTruncate (str: string, maxLength: number) {
115 const options = {
116 length: maxLength
117 }
118 const truncatedStr = truncate(str, options)
119
120 // The truncated string is okay, we can return it
121 if (truncatedStr.length <= maxLength) return truncatedStr
122
123 // Lodash takes into account all UTF characters, whereas String.prototype.length does not: some characters have a length of 2
124 // We always use the .length so we need to truncate more if needed
125 options.length -= truncatedStr.length - maxLength
126 return truncate(str, options)
127 }
128
129 function sha256 (str: string) {
130 return createHash('sha256').update(str).digest('hex')
131 }
132
133 function promisify0<A> (func: (cb: (err: any, result: A) => void) => void): () => Promise<A> {
134 return function promisified (): Promise<A> {
135 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
136 func.apply(null, [ (err: any, res: A) => err ? reject(err) : resolve(res) ])
137 })
138 }
139 }
140
141 // Thanks to https://gist.github.com/kumasento/617daa7e46f13ecdd9b2
142 function promisify1<T, A> (func: (arg: T, cb: (err: any, result: A) => void) => void): (arg: T) => Promise<A> {
143 return function promisified (arg: T): Promise<A> {
144 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
145 func.apply(null, [ arg, (err: any, res: A) => err ? reject(err) : resolve(res) ])
146 })
147 }
148 }
149
150 function promisify1WithVoid<T> (func: (arg: T, cb: (err: any) => void) => void): (arg: T) => Promise<void> {
151 return function promisified (arg: T): Promise<void> {
152 return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
153 func.apply(null, [ arg, (err: any) => err ? reject(err) : resolve() ])
154 })
155 }
156 }
157
158 function promisify2<T, U, A> (func: (arg1: T, arg2: U, cb: (err: any, result: A) => void) => void): (arg1: T, arg2: U) => Promise<A> {
159 return function promisified (arg1: T, arg2: U): Promise<A> {
160 return new Promise<A>((resolve: (arg: A) => void, reject: (err: any) => void) => {
161 func.apply(null, [ arg1, arg2, (err: any, res: A) => err ? reject(err) : resolve(res) ])
162 })
163 }
164 }
165
166 function promisify2WithVoid<T, U> (func: (arg1: T, arg2: U, cb: (err: any) => void) => void): (arg1: T, arg2: U) => Promise<void> {
167 return function promisified (arg1: T, arg2: U): Promise<void> {
168 return new Promise<void>((resolve: () => void, reject: (err: any) => void) => {
169 func.apply(null, [ arg1, arg2, (err: any) => err ? reject(err) : resolve() ])
170 })
171 }
172 }
173
174 const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
175 const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
176 const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
177 const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
178 const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
179 const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
180 const createTorrentPromise = promisify2<string, any, any>(createTorrent)
181 const execPromise2 = promisify2<string, any, string>(exec)
182 const execPromise = promisify1<string, string>(exec)
183
184 // ---------------------------------------------------------------------------
185
186 export {
187 isTestInstance,
188 isProdInstance,
189
190 root,
191 escapeHTML,
192 pageToStartAndCount,
193 sanitizeUrl,
194 sanitizeHost,
195 buildPath,
196 peertubeTruncate,
197 sha256,
198
199 promisify0,
200 promisify1,
201
202 pseudoRandomBytesPromise,
203 createPrivateKey,
204 getPublicKey,
205 bcryptComparePromise,
206 bcryptGenSaltPromise,
207 bcryptHashPromise,
208 createTorrentPromise,
209 execPromise2,
210 execPromise
211 }