]>
Commit | Line | Data |
---|---|---|
3fd3ab2d | 1 | import { Model } from 'sequelize-typescript' |
ff2c1fe8 | 2 | import * as ipaddr from 'ipaddr.js' |
6fcd19ba | 3 | import { ResultList } from '../../shared' |
3fd3ab2d | 4 | import { VideoResolution } from '../../shared/models/videos' |
0626e7af | 5 | import { CONFIG } from '../initializers' |
3fd3ab2d | 6 | import { UserModel } from '../models/account/user' |
50d6de9c C |
7 | import { ActorModel } from '../models/activitypub/actor' |
8 | import { ApplicationModel } from '../models/application/application' | |
cf7a61b5 | 9 | import { pseudoRandomBytesPromise, unlinkPromise } from './core-utils' |
efc32059 | 10 | import { logger } from './logger' |
cf7a61b5 | 11 | import { isArray } from './custom-validators/misc' |
cbe2f7c3 | 12 | |
2186386c C |
13 | const isCidr = require('is-cidr') |
14 | ||
cf7a61b5 C |
15 | function cleanUpReqFiles (req: { files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[] }) { |
16 | const files = req.files | |
17 | ||
18 | if (!files) return | |
19 | ||
20 | if (isArray(files)) { | |
21 | (files as Express.Multer.File[]).forEach(f => deleteFileAsync(f.path)) | |
22 | return | |
23 | } | |
24 | ||
25 | for (const key of Object.keys(files)) { | |
26 | const file = files[key] | |
27 | ||
28 | if (isArray(file)) file.forEach(f => deleteFileAsync(f.path)) | |
29 | else deleteFileAsync(file.path) | |
30 | } | |
31 | } | |
32 | ||
33 | function deleteFileAsync (path: string) { | |
34 | unlinkPromise(path) | |
35 | .catch(err => logger.error('Cannot delete the file %s asynchronously.', path, { err })) | |
36 | } | |
37 | ||
f5028693 C |
38 | async function generateRandomString (size: number) { |
39 | const raw = await pseudoRandomBytesPromise(size) | |
40 | ||
41 | return raw.toString('hex') | |
e4c87ec2 C |
42 | } |
43 | ||
40298b02 | 44 | interface FormattableToJSON { |
2186386c | 45 | toFormattedJSON (args?: any) |
154898b0 C |
46 | } |
47 | ||
2186386c | 48 | function getFormattedObjects<U, T extends FormattableToJSON> (objects: T[], objectsTotal: number, formattedArg?: any) { |
0aef76c4 | 49 | const formattedObjects: U[] = [] |
55fa55a9 | 50 | |
075f16ca | 51 | objects.forEach(object => { |
2186386c | 52 | formattedObjects.push(object.toFormattedJSON(formattedArg)) |
55fa55a9 C |
53 | }) |
54 | ||
2186386c | 55 | return { |
55fa55a9 | 56 | total: objectsTotal, |
0aef76c4 | 57 | data: formattedObjects |
2186386c | 58 | } as ResultList<U> |
55fa55a9 C |
59 | } |
60 | ||
f5028693 | 61 | async function isSignupAllowed () { |
291e8d3e | 62 | if (CONFIG.SIGNUP.ENABLED === false) { |
f5028693 | 63 | return false |
291e8d3e C |
64 | } |
65 | ||
66 | // No limit and signup is enabled | |
67 | if (CONFIG.SIGNUP.LIMIT === -1) { | |
f5028693 | 68 | return true |
291e8d3e C |
69 | } |
70 | ||
3fd3ab2d | 71 | const totalUsers = await UserModel.countTotal() |
f5028693 C |
72 | |
73 | return totalUsers < CONFIG.SIGNUP.LIMIT | |
291e8d3e C |
74 | } |
75 | ||
ff2c1fe8 RK |
76 | function isSignupAllowedForCurrentIP (ip: string) { |
77 | const addr = ipaddr.parse(ip) | |
78 | let excludeList = [ 'blacklist' ] | |
c1e791ba | 79 | let matched = '' |
ff2c1fe8 RK |
80 | |
81 | // if there is a valid, non-empty whitelist, we exclude all unknown adresses too | |
82 | if (CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr(cidr)).length > 0) { | |
83 | excludeList.push('unknown') | |
84 | } | |
85 | ||
86 | if (addr.kind() === 'ipv4') { | |
87 | const addrV4 = ipaddr.IPv4.parse(ip) | |
88 | const rangeList = { | |
89 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v4(cidr)) | |
90 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)), | |
91 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v4(cidr)) | |
92 | .map(cidr => ipaddr.IPv4.parseCIDR(cidr)) | |
93 | } | |
94 | matched = ipaddr.subnetMatch(addrV4, rangeList, 'unknown') | |
95 | } else if (addr.kind() === 'ipv6') { | |
96 | const addrV6 = ipaddr.IPv6.parse(ip) | |
97 | const rangeList = { | |
98 | whitelist: CONFIG.SIGNUP.FILTERS.CIDR.WHITELIST.filter(cidr => isCidr.v6(cidr)) | |
99 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)), | |
100 | blacklist: CONFIG.SIGNUP.FILTERS.CIDR.BLACKLIST.filter(cidr => isCidr.v6(cidr)) | |
101 | .map(cidr => ipaddr.IPv6.parseCIDR(cidr)) | |
102 | } | |
103 | matched = ipaddr.subnetMatch(addrV6, rangeList, 'unknown') | |
104 | } | |
105 | ||
106 | return !excludeList.includes(matched) | |
107 | } | |
108 | ||
40298b02 C |
109 | function computeResolutionsToTranscode (videoFileHeight: number) { |
110 | const resolutionsEnabled: number[] = [] | |
111 | const configResolutions = CONFIG.TRANSCODING.RESOLUTIONS | |
112 | ||
2186386c | 113 | // Put in the order we want to proceed jobs |
40298b02 | 114 | const resolutions = [ |
40298b02 | 115 | VideoResolution.H_480P, |
2186386c | 116 | VideoResolution.H_360P, |
40298b02 | 117 | VideoResolution.H_720P, |
2186386c | 118 | VideoResolution.H_240P, |
40298b02 C |
119 | VideoResolution.H_1080P |
120 | ] | |
121 | ||
122 | for (const resolution of resolutions) { | |
2186386c | 123 | if (configResolutions[ resolution + 'p' ] === true && videoFileHeight > resolution) { |
40298b02 C |
124 | resolutionsEnabled.push(resolution) |
125 | } | |
126 | } | |
127 | ||
128 | return resolutionsEnabled | |
129 | } | |
130 | ||
3f6d68d9 RK |
131 | const timeTable = { |
132 | ms: 1, | |
133 | second: 1000, | |
134 | minute: 60000, | |
135 | hour: 3600000, | |
136 | day: 3600000 * 24, | |
137 | week: 3600000 * 24 * 7, | |
138 | month: 3600000 * 24 * 30 | |
139 | } | |
98d3324d | 140 | export function parseDuration (duration: number | string): number { |
3f6d68d9 RK |
141 | if (typeof duration === 'number') return duration |
142 | ||
143 | if (typeof duration === 'string') { | |
144 | const split = duration.match(/^([\d\.,]+)\s?(\w+)$/) | |
145 | ||
146 | if (split.length === 3) { | |
147 | const len = parseFloat(split[1]) | |
148 | let unit = split[2].replace(/s$/i,'').toLowerCase() | |
149 | if (unit === 'm') { | |
150 | unit = 'ms' | |
151 | } | |
152 | ||
153 | return (len || 1) * (timeTable[unit] || 0) | |
154 | } | |
155 | } | |
156 | ||
98d3324d | 157 | throw new Error('Duration could not be properly parsed') |
3f6d68d9 RK |
158 | } |
159 | ||
3fd3ab2d | 160 | function resetSequelizeInstance (instance: Model<any>, savedFields: object) { |
eb080476 C |
161 | Object.keys(savedFields).forEach(key => { |
162 | const value = savedFields[key] | |
163 | instance.set(key, value) | |
164 | }) | |
165 | } | |
166 | ||
50d6de9c C |
167 | let serverActor: ActorModel |
168 | async function getServerActor () { | |
169 | if (serverActor === undefined) { | |
170 | const application = await ApplicationModel.load() | |
2cebd797 C |
171 | if (!application) throw Error('Could not load Application from database.') |
172 | ||
50d6de9c | 173 | serverActor = application.Account.Actor |
7a7724e6 C |
174 | } |
175 | ||
50d6de9c C |
176 | if (!serverActor) { |
177 | logger.error('Cannot load server actor.') | |
efc32059 C |
178 | process.exit(0) |
179 | } | |
180 | ||
50d6de9c | 181 | return Promise.resolve(serverActor) |
7a7724e6 C |
182 | } |
183 | ||
792dbaf0 GS |
184 | type SortType = { sortModel: any, sortValue: string } |
185 | ||
9f10b292 | 186 | // --------------------------------------------------------------------------- |
c45f7f84 | 187 | |
65fcc311 | 188 | export { |
cf7a61b5 C |
189 | cleanUpReqFiles, |
190 | deleteFileAsync, | |
65fcc311 | 191 | generateRandomString, |
0aef76c4 | 192 | getFormattedObjects, |
792dbaf0 | 193 | isSignupAllowed, |
ff2c1fe8 | 194 | isSignupAllowedForCurrentIP, |
40298b02 | 195 | computeResolutionsToTranscode, |
eb080476 | 196 | resetSequelizeInstance, |
50d6de9c | 197 | getServerActor, |
0626e7af | 198 | SortType |
65fcc311 | 199 | } |