]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame_incremental - server/helpers/core-utils.ts
Merge branch 'release/v1.0.0' into develop
[github/Chocobozzz/PeerTube.git] / server / helpers / core-utils.ts
... / ...
CommitLineData
1/*
2 Different from 'utils' because we don't not import other PeerTube modules.
3 Useful to avoid circular dependencies.
4*/
5
6import * as bcrypt from 'bcrypt'
7import * as createTorrent from 'create-torrent'
8import { createHash, pseudoRandomBytes } from 'crypto'
9import { isAbsolute, join } from 'path'
10import * as pem from 'pem'
11import { URL } from 'url'
12import { truncate } from 'lodash'
13import { exec } from 'child_process'
14
15const 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}
24export 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
44function 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
57function sanitizeHost (host: string, remoteScheme: string) {
58 const toRemove = remoteScheme === 'https' ? 443 : 80
59
60 return host.replace(new RegExp(`:${toRemove}$`), '')
61}
62
63function isTestInstance () {
64 return process.env.NODE_ENV === 'test'
65}
66
67function isProdInstance () {
68 return process.env.NODE_ENV === 'production'
69}
70
71function 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
84function 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
101function pageToStartAndCount (page: number, itemsPerPage: number) {
102 const start = (page - 1) * itemsPerPage
103
104 return { start, count: itemsPerPage }
105}
106
107function 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
114function 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
129function sha256 (str: string) {
130 return createHash('sha256').update(str).digest('hex')
131}
132
133function 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
142function 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
150function 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
158function 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
166function 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
174const pseudoRandomBytesPromise = promisify1<number, Buffer>(pseudoRandomBytes)
175const createPrivateKey = promisify1<number, { key: string }>(pem.createPrivateKey)
176const getPublicKey = promisify1<string, { publicKey: string }>(pem.getPublicKey)
177const bcryptComparePromise = promisify2<any, string, boolean>(bcrypt.compare)
178const bcryptGenSaltPromise = promisify1<number, string>(bcrypt.genSalt)
179const bcryptHashPromise = promisify2<any, string | number, string>(bcrypt.hash)
180const createTorrentPromise = promisify2<string, any, any>(createTorrent)
181const execPromise2 = promisify2<string, any, string>(exec)
182const execPromise = promisify1<string, string>(exec)
183
184// ---------------------------------------------------------------------------
185
186export {
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}