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