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