diff options
Diffstat (limited to 'server/helpers')
-rw-r--r-- | server/helpers/core-utils.ts | 29 | ||||
-rw-r--r-- | server/helpers/database-utils.ts | 10 | ||||
-rw-r--r-- | server/helpers/express-utils.ts | 24 | ||||
-rw-r--r-- | server/helpers/ffmpeg-utils.ts | 23 | ||||
-rw-r--r-- | server/helpers/signup.ts | 59 | ||||
-rw-r--r-- | server/helpers/utils.ts | 151 |
6 files changed, 149 insertions, 147 deletions
diff --git a/server/helpers/core-utils.ts b/server/helpers/core-utils.ts index 3b38da66c..90d2cd9b3 100644 --- a/server/helpers/core-utils.ts +++ b/server/helpers/core-utils.ts | |||
@@ -14,6 +14,35 @@ import * as rimraf from 'rimraf' | |||
14 | import { URL } from 'url' | 14 | import { URL } from 'url' |
15 | import { truncate } from 'lodash' | 15 | import { truncate } from 'lodash' |
16 | 16 | ||
17 | const timeTable = { | ||
18 | ms: 1, | ||
19 | second: 1000, | ||
20 | minute: 60000, | ||
21 | hour: 3600000, | ||
22 | day: 3600000 * 24, | ||
23 | week: 3600000 * 24 * 7, | ||
24 | month: 3600000 * 24 * 30 | ||
25 | } | ||
26 | export function parseDuration (duration: number | string): number { | ||
27 | if (typeof duration === 'number') return duration | ||
28 | |||
29 | if (typeof duration === 'string') { | ||
30 | const split = duration.match(/^([\d\.,]+)\s?(\w+)$/) | ||
31 | |||
32 | if (split.length === 3) { | ||
33 | const len = parseFloat(split[1]) | ||
34 | let unit = split[2].replace(/s$/i,'').toLowerCase() | ||
35 | if (unit === 'm') { | ||
36 | unit = 'ms' | ||
37 | } | ||
38 | |||
39 | return (len || 1) * (timeTable[unit] || 0) | ||
40 | } | ||
41 | } | ||
42 | |||
43 | throw new Error('Duration could not be properly parsed') | ||
44 | } | ||
45 | |||
17 | function sanitizeUrl (url: string) { | 46 | function sanitizeUrl (url: string) { |
18 | const urlObject = new URL(url) | 47 | const urlObject = new URL(url) |
19 | 48 | ||
diff --git a/server/helpers/database-utils.ts b/server/helpers/database-utils.ts index 53f881fb3..1005d2cf1 100644 --- a/server/helpers/database-utils.ts +++ b/server/helpers/database-utils.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | import * as retry from 'async/retry' | 1 | import * as retry from 'async/retry' |
2 | import * as Bluebird from 'bluebird' | 2 | import * as Bluebird from 'bluebird' |
3 | import { Model, Sequelize } from 'sequelize-typescript' | 3 | import { Model } from 'sequelize-typescript' |
4 | import { logger } from './logger' | 4 | import { logger } from './logger' |
5 | 5 | ||
6 | function retryTransactionWrapper <T, A, B, C> ( | 6 | function retryTransactionWrapper <T, A, B, C> ( |
@@ -66,9 +66,17 @@ function updateInstanceWithAnother <T extends Model<T>> (instanceToUpdate: Model | |||
66 | } | 66 | } |
67 | } | 67 | } |
68 | 68 | ||
69 | function resetSequelizeInstance (instance: Model<any>, savedFields: object) { | ||
70 | Object.keys(savedFields).forEach(key => { | ||
71 | const value = savedFields[key] | ||
72 | instance.set(key, value) | ||
73 | }) | ||
74 | } | ||
75 | |||
69 | // --------------------------------------------------------------------------- | 76 | // --------------------------------------------------------------------------- |
70 | 77 | ||
71 | export { | 78 | export { |
79 | resetSequelizeInstance, | ||
72 | retryTransactionWrapper, | 80 | retryTransactionWrapper, |
73 | transactionRetryer, | 81 | transactionRetryer, |
74 | updateInstanceWithAnother | 82 | updateInstanceWithAnother |
diff --git a/server/helpers/express-utils.ts b/server/helpers/express-utils.ts index b3cc40848..1d7bee87e 100644 --- a/server/helpers/express-utils.ts +++ b/server/helpers/express-utils.ts | |||
@@ -3,8 +3,9 @@ import * as multer from 'multer' | |||
3 | import { CONFIG, REMOTE_SCHEME } from '../initializers' | 3 | import { CONFIG, REMOTE_SCHEME } from '../initializers' |
4 | import { logger } from './logger' | 4 | import { logger } from './logger' |
5 | import { User } from '../../shared/models/users' | 5 | import { User } from '../../shared/models/users' |
6 | import { generateRandomString } from './utils' | 6 | import { deleteFileAsync, generateRandomString } from './utils' |
7 | import { extname } from 'path' | 7 | import { extname } from 'path' |
8 | import { isArray } from './custom-validators/misc' | ||
8 | 9 | ||
9 | function buildNSFWFilter (res: express.Response, paramNSFW?: string) { | 10 | function buildNSFWFilter (res: express.Response, paramNSFW?: string) { |
10 | if (paramNSFW === 'true') return true | 11 | if (paramNSFW === 'true') return true |
@@ -23,6 +24,24 @@ function buildNSFWFilter (res: express.Response, paramNSFW?: string) { | |||
23 | return null | 24 | return null |
24 | } | 25 | } |
25 | 26 | ||
27 | function cleanUpReqFiles (req: { files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[] }) { | ||
28 | const files = req.files | ||
29 | |||
30 | if (!files) return | ||
31 | |||
32 | if (isArray(files)) { | ||
33 | (files as Express.Multer.File[]).forEach(f => deleteFileAsync(f.path)) | ||
34 | return | ||
35 | } | ||
36 | |||
37 | for (const key of Object.keys(files)) { | ||
38 | const file = files[ key ] | ||
39 | |||
40 | if (isArray(file)) file.forEach(f => deleteFileAsync(f.path)) | ||
41 | else deleteFileAsync(file.path) | ||
42 | } | ||
43 | } | ||
44 | |||
26 | function getHostWithPort (host: string) { | 45 | function getHostWithPort (host: string) { |
27 | const splitted = host.split(':') | 46 | const splitted = host.split(':') |
28 | 47 | ||
@@ -82,5 +101,6 @@ export { | |||
82 | buildNSFWFilter, | 101 | buildNSFWFilter, |
83 | getHostWithPort, | 102 | getHostWithPort, |
84 | badRequest, | 103 | badRequest, |
85 | createReqFiles | 104 | createReqFiles, |
105 | cleanUpReqFiles | ||
86 | } | 106 | } |
diff --git a/server/helpers/ffmpeg-utils.ts b/server/helpers/ffmpeg-utils.ts index ced56b82d..f8299c36f 100644 --- a/server/helpers/ffmpeg-utils.ts +++ b/server/helpers/ffmpeg-utils.ts | |||
@@ -7,6 +7,28 @@ import { processImage } from './image-utils' | |||
7 | import { logger } from './logger' | 7 | import { logger } from './logger' |
8 | import { checkFFmpegEncoders } from '../initializers/checker' | 8 | import { checkFFmpegEncoders } from '../initializers/checker' |
9 | 9 | ||
10 | function computeResolutionsToTranscode (videoFileHeight: number) { | ||
11 | const resolutionsEnabled: number[] = [] | ||
12 | const configResolutions = CONFIG.TRANSCODING.RESOLUTIONS | ||
13 | |||
14 | // Put in the order we want to proceed jobs | ||
15 | const resolutions = [ | ||
16 | VideoResolution.H_480P, | ||
17 | VideoResolution.H_360P, | ||
18 | VideoResolution.H_720P, | ||
19 | VideoResolution.H_240P, | ||
20 | VideoResolution.H_1080P | ||
21 | ] | ||
22 | |||
23 | for (const resolution of resolutions) { | ||
24 | if (configResolutions[ resolution + 'p' ] === true && videoFileHeight > resolution) { | ||
25 | resolutionsEnabled.push(resolution) | ||
26 | } | ||
27 | } | ||
28 | |||
29 | return resolutionsEnabled | ||
30 | } | ||
31 | |||
10 | async function getVideoFileResolution (path: string) { | 32 | async function getVideoFileResolution (path: string) { |
11 | const videoStream = await getVideoFileStream(path) | 33 | const videoStream = await getVideoFileStream(path) |
12 | 34 | ||
@@ -134,6 +156,7 @@ export { | |||
134 | generateImageFromVideoFile, | 156 | generateImageFromVideoFile, |
135 | transcode, | 157 | transcode, |
136 | getVideoFileFPS, | 158 | getVideoFileFPS, |
159 | computeResolutionsToTranscode, | ||
137 | audio | 160 | audio |
138 | } | 161 | } |
139 | 162 | ||
diff --git a/server/helpers/signup.ts b/server/helpers/signup.ts new file mode 100644 index 000000000..cdce7989d --- /dev/null +++ b/server/helpers/signup.ts | |||
@@ -0,0 +1,59 @@ | |||
1 | import { CONFIG } from '../initializers' | ||
2 | import { UserModel } from '../models/account/user' | ||
3 | import * as ipaddr from 'ipaddr.js' | ||
4 | const isCidr = require('is-cidr') | ||
5 | |||
6 | async function isSignupAllowed () { | ||
7 | if (CONFIG.SIGNUP.ENABLED === false) { | ||
8 | return false | ||
9 | } | ||
10 | |||
11 | // No limit and signup is enabled | ||
12 | if (CONFIG.SIGNUP.LIMIT === -1) { | ||
13 | return true | ||
14 | } | ||
15 | |||
16 | const totalUsers = await UserModel.countTotal() | ||
17 | |||
18 | return totalUsers < CONFIG.SIGNUP.LIMIT | ||
19 | } | ||
20 | |||
21 | function isSignupAllowedForCurrentIP (ip: string) { | ||
22 | const addr = ipaddr.parse(ip) | ||
23 | let excludeList = [ 'blacklist' ] | ||
24 | let matched = '' | ||
25 | |||
26 | // if there is a valid, non-empty whitelist, we exclude all unknown adresses too | ||
27 | if (CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr(cidr)).length > 0) { | ||
28 | excludeList.push('unknown') | ||
29 | } | ||
30 | |||
31 | if (addr.kind() === 'ipv4') { | ||
32 | const addrV4 = ipaddr.IPv4.parse(ip) | ||
33 | const rangeList = { | ||
34 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v4(cidr)) | ||
35 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)), | ||
36 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v4(cidr)) | ||
37 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)) | ||
38 | } | ||
39 | matched = ipaddr.subnetMatch(addrV4, rangeList, 'unknown') | ||
40 | } else if (addr.kind() === 'ipv6') { | ||
41 | const addrV6 = ipaddr.IPv6.parse(ip) | ||
42 | const rangeList = { | ||
43 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v6(cidr)) | ||
44 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)), | ||
45 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v6(cidr)) | ||
46 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)) | ||
47 | } | ||
48 | matched = ipaddr.subnetMatch(addrV6, rangeList, 'unknown') | ||
49 | } | ||
50 | |||
51 | return !excludeList.includes(matched) | ||
52 | } | ||
53 | |||
54 | // --------------------------------------------------------------------------- | ||
55 | |||
56 | export { | ||
57 | isSignupAllowed, | ||
58 | isSignupAllowedForCurrentIP | ||
59 | } | ||
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index eaad55555..703e57887 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts | |||
@@ -1,37 +1,12 @@ | |||
1 | import { Model } from 'sequelize-typescript' | ||
2 | import * as ipaddr from 'ipaddr.js' | ||
3 | import { ResultList } from '../../shared' | 1 | import { ResultList } from '../../shared' |
4 | import { VideoResolution } from '../../shared/models/videos' | ||
5 | import { CONFIG } from '../initializers' | 2 | import { CONFIG } from '../initializers' |
6 | import { UserModel } from '../models/account/user' | ||
7 | import { ActorModel } from '../models/activitypub/actor' | 3 | import { ActorModel } from '../models/activitypub/actor' |
8 | import { ApplicationModel } from '../models/application/application' | 4 | import { ApplicationModel } from '../models/application/application' |
9 | import { pseudoRandomBytesPromise, sha256, unlinkPromise } from './core-utils' | 5 | import { pseudoRandomBytesPromise, sha256, unlinkPromise } from './core-utils' |
10 | import { logger } from './logger' | 6 | import { logger } from './logger' |
11 | import { isArray } from './custom-validators/misc' | ||
12 | import { join } from 'path' | 7 | import { join } from 'path' |
13 | import { Instance as ParseTorrent } from 'parse-torrent' | 8 | import { Instance as ParseTorrent } from 'parse-torrent' |
14 | 9 | ||
15 | const isCidr = require('is-cidr') | ||
16 | |||
17 | function cleanUpReqFiles (req: { files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[] }) { | ||
18 | const files = req.files | ||
19 | |||
20 | if (!files) return | ||
21 | |||
22 | if (isArray(files)) { | ||
23 | (files as Express.Multer.File[]).forEach(f => deleteFileAsync(f.path)) | ||
24 | return | ||
25 | } | ||
26 | |||
27 | for (const key of Object.keys(files)) { | ||
28 | const file = files[key] | ||
29 | |||
30 | if (isArray(file)) file.forEach(f => deleteFileAsync(f.path)) | ||
31 | else deleteFileAsync(file.path) | ||
32 | } | ||
33 | } | ||
34 | |||
35 | function deleteFileAsync (path: string) { | 10 | function deleteFileAsync (path: string) { |
36 | unlinkPromise(path) | 11 | unlinkPromise(path) |
37 | .catch(err => logger.error('Cannot delete the file %s asynchronously.', path, { err })) | 12 | .catch(err => logger.error('Cannot delete the file %s asynchronously.', path, { err })) |
@@ -60,127 +35,23 @@ function getFormattedObjects<U, T extends FormattableToJSON> (objects: T[], obje | |||
60 | } as ResultList<U> | 35 | } as ResultList<U> |
61 | } | 36 | } |
62 | 37 | ||
63 | async function isSignupAllowed () { | ||
64 | if (CONFIG.SIGNUP.ENABLED === false) { | ||
65 | return false | ||
66 | } | ||
67 | |||
68 | // No limit and signup is enabled | ||
69 | if (CONFIG.SIGNUP.LIMIT === -1) { | ||
70 | return true | ||
71 | } | ||
72 | |||
73 | const totalUsers = await UserModel.countTotal() | ||
74 | |||
75 | return totalUsers < CONFIG.SIGNUP.LIMIT | ||
76 | } | ||
77 | |||
78 | function isSignupAllowedForCurrentIP (ip: string) { | ||
79 | const addr = ipaddr.parse(ip) | ||
80 | let excludeList = [ 'blacklist' ] | ||
81 | let matched = '' | ||
82 | |||
83 | // if there is a valid, non-empty whitelist, we exclude all unknown adresses too | ||
84 | if (CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr(cidr)).length > 0) { | ||
85 | excludeList.push('unknown') | ||
86 | } | ||
87 | |||
88 | if (addr.kind() === 'ipv4') { | ||
89 | const addrV4 = ipaddr.IPv4.parse(ip) | ||
90 | const rangeList = { | ||
91 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v4(cidr)) | ||
92 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)), | ||
93 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v4(cidr)) | ||
94 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)) | ||
95 | } | ||
96 | matched = ipaddr.subnetMatch(addrV4, rangeList, 'unknown') | ||
97 | } else if (addr.kind() === 'ipv6') { | ||
98 | const addrV6 = ipaddr.IPv6.parse(ip) | ||
99 | const rangeList = { | ||
100 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v6(cidr)) | ||
101 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)), | ||
102 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v6(cidr)) | ||
103 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)) | ||
104 | } | ||
105 | matched = ipaddr.subnetMatch(addrV6, rangeList, 'unknown') | ||
106 | } | ||
107 | |||
108 | return !excludeList.includes(matched) | ||
109 | } | ||
110 | |||
111 | function computeResolutionsToTranscode (videoFileHeight: number) { | ||
112 | const resolutionsEnabled: number[] = [] | ||
113 | const configResolutions = CONFIG.TRANSCODING.RESOLUTIONS | ||
114 | |||
115 | // Put in the order we want to proceed jobs | ||
116 | const resolutions = [ | ||
117 | VideoResolution.H_480P, | ||
118 | VideoResolution.H_360P, | ||
119 | VideoResolution.H_720P, | ||
120 | VideoResolution.H_240P, | ||
121 | VideoResolution.H_1080P | ||
122 | ] | ||
123 | |||
124 | for (const resolution of resolutions) { | ||
125 | if (configResolutions[ resolution + 'p' ] === true && videoFileHeight > resolution) { | ||
126 | resolutionsEnabled.push(resolution) | ||
127 | } | ||
128 | } | ||
129 | |||
130 | return resolutionsEnabled | ||
131 | } | ||
132 | |||
133 | const timeTable = { | ||
134 | ms: 1, | ||
135 | second: 1000, | ||
136 | minute: 60000, | ||
137 | hour: 3600000, | ||
138 | day: 3600000 * 24, | ||
139 | week: 3600000 * 24 * 7, | ||
140 | month: 3600000 * 24 * 30 | ||
141 | } | ||
142 | export function parseDuration (duration: number | string): number { | ||
143 | if (typeof duration === 'number') return duration | ||
144 | |||
145 | if (typeof duration === 'string') { | ||
146 | const split = duration.match(/^([\d\.,]+)\s?(\w+)$/) | ||
147 | |||
148 | if (split.length === 3) { | ||
149 | const len = parseFloat(split[1]) | ||
150 | let unit = split[2].replace(/s$/i,'').toLowerCase() | ||
151 | if (unit === 'm') { | ||
152 | unit = 'ms' | ||
153 | } | ||
154 | |||
155 | return (len || 1) * (timeTable[unit] || 0) | ||
156 | } | ||
157 | } | ||
158 | |||
159 | throw new Error('Duration could not be properly parsed') | ||
160 | } | ||
161 | |||
162 | function resetSequelizeInstance (instance: Model<any>, savedFields: object) { | ||
163 | Object.keys(savedFields).forEach(key => { | ||
164 | const value = savedFields[key] | ||
165 | instance.set(key, value) | ||
166 | }) | ||
167 | } | ||
168 | |||
169 | let serverActor: ActorModel | ||
170 | async function getServerActor () { | 38 | async function getServerActor () { |
171 | if (serverActor === undefined) { | 39 | if (getServerActor.serverActor === undefined) { |
172 | const application = await ApplicationModel.load() | 40 | const application = await ApplicationModel.load() |
173 | if (!application) throw Error('Could not load Application from database.') | 41 | if (!application) throw Error('Could not load Application from database.') |
174 | 42 | ||
175 | serverActor = application.Account.Actor | 43 | getServerActor.serverActor = application.Account.Actor |
176 | } | 44 | } |
177 | 45 | ||
178 | if (!serverActor) { | 46 | if (!getServerActor.serverActor) { |
179 | logger.error('Cannot load server actor.') | 47 | logger.error('Cannot load server actor.') |
180 | process.exit(0) | 48 | process.exit(0) |
181 | } | 49 | } |
182 | 50 | ||
183 | return Promise.resolve(serverActor) | 51 | return Promise.resolve(getServerActor.serverActor) |
52 | } | ||
53 | namespace getServerActor { | ||
54 | export let serverActor: ActorModel | ||
184 | } | 55 | } |
185 | 56 | ||
186 | function generateVideoTmpPath (target: string | ParseTorrent) { | 57 | function generateVideoTmpPath (target: string | ParseTorrent) { |
@@ -194,21 +65,13 @@ function getSecureTorrentName (originalName: string) { | |||
194 | return sha256(originalName) + '.torrent' | 65 | return sha256(originalName) + '.torrent' |
195 | } | 66 | } |
196 | 67 | ||
197 | type SortType = { sortModel: any, sortValue: string } | ||
198 | |||
199 | // --------------------------------------------------------------------------- | 68 | // --------------------------------------------------------------------------- |
200 | 69 | ||
201 | export { | 70 | export { |
202 | cleanUpReqFiles, | ||
203 | deleteFileAsync, | 71 | deleteFileAsync, |
204 | generateRandomString, | 72 | generateRandomString, |
205 | getFormattedObjects, | 73 | getFormattedObjects, |
206 | isSignupAllowed, | ||
207 | getSecureTorrentName, | 74 | getSecureTorrentName, |
208 | isSignupAllowedForCurrentIP, | ||
209 | computeResolutionsToTranscode, | ||
210 | resetSequelizeInstance, | ||
211 | getServerActor, | 75 | getServerActor, |
212 | SortType, | ||
213 | generateVideoTmpPath | 76 | generateVideoTmpPath |
214 | } | 77 | } |