aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/controllers/api
diff options
context:
space:
mode:
Diffstat (limited to 'server/controllers/api')
-rw-r--r--server/controllers/api/accounts.ts13
-rw-r--r--server/controllers/api/config.ts33
-rw-r--r--server/controllers/api/index.ts4
-rw-r--r--server/controllers/api/jobs.ts2
-rw-r--r--server/controllers/api/overviews.ts2
-rw-r--r--server/controllers/api/server/debug.ts6
-rw-r--r--server/controllers/api/server/follows.ts5
-rw-r--r--server/controllers/api/server/logs.ts10
-rw-r--r--server/controllers/api/server/redundancy.ts84
-rw-r--r--server/controllers/api/server/stats.ts10
-rw-r--r--server/controllers/api/users/index.ts18
-rw-r--r--server/controllers/api/users/me.ts4
-rw-r--r--server/controllers/api/users/my-subscriptions.ts6
-rw-r--r--server/controllers/api/video-channel.ts3
-rw-r--r--server/controllers/api/video-playlist.ts1
-rw-r--r--server/controllers/api/videos/captions.ts2
-rw-r--r--server/controllers/api/videos/import.ts22
-rw-r--r--server/controllers/api/videos/index.ts7
18 files changed, 160 insertions, 72 deletions
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 05740318e..f354ccf24 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -16,21 +16,17 @@ import {
16 accountNameWithHostGetValidator, 16 accountNameWithHostGetValidator,
17 accountsSortValidator, 17 accountsSortValidator,
18 ensureAuthUserOwnsAccountValidator, 18 ensureAuthUserOwnsAccountValidator,
19 videosSortValidator, 19 videoChannelsSortValidator,
20 videoChannelsSortValidator 20 videosSortValidator
21} from '../../middlewares/validators' 21} from '../../middlewares/validators'
22import { AccountModel } from '../../models/account/account' 22import { AccountModel } from '../../models/account/account'
23import { AccountVideoRateModel } from '../../models/account/account-video-rate' 23import { AccountVideoRateModel } from '../../models/account/account-video-rate'
24import { VideoModel } from '../../models/video/video' 24import { VideoModel } from '../../models/video/video'
25import { buildNSFWFilter, isUserAbleToSearchRemoteURI, getCountVideos } from '../../helpers/express-utils' 25import { buildNSFWFilter, getCountVideos, isUserAbleToSearchRemoteURI } from '../../helpers/express-utils'
26import { VideoChannelModel } from '../../models/video/video-channel' 26import { VideoChannelModel } from '../../models/video/video-channel'
27import { JobQueue } from '../../lib/job-queue' 27import { JobQueue } from '../../lib/job-queue'
28import { logger } from '../../helpers/logger'
29import { VideoPlaylistModel } from '../../models/video/video-playlist' 28import { VideoPlaylistModel } from '../../models/video/video-playlist'
30import { 29import { commonVideoPlaylistFiltersValidator, videoPlaylistsSearchValidator } from '../../middlewares/validators/videos/video-playlists'
31 commonVideoPlaylistFiltersValidator,
32 videoPlaylistsSearchValidator
33} from '../../middlewares/validators/videos/video-playlists'
34 30
35const accountsRouter = express.Router() 31const accountsRouter = express.Router()
36 32
@@ -104,7 +100,6 @@ function getAccount (req: express.Request, res: express.Response) {
104 100
105 if (account.isOutdated()) { 101 if (account.isOutdated()) {
106 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } }) 102 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: account.Actor.url } })
107 .catch(err => logger.error('Cannot create AP refresher job for actor %s.', account.Actor.url, { err }))
108 } 103 }
109 104
110 return res.json(account.toFormattedJSON()) 105 return res.json(account.toFormattedJSON())
diff --git a/server/controllers/api/config.ts b/server/controllers/api/config.ts
index ae4e00248..06fe30371 100644
--- a/server/controllers/api/config.ts
+++ b/server/controllers/api/config.ts
@@ -11,10 +11,9 @@ import { ClientHtml } from '../../lib/client-html'
11import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger' 11import { auditLoggerFactory, CustomConfigAuditView, getAuditIdFromRes } from '../../helpers/audit-logger'
12import { remove, writeJSON } from 'fs-extra' 12import { remove, writeJSON } from 'fs-extra'
13import { getServerCommit } from '../../helpers/utils' 13import { getServerCommit } from '../../helpers/utils'
14import { Emailer } from '../../lib/emailer'
15import validator from 'validator' 14import validator from 'validator'
16import { objectConverter } from '../../helpers/core-utils' 15import { objectConverter } from '../../helpers/core-utils'
17import { CONFIG, reloadConfig } from '../../initializers/config' 16import { CONFIG, isEmailEnabled, reloadConfig } from '../../initializers/config'
18import { PluginManager } from '../../lib/plugins/plugin-manager' 17import { PluginManager } from '../../lib/plugins/plugin-manager'
19import { getThemeOrDefault } from '../../lib/plugins/theme-utils' 18import { getThemeOrDefault } from '../../lib/plugins/theme-utils'
20import { Hooks } from '@server/lib/plugins/hooks' 19import { Hooks } from '@server/lib/plugins/hooks'
@@ -31,12 +30,12 @@ configRouter.get('/',
31configRouter.get('/custom', 30configRouter.get('/custom',
32 authenticate, 31 authenticate,
33 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), 32 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
34 asyncMiddleware(getCustomConfig) 33 getCustomConfig
35) 34)
36configRouter.put('/custom', 35configRouter.put('/custom',
37 authenticate, 36 authenticate,
38 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION), 37 ensureUserHasRight(UserRight.MANAGE_CONFIGURATION),
39 asyncMiddleware(customConfigUpdateValidator), 38 customConfigUpdateValidator,
40 asyncMiddleware(updateCustomConfig) 39 asyncMiddleware(updateCustomConfig)
41) 40)
42configRouter.delete('/custom', 41configRouter.delete('/custom',
@@ -73,6 +72,12 @@ async function getConfig (req: express.Request, res: express.Response) {
73 css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS 72 css: CONFIG.INSTANCE.CUSTOMIZATIONS.CSS
74 } 73 }
75 }, 74 },
75 search: {
76 remoteUri: {
77 users: CONFIG.SEARCH.REMOTE_URI.USERS,
78 anonymous: CONFIG.SEARCH.REMOTE_URI.ANONYMOUS
79 }
80 },
76 plugin: { 81 plugin: {
77 registered: getRegisteredPlugins() 82 registered: getRegisteredPlugins()
78 }, 83 },
@@ -81,7 +86,7 @@ async function getConfig (req: express.Request, res: express.Response) {
81 default: defaultTheme 86 default: defaultTheme
82 }, 87 },
83 email: { 88 email: {
84 enabled: Emailer.isEnabled() 89 enabled: isEmailEnabled()
85 }, 90 },
86 contactForm: { 91 contactForm: {
87 enabled: CONFIG.CONTACT_FORM.ENABLED 92 enabled: CONFIG.CONTACT_FORM.ENABLED
@@ -196,7 +201,7 @@ function getAbout (req: express.Request, res: express.Response) {
196 return res.json(about).end() 201 return res.json(about).end()
197} 202}
198 203
199async function getCustomConfig (req: express.Request, res: express.Response) { 204function getCustomConfig (req: express.Request, res: express.Response) {
200 const data = customConfig() 205 const data = customConfig()
201 206
202 return res.json(data).end() 207 return res.json(data).end()
@@ -250,7 +255,7 @@ function getRegisteredThemes () {
250 255
251function getEnabledResolutions () { 256function getEnabledResolutions () {
252 return Object.keys(CONFIG.TRANSCODING.RESOLUTIONS) 257 return Object.keys(CONFIG.TRANSCODING.RESOLUTIONS)
253 .filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[ key ] === true) 258 .filter(key => CONFIG.TRANSCODING.ENABLED && CONFIG.TRANSCODING.RESOLUTIONS[key] === true)
254 .map(r => parseInt(r, 10)) 259 .map(r => parseInt(r, 10))
255} 260}
256 261
@@ -340,13 +345,13 @@ function customConfig (): CustomConfig {
340 allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES, 345 allowAudioFiles: CONFIG.TRANSCODING.ALLOW_AUDIO_FILES,
341 threads: CONFIG.TRANSCODING.THREADS, 346 threads: CONFIG.TRANSCODING.THREADS,
342 resolutions: { 347 resolutions: {
343 '0p': CONFIG.TRANSCODING.RESOLUTIONS[ '0p' ], 348 '0p': CONFIG.TRANSCODING.RESOLUTIONS['0p'],
344 '240p': CONFIG.TRANSCODING.RESOLUTIONS[ '240p' ], 349 '240p': CONFIG.TRANSCODING.RESOLUTIONS['240p'],
345 '360p': CONFIG.TRANSCODING.RESOLUTIONS[ '360p' ], 350 '360p': CONFIG.TRANSCODING.RESOLUTIONS['360p'],
346 '480p': CONFIG.TRANSCODING.RESOLUTIONS[ '480p' ], 351 '480p': CONFIG.TRANSCODING.RESOLUTIONS['480p'],
347 '720p': CONFIG.TRANSCODING.RESOLUTIONS[ '720p' ], 352 '720p': CONFIG.TRANSCODING.RESOLUTIONS['720p'],
348 '1080p': CONFIG.TRANSCODING.RESOLUTIONS[ '1080p' ], 353 '1080p': CONFIG.TRANSCODING.RESOLUTIONS['1080p'],
349 '2160p': CONFIG.TRANSCODING.RESOLUTIONS[ '2160p' ] 354 '2160p': CONFIG.TRANSCODING.RESOLUTIONS['2160p']
350 }, 355 },
351 webtorrent: { 356 webtorrent: {
352 enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED 357 enabled: CONFIG.TRANSCODING.WEBTORRENT.ENABLED
diff --git a/server/controllers/api/index.ts b/server/controllers/api/index.ts
index 6138a32de..7bec6c527 100644
--- a/server/controllers/api/index.ts
+++ b/server/controllers/api/index.ts
@@ -1,5 +1,4 @@
1import * as express from 'express' 1import * as express from 'express'
2import * as RateLimit from 'express-rate-limit'
3import { configRouter } from './config' 2import { configRouter } from './config'
4import { jobsRouter } from './jobs' 3import { jobsRouter } from './jobs'
5import { oauthClientsRouter } from './oauth-clients' 4import { oauthClientsRouter } from './oauth-clients'
@@ -15,6 +14,7 @@ import { overviewsRouter } from './overviews'
15import { videoPlaylistRouter } from './video-playlist' 14import { videoPlaylistRouter } from './video-playlist'
16import { CONFIG } from '../../initializers/config' 15import { CONFIG } from '../../initializers/config'
17import { pluginRouter } from './plugins' 16import { pluginRouter } from './plugins'
17import * as RateLimit from 'express-rate-limit'
18 18
19const apiRouter = express.Router() 19const apiRouter = express.Router()
20 20
@@ -24,8 +24,6 @@ apiRouter.use(cors({
24 credentials: true 24 credentials: true
25})) 25}))
26 26
27// FIXME: https://github.com/nfriedly/express-rate-limit/issues/138
28// @ts-ignore
29const apiRateLimiter = RateLimit({ 27const apiRateLimiter = RateLimit({
30 windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS, 28 windowMs: CONFIG.RATES_LIMIT.API.WINDOW_MS,
31 max: CONFIG.RATES_LIMIT.API.MAX 29 max: CONFIG.RATES_LIMIT.API.MAX
diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts
index 05320311e..13fc04d18 100644
--- a/server/controllers/api/jobs.ts
+++ b/server/controllers/api/jobs.ts
@@ -50,7 +50,7 @@ async function listJobs (req: express.Request, res: express.Response) {
50 }) 50 })
51 const total = await JobQueue.Instance.count(state) 51 const total = await JobQueue.Instance.count(state)
52 52
53 const result: ResultList<any> = { 53 const result: ResultList<Job> = {
54 total, 54 total,
55 data: jobs.map(j => formatJob(j, state)) 55 data: jobs.map(j => formatJob(j, state))
56 } 56 }
diff --git a/server/controllers/api/overviews.ts b/server/controllers/api/overviews.ts
index 46e76ac6b..75f3baedb 100644
--- a/server/controllers/api/overviews.ts
+++ b/server/controllers/api/overviews.ts
@@ -24,7 +24,7 @@ export { overviewsRouter }
24const buildSamples = memoizee(async function () { 24const buildSamples = memoizee(async function () {
25 const [ categories, channels, tags ] = await Promise.all([ 25 const [ categories, channels, tags ] = await Promise.all([
26 VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT), 26 VideoModel.getRandomFieldSamples('category', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT),
27 VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD ,OVERVIEWS.VIDEOS.SAMPLES_COUNT), 27 VideoModel.getRandomFieldSamples('channelId', OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT),
28 TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT) 28 TagModel.getRandomSamples(OVERVIEWS.VIDEOS.SAMPLE_THRESHOLD, OVERVIEWS.VIDEOS.SAMPLES_COUNT)
29 ]) 29 ])
30 30
diff --git a/server/controllers/api/server/debug.ts b/server/controllers/api/server/debug.ts
index 4450038f6..e12fc1dd4 100644
--- a/server/controllers/api/server/debug.ts
+++ b/server/controllers/api/server/debug.ts
@@ -1,13 +1,13 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../../../shared/models/users' 2import { UserRight } from '../../../../shared/models/users'
3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' 3import { authenticate, ensureUserHasRight } from '../../../middlewares'
4 4
5const debugRouter = express.Router() 5const debugRouter = express.Router()
6 6
7debugRouter.get('/debug', 7debugRouter.get('/debug',
8 authenticate, 8 authenticate,
9 ensureUserHasRight(UserRight.MANAGE_DEBUG), 9 ensureUserHasRight(UserRight.MANAGE_DEBUG),
10 asyncMiddleware(getDebug) 10 getDebug
11) 11)
12 12
13// --------------------------------------------------------------------------- 13// ---------------------------------------------------------------------------
@@ -18,7 +18,7 @@ export {
18 18
19// --------------------------------------------------------------------------- 19// ---------------------------------------------------------------------------
20 20
21async function getDebug (req: express.Request, res: express.Response) { 21function getDebug (req: express.Request, res: express.Response) {
22 return res.json({ 22 return res.json({
23 ip: req.ip 23 ip: req.ip
24 }).end() 24 }).end()
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 29a403a43..0bc20bd60 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -24,7 +24,7 @@ import {
24} from '../../../middlewares/validators' 24} from '../../../middlewares/validators'
25import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 25import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
26import { JobQueue } from '../../../lib/job-queue' 26import { JobQueue } from '../../../lib/job-queue'
27import { removeRedundancyOf } from '../../../lib/redundancy' 27import { removeRedundanciesOfServer } from '../../../lib/redundancy'
28import { sequelizeTypescript } from '../../../initializers/database' 28import { sequelizeTypescript } from '../../../initializers/database'
29import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow' 29import { autoFollowBackIfNeeded } from '../../../lib/activitypub/follow'
30 30
@@ -135,7 +135,6 @@ async function followInstance (req: express.Request, res: express.Response) {
135 } 135 }
136 136
137 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 137 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
138 .catch(err => logger.error('Cannot create follow job for %s.', host, err))
139 } 138 }
140 139
141 return res.status(204).end() 140 return res.status(204).end()
@@ -153,7 +152,7 @@ async function removeFollowing (req: express.Request, res: express.Response) {
153 await server.save({ transaction: t }) 152 await server.save({ transaction: t })
154 153
155 // Async, could be long 154 // Async, could be long
156 removeRedundancyOf(server.id) 155 removeRedundanciesOfServer(server.id)
157 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err)) 156 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, err))
158 157
159 await follow.destroy({ transaction: t }) 158 await follow.destroy({ transaction: t })
diff --git a/server/controllers/api/server/logs.ts b/server/controllers/api/server/logs.ts
index cd1e0f5bf..4b543d686 100644
--- a/server/controllers/api/server/logs.ts
+++ b/server/controllers/api/server/logs.ts
@@ -59,9 +59,9 @@ async function getLogs (req: express.Request, res: express.Response) {
59} 59}
60 60
61async function generateOutput (options: { 61async function generateOutput (options: {
62 startDateQuery: string, 62 startDateQuery: string
63 endDateQuery?: string, 63 endDateQuery?: string
64 level: LogLevel, 64 level: LogLevel
65 nameFilter: RegExp 65 nameFilter: RegExp
66}) { 66}) {
67 const { startDateQuery, level, nameFilter } = options 67 const { startDateQuery, level, nameFilter } = options
@@ -111,7 +111,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
111 const output: any[] = [] 111 const output: any[] = []
112 112
113 for (let i = lines.length - 1; i >= 0; i--) { 113 for (let i = lines.length - 1; i >= 0; i--) {
114 const line = lines[ i ] 114 const line = lines[i]
115 let log: any 115 let log: any
116 116
117 try { 117 try {
@@ -122,7 +122,7 @@ async function getOutputFromFile (path: string, startDate: Date, endDate: Date,
122 } 122 }
123 123
124 logTime = new Date(log.timestamp).getTime() 124 logTime = new Date(log.timestamp).getTime()
125 if (logTime >= startTime && logTime <= endTime && logsLevel[ log.level ] >= logsLevel[ level ]) { 125 if (logTime >= startTime && logTime <= endTime && logsLevel[log.level] >= logsLevel[level]) {
126 output.push(log) 126 output.push(log)
127 127
128 currentSize += line.length 128 currentSize += line.length
diff --git a/server/controllers/api/server/redundancy.ts b/server/controllers/api/server/redundancy.ts
index 4ea6164a3..1ced0759e 100644
--- a/server/controllers/api/server/redundancy.ts
+++ b/server/controllers/api/server/redundancy.ts
@@ -1,9 +1,24 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../../../shared/models/users' 2import { UserRight } from '../../../../shared/models/users'
3import { asyncMiddleware, authenticate, ensureUserHasRight } from '../../../middlewares' 3import {
4import { updateServerRedundancyValidator } from '../../../middlewares/validators/redundancy' 4 asyncMiddleware,
5import { removeRedundancyOf } from '../../../lib/redundancy' 5 authenticate,
6 ensureUserHasRight,
7 paginationValidator,
8 setDefaultPagination,
9 setDefaultVideoRedundanciesSort,
10 videoRedundanciesSortValidator
11} from '../../../middlewares'
12import {
13 listVideoRedundanciesValidator,
14 updateServerRedundancyValidator,
15 addVideoRedundancyValidator,
16 removeVideoRedundancyValidator
17} from '../../../middlewares/validators/redundancy'
18import { removeRedundanciesOfServer, removeVideoRedundancy } from '../../../lib/redundancy'
6import { logger } from '../../../helpers/logger' 19import { logger } from '../../../helpers/logger'
20import { VideoRedundancyModel } from '@server/models/redundancy/video-redundancy'
21import { JobQueue } from '@server/lib/job-queue'
7 22
8const serverRedundancyRouter = express.Router() 23const serverRedundancyRouter = express.Router()
9 24
@@ -14,6 +29,31 @@ serverRedundancyRouter.put('/redundancy/:host',
14 asyncMiddleware(updateRedundancy) 29 asyncMiddleware(updateRedundancy)
15) 30)
16 31
32serverRedundancyRouter.get('/redundancy/videos',
33 authenticate,
34 ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES),
35 listVideoRedundanciesValidator,
36 paginationValidator,
37 videoRedundanciesSortValidator,
38 setDefaultVideoRedundanciesSort,
39 setDefaultPagination,
40 asyncMiddleware(listVideoRedundancies)
41)
42
43serverRedundancyRouter.post('/redundancy/videos',
44 authenticate,
45 ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES),
46 addVideoRedundancyValidator,
47 asyncMiddleware(addVideoRedundancy)
48)
49
50serverRedundancyRouter.delete('/redundancy/videos/:redundancyId',
51 authenticate,
52 ensureUserHasRight(UserRight.MANAGE_VIDEOS_REDUNDANCIES),
53 removeVideoRedundancyValidator,
54 asyncMiddleware(removeVideoRedundancyController)
55)
56
17// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
18 58
19export { 59export {
@@ -22,6 +62,42 @@ export {
22 62
23// --------------------------------------------------------------------------- 63// ---------------------------------------------------------------------------
24 64
65async function listVideoRedundancies (req: express.Request, res: express.Response) {
66 const resultList = await VideoRedundancyModel.listForApi({
67 start: req.query.start,
68 count: req.query.count,
69 sort: req.query.sort,
70 target: req.query.target,
71 strategy: req.query.strategy
72 })
73
74 const result = {
75 total: resultList.total,
76 data: resultList.data.map(r => VideoRedundancyModel.toFormattedJSONStatic(r))
77 }
78
79 return res.json(result)
80}
81
82async function addVideoRedundancy (req: express.Request, res: express.Response) {
83 const payload = {
84 videoId: res.locals.onlyVideo.id
85 }
86
87 await JobQueue.Instance.createJobWithPromise({
88 type: 'video-redundancy',
89 payload
90 })
91
92 return res.sendStatus(204)
93}
94
95async function removeVideoRedundancyController (req: express.Request, res: express.Response) {
96 await removeVideoRedundancy(res.locals.videoRedundancy)
97
98 return res.sendStatus(204)
99}
100
25async function updateRedundancy (req: express.Request, res: express.Response) { 101async function updateRedundancy (req: express.Request, res: express.Response) {
26 const server = res.locals.server 102 const server = res.locals.server
27 103
@@ -30,7 +106,7 @@ async function updateRedundancy (req: express.Request, res: express.Response) {
30 await server.save() 106 await server.save()
31 107
32 // Async, could be long 108 // Async, could be long
33 removeRedundancyOf(server.id) 109 removeRedundanciesOfServer(server.id)
34 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err })) 110 .catch(err => logger.error('Cannot remove redundancy of %s.', server.host, { err }))
35 111
36 return res.sendStatus(204) 112 return res.sendStatus(204)
diff --git a/server/controllers/api/server/stats.ts b/server/controllers/api/server/stats.ts
index 3616c074d..6d508a481 100644
--- a/server/controllers/api/server/stats.ts
+++ b/server/controllers/api/server/stats.ts
@@ -10,6 +10,7 @@ import { ROUTE_CACHE_LIFETIME } from '../../../initializers/constants'
10import { cacheRoute } from '../../../middlewares/cache' 10import { cacheRoute } from '../../../middlewares/cache'
11import { VideoFileModel } from '../../../models/video/video-file' 11import { VideoFileModel } from '../../../models/video/video-file'
12import { CONFIG } from '../../../initializers/config' 12import { CONFIG } from '../../../initializers/config'
13import { VideoRedundancyStrategyWithManual } from '@shared/models'
13 14
14const statsRouter = express.Router() 15const statsRouter = express.Router()
15 16
@@ -25,8 +26,15 @@ async function getStats (req: express.Request, res: express.Response) {
25 const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats() 26 const { totalInstanceFollowers, totalInstanceFollowing } = await ActorFollowModel.getStats()
26 const { totalLocalVideoFilesSize } = await VideoFileModel.getStats() 27 const { totalLocalVideoFilesSize } = await VideoFileModel.getStats()
27 28
29 const strategies: { strategy: VideoRedundancyStrategyWithManual, size: number }[] = CONFIG.REDUNDANCY.VIDEOS.STRATEGIES
30 .map(r => ({
31 strategy: r.strategy,
32 size: r.size
33 }))
34 strategies.push({ strategy: 'manual', size: null })
35
28 const videosRedundancyStats = await Promise.all( 36 const videosRedundancyStats = await Promise.all(
29 CONFIG.REDUNDANCY.VIDEOS.STRATEGIES.map(r => { 37 strategies.map(r => {
30 return VideoRedundancyModel.getStats(r.strategy) 38 return VideoRedundancyModel.getStats(r.strategy)
31 .then(stats => Object.assign(stats, { strategy: r.strategy, totalSize: r.size })) 39 .then(stats => Object.assign(stats, { strategy: r.strategy, totalSize: r.size }))
32 }) 40 })
diff --git a/server/controllers/api/users/index.ts b/server/controllers/api/users/index.ts
index b960e80c1..98eb2beed 100644
--- a/server/controllers/api/users/index.ts
+++ b/server/controllers/api/users/index.ts
@@ -2,7 +2,7 @@ import * as express from 'express'
2import * as RateLimit from 'express-rate-limit' 2import * as RateLimit from 'express-rate-limit'
3import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared' 3import { UserCreate, UserRight, UserRole, UserUpdate } from '../../../../shared'
4import { logger } from '../../../helpers/logger' 4import { logger } from '../../../helpers/logger'
5import { getFormattedObjects } from '../../../helpers/utils' 5import { generateRandomString, getFormattedObjects } from '../../../helpers/utils'
6import { WEBSERVER } from '../../../initializers/constants' 6import { WEBSERVER } from '../../../initializers/constants'
7import { Emailer } from '../../../lib/emailer' 7import { Emailer } from '../../../lib/emailer'
8import { Redis } from '../../../lib/redis' 8import { Redis } from '../../../lib/redis'
@@ -53,8 +53,6 @@ import { Hooks } from '@server/lib/plugins/hooks'
53 53
54const auditLogger = auditLoggerFactory('users') 54const auditLogger = auditLoggerFactory('users')
55 55
56// FIXME: https://github.com/nfriedly/express-rate-limit/issues/138
57// @ts-ignore
58const loginRateLimiter = RateLimit({ 56const loginRateLimiter = RateLimit({
59 windowMs: CONFIG.RATES_LIMIT.LOGIN.WINDOW_MS, 57 windowMs: CONFIG.RATES_LIMIT.LOGIN.WINDOW_MS,
60 max: CONFIG.RATES_LIMIT.LOGIN.MAX 58 max: CONFIG.RATES_LIMIT.LOGIN.MAX
@@ -199,11 +197,25 @@ async function createUser (req: express.Request, res: express.Response) {
199 adminFlags: body.adminFlags || UserAdminFlag.NONE 197 adminFlags: body.adminFlags || UserAdminFlag.NONE
200 }) as MUser 198 }) as MUser
201 199
200 // NB: due to the validator usersAddValidator, password==='' can only be true if we can send the mail.
201 const createPassword = userToCreate.password === ''
202 if (createPassword) {
203 userToCreate.password = await generateRandomString(20)
204 }
205
202 const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate }) 206 const { user, account, videoChannel } = await createUserAccountAndChannelAndPlaylist({ userToCreate: userToCreate })
203 207
204 auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON())) 208 auditLogger.create(getAuditIdFromRes(res), new UserAuditView(user.toFormattedJSON()))
205 logger.info('User %s with its channel and account created.', body.username) 209 logger.info('User %s with its channel and account created.', body.username)
206 210
211 if (createPassword) {
212 // this will send an email for newly created users, so then can set their first password.
213 logger.info('Sending to user %s a create password email', body.username)
214 const verificationString = await Redis.Instance.setCreatePasswordVerificationString(user.id)
215 const url = WEBSERVER.URL + '/reset-password?userId=' + user.id + '&verificationString=' + verificationString
216 await Emailer.Instance.addPasswordCreateEmailJob(userToCreate.username, user.email, url)
217 }
218
207 Hooks.runAction('action:api.user.created', { body, user, account, videoChannel }) 219 Hooks.runAction('action:api.user.created', { body, user, account, videoChannel })
208 220
209 return res.json({ 221 return res.json({
diff --git a/server/controllers/api/users/me.ts b/server/controllers/api/users/me.ts
index ac7c62aab..23890e20c 100644
--- a/server/controllers/api/users/me.ts
+++ b/server/controllers/api/users/me.ts
@@ -39,7 +39,7 @@ meRouter.get('/me',
39) 39)
40meRouter.delete('/me', 40meRouter.delete('/me',
41 authenticate, 41 authenticate,
42 asyncMiddleware(deleteMeValidator), 42 deleteMeValidator,
43 asyncMiddleware(deleteMe) 43 asyncMiddleware(deleteMe)
44) 44)
45 45
@@ -214,7 +214,7 @@ async function updateMe (req: express.Request, res: express.Response) {
214} 214}
215 215
216async function updateMyAvatar (req: express.Request, res: express.Response) { 216async function updateMyAvatar (req: express.Request, res: express.Response) {
217 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] 217 const avatarPhysicalFile = req.files['avatarfile'][0]
218 const user = res.locals.oauth.token.user 218 const user = res.locals.oauth.token.user
219 219
220 const userAccount = await AccountModel.load(user.Account.id) 220 const userAccount = await AccountModel.load(user.Account.id)
diff --git a/server/controllers/api/users/my-subscriptions.ts b/server/controllers/api/users/my-subscriptions.ts
index 43c4c37d8..888392b8b 100644
--- a/server/controllers/api/users/my-subscriptions.ts
+++ b/server/controllers/api/users/my-subscriptions.ts
@@ -19,7 +19,6 @@ import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
19import { VideoFilter } from '../../../../shared/models/videos/video-query.type' 19import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
20import { ActorFollowModel } from '../../../models/activitypub/actor-follow' 20import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
21import { JobQueue } from '../../../lib/job-queue' 21import { JobQueue } from '../../../lib/job-queue'
22import { logger } from '../../../helpers/logger'
23import { sequelizeTypescript } from '../../../initializers/database' 22import { sequelizeTypescript } from '../../../initializers/database'
24 23
25const mySubscriptionsRouter = express.Router() 24const mySubscriptionsRouter = express.Router()
@@ -52,7 +51,7 @@ mySubscriptionsRouter.get('/me/subscriptions',
52mySubscriptionsRouter.post('/me/subscriptions', 51mySubscriptionsRouter.post('/me/subscriptions',
53 authenticate, 52 authenticate,
54 userSubscriptionAddValidator, 53 userSubscriptionAddValidator,
55 asyncMiddleware(addUserSubscription) 54 addUserSubscription
56) 55)
57 56
58mySubscriptionsRouter.get('/me/subscriptions/:uri', 57mySubscriptionsRouter.get('/me/subscriptions/:uri',
@@ -106,7 +105,7 @@ async function areSubscriptionsExist (req: express.Request, res: express.Respons
106 return res.json(existObject) 105 return res.json(existObject)
107} 106}
108 107
109async function addUserSubscription (req: express.Request, res: express.Response) { 108function addUserSubscription (req: express.Request, res: express.Response) {
110 const user = res.locals.oauth.token.User 109 const user = res.locals.oauth.token.User
111 const [ name, host ] = req.body.uri.split('@') 110 const [ name, host ] = req.body.uri.split('@')
112 111
@@ -117,7 +116,6 @@ async function addUserSubscription (req: express.Request, res: express.Response)
117 } 116 }
118 117
119 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload }) 118 JobQueue.Instance.createJob({ type: 'activitypub-follow', payload })
120 .catch(err => logger.error('Cannot create follow job for subscription %s.', req.body.uri, err))
121 119
122 return res.status(204).end() 120 return res.status(204).end()
123} 121}
diff --git a/server/controllers/api/video-channel.ts b/server/controllers/api/video-channel.ts
index e1f37a8fb..a808896ff 100644
--- a/server/controllers/api/video-channel.ts
+++ b/server/controllers/api/video-channel.ts
@@ -119,7 +119,7 @@ async function listVideoChannels (req: express.Request, res: express.Response) {
119} 119}
120 120
121async function updateVideoChannelAvatar (req: express.Request, res: express.Response) { 121async function updateVideoChannelAvatar (req: express.Request, res: express.Response) {
122 const avatarPhysicalFile = req.files[ 'avatarfile' ][ 0 ] 122 const avatarPhysicalFile = req.files['avatarfile'][0]
123 const videoChannel = res.locals.videoChannel 123 const videoChannel = res.locals.videoChannel
124 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON()) 124 const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
125 125
@@ -232,7 +232,6 @@ async function getVideoChannel (req: express.Request, res: express.Response) {
232 232
233 if (videoChannelWithVideos.isOutdated()) { 233 if (videoChannelWithVideos.isOutdated()) {
234 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } }) 234 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } })
235 .catch(err => logger.error('Cannot create AP refresher job for actor %s.', videoChannelWithVideos.Actor.url, { err }))
236 } 235 }
237 236
238 return res.json(videoChannelWithVideos.toFormattedJSON()) 237 return res.json(videoChannelWithVideos.toFormattedJSON())
diff --git a/server/controllers/api/video-playlist.ts b/server/controllers/api/video-playlist.ts
index d9f0ff925..b51490bf9 100644
--- a/server/controllers/api/video-playlist.ts
+++ b/server/controllers/api/video-playlist.ts
@@ -144,7 +144,6 @@ function getVideoPlaylist (req: express.Request, res: express.Response) {
144 144
145 if (videoPlaylist.isOutdated()) { 145 if (videoPlaylist.isOutdated()) {
146 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } }) 146 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video-playlist', url: videoPlaylist.url } })
147 .catch(err => logger.error('Cannot create AP refresher job for playlist %s.', videoPlaylist.url, { err }))
148 } 147 }
149 148
150 return res.json(videoPlaylist.toFormattedJSON()) 149 return res.json(videoPlaylist.toFormattedJSON())
diff --git a/server/controllers/api/videos/captions.ts b/server/controllers/api/videos/captions.ts
index 37481d12f..fd7b165fb 100644
--- a/server/controllers/api/videos/captions.ts
+++ b/server/controllers/api/videos/captions.ts
@@ -66,7 +66,7 @@ async function addVideoCaption (req: express.Request, res: express.Response) {
66 await moveAndProcessCaptionFile(videoCaptionPhysicalFile, videoCaption) 66 await moveAndProcessCaptionFile(videoCaptionPhysicalFile, videoCaption)
67 67
68 await sequelizeTypescript.transaction(async t => { 68 await sequelizeTypescript.transaction(async t => {
69 await VideoCaptionModel.insertOrReplaceLanguage(video.id, req.params.captionLanguage, t) 69 await VideoCaptionModel.insertOrReplaceLanguage(video.id, req.params.captionLanguage, null, t)
70 70
71 // Update video update 71 // Update video update
72 await federateVideoIfNeeded(video, false, t) 72 await federateVideoIfNeeded(video, false, t)
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 28ced5836..ed223cbc9 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -88,12 +88,12 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
88 const buf = await readFile(torrentfile.path) 88 const buf = await readFile(torrentfile.path)
89 const parsedTorrent = parseTorrent(buf) 89 const parsedTorrent = parseTorrent(buf)
90 90
91 videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[ 0 ] : parsedTorrent.name as string 91 videoName = isArray(parsedTorrent.name) ? parsedTorrent.name[0] : parsedTorrent.name as string
92 } else { 92 } else {
93 magnetUri = body.magnetUri 93 magnetUri = body.magnetUri
94 94
95 const parsed = magnetUtil.decode(magnetUri) 95 const parsed = magnetUtil.decode(magnetUri)
96 videoName = isArray(parsed.name) ? parsed.name[ 0 ] : parsed.name as string 96 videoName = isArray(parsed.name) ? parsed.name[0] : parsed.name as string
97 } 97 }
98 98
99 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName }) 99 const video = buildVideo(res.locals.videoChannel.id, body, { name: videoName })
@@ -124,7 +124,7 @@ async function addTorrentImport (req: express.Request, res: express.Response, to
124 videoImportId: videoImport.id, 124 videoImportId: videoImport.id,
125 magnetUri 125 magnetUri
126 } 126 }
127 await JobQueue.Instance.createJob({ type: 'video-import', payload }) 127 await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload })
128 128
129 auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) 129 auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON()))
130 130
@@ -176,7 +176,7 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
176 downloadThumbnail: !thumbnailModel, 176 downloadThumbnail: !thumbnailModel,
177 downloadPreview: !previewModel 177 downloadPreview: !previewModel
178 } 178 }
179 await JobQueue.Instance.createJob({ type: 'video-import', payload }) 179 await JobQueue.Instance.createJobWithPromise({ type: 'video-import', payload })
180 180
181 auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON())) 181 auditLogger.create(getAuditIdFromRes(res), new VideoImportAuditView(videoImport.toFormattedJSON()))
182 182
@@ -211,7 +211,7 @@ function buildVideo (channelId: number, body: VideoImportCreate, importData: You
211async function processThumbnail (req: express.Request, video: VideoModel) { 211async function processThumbnail (req: express.Request, video: VideoModel) {
212 const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined 212 const thumbnailField = req.files ? req.files['thumbnailfile'] : undefined
213 if (thumbnailField) { 213 if (thumbnailField) {
214 const thumbnailPhysicalFile = thumbnailField[ 0 ] 214 const thumbnailPhysicalFile = thumbnailField[0]
215 215
216 return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false) 216 return createVideoMiniatureFromExisting(thumbnailPhysicalFile.path, video, ThumbnailType.MINIATURE, false)
217 } 217 }
@@ -231,12 +231,12 @@ async function processPreview (req: express.Request, video: VideoModel) {
231} 231}
232 232
233function insertIntoDB (parameters: { 233function insertIntoDB (parameters: {
234 video: MVideoThumbnailAccountDefault, 234 video: MVideoThumbnailAccountDefault
235 thumbnailModel: MThumbnail, 235 thumbnailModel: MThumbnail
236 previewModel: MThumbnail, 236 previewModel: MThumbnail
237 videoChannel: MChannelAccountDefault, 237 videoChannel: MChannelAccountDefault
238 tags: string[], 238 tags: string[]
239 videoImportAttributes: Partial<MVideoImport>, 239 videoImportAttributes: Partial<MVideoImport>
240 user: MUser 240 user: MUser
241}): Bluebird<MVideoImportFormattable> { 241}): Bluebird<MVideoImportFormattable> {
242 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters 242 const { video, thumbnailModel, previewModel, videoChannel, tags, videoImportAttributes, user } = parameters
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 8d4ff07eb..eb46ea01f 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -135,7 +135,7 @@ videosRouter.get('/:id',
135 asyncMiddleware(getVideo) 135 asyncMiddleware(getVideo)
136) 136)
137videosRouter.post('/:id/views', 137videosRouter.post('/:id/views',
138 asyncMiddleware(videosGetValidator), 138 asyncMiddleware(videosCustomGetValidator('only-immutable-attributes')),
139 asyncMiddleware(viewVideo) 139 asyncMiddleware(viewVideo)
140) 140)
141 141
@@ -307,7 +307,7 @@ async function addVideo (req: express.Request, res: express.Response) {
307 } 307 }
308 } 308 }
309 309
310 await JobQueue.Instance.createJob({ type: 'video-transcoding', payload: dataInput }) 310 await JobQueue.Instance.createJobWithPromise({ type: 'video-transcoding', payload: dataInput })
311 } 311 }
312 312
313 Hooks.runAction('action:api.video.uploaded', { video: videoCreated }) 313 Hooks.runAction('action:api.video.uploaded', { video: videoCreated })
@@ -452,14 +452,13 @@ async function getVideo (req: express.Request, res: express.Response) {
452 452
453 if (video.isOutdated()) { 453 if (video.isOutdated()) {
454 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } }) 454 JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'video', url: video.url } })
455 .catch(err => logger.error('Cannot create AP refresher job for video %s.', video.url, { err }))
456 } 455 }
457 456
458 return res.json(video.toFormattedDetailsJSON()) 457 return res.json(video.toFormattedDetailsJSON())
459} 458}
460 459
461async function viewVideo (req: express.Request, res: express.Response) { 460async function viewVideo (req: express.Request, res: express.Response) {
462 const videoInstance = res.locals.videoAll 461 const videoInstance = res.locals.onlyImmutableVideo
463 462
464 const ip = req.ip 463 const ip = req.ip
465 const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid) 464 const exists = await Redis.Instance.doesVideoIPViewExist(ip, videoInstance.uuid)