]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/helpers/core-utils.ts
Fix comment deletion with mastodon
[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 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
182 // ---------------------------------------------------------------------------
183
184 export {
185 isTestInstance,
186 isProdInstance,
187
188 root,
189 escapeHTML,
190 pageToStartAndCount,
191 sanitizeUrl,
192 sanitizeHost,
193 buildPath,
194 peertubeTruncate,
195 sha256,
196
197 promisify0,
198 promisify1,
199
200 pseudoRandomBytesPromise,
201 createPrivateKey,
202 getPublicKey,
203 bcryptComparePromise,
204 bcryptGenSaltPromise,
205 bcryptHashPromise,
206 createTorrentPromise
207 }