aboutsummaryrefslogtreecommitdiffhomepage
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/controllers/api/videos/import.ts12
-rw-r--r--server/controllers/client.ts10
-rw-r--r--server/controllers/static.ts1
-rw-r--r--server/helpers/regexp.ts1
-rw-r--r--server/initializers/migrations/0135-video-channel-actor.ts1
-rw-r--r--server/lib/video-state.ts6
-rw-r--r--server/middlewares/index.ts1
-rw-r--r--server/middlewares/robots.ts13
-rw-r--r--server/middlewares/validators/oembed.ts16
-rw-r--r--server/models/video/video-channel.ts2
-rw-r--r--server/models/video/video.ts4
-rw-r--r--server/tests/api/live/live.ts4
-rw-r--r--server/tests/api/search/search-channels.ts7
-rw-r--r--server/tests/api/server/services.ts66
-rw-r--r--server/tests/cli/create-transcoding-job.ts16
-rw-r--r--server/tests/client.ts10
16 files changed, 123 insertions, 47 deletions
diff --git a/server/controllers/api/videos/import.ts b/server/controllers/api/videos/import.ts
index 5f90e4308..4265f3217 100644
--- a/server/controllers/api/videos/import.ts
+++ b/server/controllers/api/videos/import.ts
@@ -158,7 +158,11 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
158 158
159 // Process video thumbnail from url if processing from request.files failed 159 // Process video thumbnail from url if processing from request.files failed
160 if (!thumbnailModel && youtubeDLInfo.thumbnailUrl) { 160 if (!thumbnailModel && youtubeDLInfo.thumbnailUrl) {
161 thumbnailModel = await processThumbnailFromUrl(youtubeDLInfo.thumbnailUrl, video) 161 try {
162 thumbnailModel = await processThumbnailFromUrl(youtubeDLInfo.thumbnailUrl, video)
163 } catch (err) {
164 logger.warn('Cannot process thumbnail %s from youtubedl.', youtubeDLInfo.thumbnailUrl, { err })
165 }
162 } 166 }
163 167
164 // Process video preview from request.files 168 // Process video preview from request.files
@@ -166,7 +170,11 @@ async function addYoutubeDLImport (req: express.Request, res: express.Response)
166 170
167 // Process video preview from url if processing from request.files failed 171 // Process video preview from url if processing from request.files failed
168 if (!previewModel && youtubeDLInfo.thumbnailUrl) { 172 if (!previewModel && youtubeDLInfo.thumbnailUrl) {
169 previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video) 173 try {
174 previewModel = await processPreviewFromUrl(youtubeDLInfo.thumbnailUrl, video)
175 } catch (err) {
176 logger.warn('Cannot process preview %s from youtubedl.', youtubeDLInfo.thumbnailUrl, { err })
177 }
170 } 178 }
171 179
172 const videoImport = await insertIntoDB({ 180 const videoImport = await insertIntoDB({
diff --git a/server/controllers/client.ts b/server/controllers/client.ts
index d81e35ec3..cdc556da2 100644
--- a/server/controllers/client.ts
+++ b/server/controllers/client.ts
@@ -5,12 +5,12 @@ import { join } from 'path'
5import { logger } from '@server/helpers/logger' 5import { logger } from '@server/helpers/logger'
6import { CONFIG } from '@server/initializers/config' 6import { CONFIG } from '@server/initializers/config'
7import { Hooks } from '@server/lib/plugins/hooks' 7import { Hooks } from '@server/lib/plugins/hooks'
8import { HttpStatusCode } from '@shared/models'
9import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n' 8import { buildFileLocale, getCompleteLocale, is18nLocale, LOCALE_FILES } from '@shared/core-utils/i18n'
9import { HttpStatusCode } from '@shared/models'
10import { root } from '../helpers/core-utils' 10import { root } from '../helpers/core-utils'
11import { STATIC_MAX_AGE } from '../initializers/constants' 11import { STATIC_MAX_AGE } from '../initializers/constants'
12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html' 12import { ClientHtml, sendHTML, serveIndexHTML } from '../lib/client-html'
13import { asyncMiddleware, embedCSP } from '../middlewares' 13import { asyncMiddleware, disableRobots, embedCSP } from '../middlewares'
14 14
15const clientsRouter = express.Router() 15const clientsRouter = express.Router()
16 16
@@ -81,6 +81,12 @@ clientsRouter.use('/client/*', (req: express.Request, res: express.Response) =>
81 res.status(HttpStatusCode.NOT_FOUND_404).end() 81 res.status(HttpStatusCode.NOT_FOUND_404).end()
82}) 82})
83 83
84// No index exceptions
85clientsRouter.all('/about/peertube',
86 disableRobots,
87 asyncMiddleware(serveIndexHTML)
88)
89
84// Always serve index client page (the client is a single page application, let it handle routing) 90// Always serve index client page (the client is a single page application, let it handle routing)
85// Try to provide the right language index.html 91// Try to provide the right language index.html
86clientsRouter.use('/(:language)?', asyncMiddleware(serveIndexHTML)) 92clientsRouter.use('/(:language)?', asyncMiddleware(serveIndexHTML))
diff --git a/server/controllers/static.ts b/server/controllers/static.ts
index 1c04e4b11..fe1629910 100644
--- a/server/controllers/static.ts
+++ b/server/controllers/static.ts
@@ -69,6 +69,7 @@ staticRouter.get('/robots.txt',
69 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS), 69 cacheRoute(ROUTE_CACHE_LIFETIME.ROBOTS),
70 (_, res: express.Response) => { 70 (_, res: express.Response) => {
71 res.type('text/plain') 71 res.type('text/plain')
72
72 return res.send(CONFIG.INSTANCE.ROBOTS) 73 return res.send(CONFIG.INSTANCE.ROBOTS)
73 } 74 }
74) 75)
diff --git a/server/helpers/regexp.ts b/server/helpers/regexp.ts
index cfc2be488..257054cea 100644
--- a/server/helpers/regexp.ts
+++ b/server/helpers/regexp.ts
@@ -4,7 +4,6 @@ function regexpCapture (str: string, regex: RegExp, maxIterations = 100) {
4 let m: RegExpExecArray 4 let m: RegExpExecArray
5 let i = 0 5 let i = 0
6 6
7 // tslint:disable:no-conditional-assignment
8 while ((m = regex.exec(str)) !== null && i < maxIterations) { 7 while ((m = regex.exec(str)) !== null && i < maxIterations) {
9 // This is necessary to avoid infinite loops with zero-width matches 8 // This is necessary to avoid infinite loops with zero-width matches
10 if (m.index === regex.lastIndex) { 9 if (m.index === regex.lastIndex) {
diff --git a/server/initializers/migrations/0135-video-channel-actor.ts b/server/initializers/migrations/0135-video-channel-actor.ts
index 3f620dfa3..6989e1cbb 100644
--- a/server/initializers/migrations/0135-video-channel-actor.ts
+++ b/server/initializers/migrations/0135-video-channel-actor.ts
@@ -56,7 +56,6 @@ async function up (utils: {
56 } 56 }
57 57
58 { 58 {
59 // tslint:disable:no-trailing-whitespace
60 const query1 = 59 const query1 =
61 ` 60 `
62 INSERT INTO "actor" 61 INSERT INTO "actor"
diff --git a/server/lib/video-state.ts b/server/lib/video-state.ts
index 0613d94bf..9352a67d1 100644
--- a/server/lib/video-state.ts
+++ b/server/lib/video-state.ts
@@ -70,13 +70,13 @@ async function moveToPublishedState (video: MVideoFullLight, isNewVideo: boolean
70 logger.info('Publishing video %s.', video.uuid, { tags: [ video.uuid ] }) 70 logger.info('Publishing video %s.', video.uuid, { tags: [ video.uuid ] })
71 71
72 const previousState = video.state 72 const previousState = video.state
73 await video.setNewState(VideoState.PUBLISHED, transaction) 73 await video.setNewState(VideoState.PUBLISHED, isNewVideo, transaction)
74 74
75 // If the video was not published, we consider it is a new one for other instances 75 // If the video was not published, we consider it is a new one for other instances
76 // Live videos are always federated, so it's not a new video 76 // Live videos are always federated, so it's not a new video
77 await federateVideoIfNeeded(video, isNewVideo, transaction) 77 await federateVideoIfNeeded(video, isNewVideo, transaction)
78 78
79 Notifier.Instance.notifyOnNewVideoIfNeeded(video) 79 if (isNewVideo) Notifier.Instance.notifyOnNewVideoIfNeeded(video)
80 80
81 if (previousState === VideoState.TO_TRANSCODE) { 81 if (previousState === VideoState.TO_TRANSCODE) {
82 Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(video) 82 Notifier.Instance.notifyOnVideoPublishedAfterTranscoding(video)
@@ -90,7 +90,7 @@ async function moveToExternalStorageState (video: MVideoFullLight, isNewVideo: b
90 // We want to wait all transcoding jobs before moving the video on an external storage 90 // We want to wait all transcoding jobs before moving the video on an external storage
91 if (pendingTranscode !== 0) return 91 if (pendingTranscode !== 0) return
92 92
93 await video.setNewState(VideoState.TO_MOVE_TO_EXTERNAL_STORAGE, transaction) 93 await video.setNewState(VideoState.TO_MOVE_TO_EXTERNAL_STORAGE, isNewVideo, transaction)
94 94
95 logger.info('Creating external storage move job for video %s.', video.uuid, { tags: [ video.uuid ] }) 95 logger.info('Creating external storage move job for video %s.', video.uuid, { tags: [ video.uuid ] })
96 96
diff --git a/server/middlewares/index.ts b/server/middlewares/index.ts
index a0035f623..d2ed079b6 100644
--- a/server/middlewares/index.ts
+++ b/server/middlewares/index.ts
@@ -4,6 +4,7 @@ export * from './activitypub'
4export * from './async' 4export * from './async'
5export * from './auth' 5export * from './auth'
6export * from './pagination' 6export * from './pagination'
7export * from './robots'
7export * from './servers' 8export * from './servers'
8export * from './sort' 9export * from './sort'
9export * from './user-right' 10export * from './user-right'
diff --git a/server/middlewares/robots.ts b/server/middlewares/robots.ts
new file mode 100644
index 000000000..b22b24a9f
--- /dev/null
+++ b/server/middlewares/robots.ts
@@ -0,0 +1,13 @@
1import express from 'express'
2
3function disableRobots (req: express.Request, res: express.Response, next: express.NextFunction) {
4 res.setHeader('X-Robots-Tag', 'noindex')
5
6 return next()
7}
8
9// ---------------------------------------------------------------------------
10
11export {
12 disableRobots
13}
diff --git a/server/middlewares/validators/oembed.ts b/server/middlewares/validators/oembed.ts
index 5e47211b5..96c8adc99 100644
--- a/server/middlewares/validators/oembed.ts
+++ b/server/middlewares/validators/oembed.ts
@@ -62,12 +62,26 @@ const oembedValidator = [
62 62
63 const url = req.query.url as string 63 const url = req.query.url as string
64 64
65 let urlPath: string
66
67 try {
68 urlPath = new URL(url).pathname
69 } catch (err) {
70 return res.fail({
71 status: HttpStatusCode.BAD_REQUEST_400,
72 message: err.message,
73 data: {
74 url
75 }
76 })
77 }
78
65 const isPlaylist = startPlaylistURLs.some(u => url.startsWith(u)) 79 const isPlaylist = startPlaylistURLs.some(u => url.startsWith(u))
66 const isVideo = isPlaylist ? false : startVideoURLs.some(u => url.startsWith(u)) 80 const isVideo = isPlaylist ? false : startVideoURLs.some(u => url.startsWith(u))
67 81
68 const startIsOk = isVideo || isPlaylist 82 const startIsOk = isVideo || isPlaylist
69 83
70 const matches = watchRegex.exec(url) 84 const matches = watchRegex.exec(urlPath)
71 85
72 if (startIsOk === false || matches === null) { 86 if (startIsOk === false || matches === null) {
73 return res.fail({ 87 return res.fail({
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index c04bd4355..8ccb818b3 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -121,7 +121,7 @@ export type SummaryOptions = {
121 for (const handle of options.handles || []) { 121 for (const handle of options.handles || []) {
122 const [ preferredUsername, host ] = handle.split('@') 122 const [ preferredUsername, host ] = handle.split('@')
123 123
124 if (!host) { 124 if (!host || host === WEBSERVER.HOST) {
125 or.push({ 125 or.push({
126 '$Actor.preferredUsername$': preferredUsername, 126 '$Actor.preferredUsername$': preferredUsername,
127 '$Actor.serverId$': null 127 '$Actor.serverId$': null
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index e0c4dd2db..d2daf18ee 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -1766,12 +1766,12 @@ export class VideoModel extends Model<Partial<AttributesOnly<VideoModel>>> {
1766 this.privacy === VideoPrivacy.INTERNAL 1766 this.privacy === VideoPrivacy.INTERNAL
1767 } 1767 }
1768 1768
1769 async setNewState (newState: VideoState, transaction: Transaction) { 1769 async setNewState (newState: VideoState, isNewVideo: boolean, transaction: Transaction) {
1770 if (this.state === newState) throw new Error('Cannot use same state ' + newState) 1770 if (this.state === newState) throw new Error('Cannot use same state ' + newState)
1771 1771
1772 this.state = newState 1772 this.state = newState
1773 1773
1774 if (this.state === VideoState.PUBLISHED) { 1774 if (this.state === VideoState.PUBLISHED && isNewVideo) {
1775 this.publishedAt = new Date() 1775 this.publishedAt = new Date()
1776 } 1776 }
1777 1777
diff --git a/server/tests/api/live/live.ts b/server/tests/api/live/live.ts
index ba952aff5..5cac3bc4e 100644
--- a/server/tests/api/live/live.ts
+++ b/server/tests/api/live/live.ts
@@ -439,7 +439,7 @@ describe('Test live', function () {
439 }) 439 })
440 440
441 it('Should enable transcoding without additional resolutions', async function () { 441 it('Should enable transcoding without additional resolutions', async function () {
442 this.timeout(60000) 442 this.timeout(120000)
443 443
444 liveVideoId = await createLiveWrapper(false) 444 liveVideoId = await createLiveWrapper(false)
445 445
@@ -453,7 +453,7 @@ describe('Test live', function () {
453 }) 453 })
454 454
455 it('Should enable transcoding with some resolutions', async function () { 455 it('Should enable transcoding with some resolutions', async function () {
456 this.timeout(60000) 456 this.timeout(120000)
457 457
458 const resolutions = [ 240, 480 ] 458 const resolutions = [ 240, 480 ]
459 await updateConf(resolutions) 459 await updateConf(resolutions)
diff --git a/server/tests/api/search/search-channels.ts b/server/tests/api/search/search-channels.ts
index 8a01aff90..67612537c 100644
--- a/server/tests/api/search/search-channels.ts
+++ b/server/tests/api/search/search-channels.ts
@@ -129,6 +129,13 @@ describe('Test channels search', function () {
129 } 129 }
130 130
131 { 131 {
132 const body = await command.advancedChannelSearch({ search: { handles: [ 'squall_channel@' + server.host ] } })
133 expect(body.total).to.equal(1)
134 expect(body.data).to.have.lengthOf(1)
135 expect(body.data[0].displayName).to.equal('Squall channel')
136 }
137
138 {
132 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } }) 139 const body = await command.advancedChannelSearch({ search: { handles: [ 'chocobozzz_channel' ] } })
133 expect(body.total).to.equal(0) 140 expect(body.total).to.equal(0)
134 expect(body.data).to.have.lengthOf(0) 141 expect(body.data).to.have.lengthOf(0)
diff --git a/server/tests/api/server/services.ts b/server/tests/api/server/services.ts
index 69d030dbb..3a87df981 100644
--- a/server/tests/api/server/services.ts
+++ b/server/tests/api/server/services.ts
@@ -52,42 +52,46 @@ describe('Test services', function () {
52 52
53 it('Should have a valid oEmbed video response', async function () { 53 it('Should have a valid oEmbed video response', async function () {
54 for (const basePath of [ '/videos/watch/', '/w/' ]) { 54 for (const basePath of [ '/videos/watch/', '/w/' ]) {
55 const oembedUrl = 'http://localhost:' + server.port + basePath + video.uuid 55 for (const suffix of [ '', '?param=1' ]) {
56 56 const oembedUrl = server.url + basePath + video.uuid + suffix
57 const res = await server.services.getOEmbed({ oembedUrl }) 57
58 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 58 const res = await server.services.getOEmbed({ oembedUrl })
59 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` + 59 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
60 'frameborder="0" allowfullscreen></iframe>' 60 `title="${video.name}" src="http://localhost:${server.port}/videos/embed/${video.uuid}" ` +
61 const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath 61 'frameborder="0" allowfullscreen></iframe>'
62 62 const expectedThumbnailUrl = 'http://localhost:' + server.port + video.previewPath
63 expect(res.body.html).to.equal(expectedHtml) 63
64 expect(res.body.title).to.equal(video.name) 64 expect(res.body.html).to.equal(expectedHtml)
65 expect(res.body.author_name).to.equal(server.store.channel.displayName) 65 expect(res.body.title).to.equal(video.name)
66 expect(res.body.width).to.equal(560) 66 expect(res.body.author_name).to.equal(server.store.channel.displayName)
67 expect(res.body.height).to.equal(315) 67 expect(res.body.width).to.equal(560)
68 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl) 68 expect(res.body.height).to.equal(315)
69 expect(res.body.thumbnail_width).to.equal(850) 69 expect(res.body.thumbnail_url).to.equal(expectedThumbnailUrl)
70 expect(res.body.thumbnail_height).to.equal(480) 70 expect(res.body.thumbnail_width).to.equal(850)
71 expect(res.body.thumbnail_height).to.equal(480)
72 }
71 } 73 }
72 }) 74 })
73 75
74 it('Should have a valid playlist oEmbed response', async function () { 76 it('Should have a valid playlist oEmbed response', async function () {
75 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) { 77 for (const basePath of [ '/videos/watch/playlist/', '/w/p/' ]) {
76 const oembedUrl = 'http://localhost:' + server.port + basePath + playlistUUID 78 for (const suffix of [ '', '?param=1' ]) {
77 79 const oembedUrl = server.url + basePath + playlistUUID + suffix
78 const res = await server.services.getOEmbed({ oembedUrl }) 80
79 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' + 81 const res = await server.services.getOEmbed({ oembedUrl })
80 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` + 82 const expectedHtml = '<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts" ' +
81 'frameborder="0" allowfullscreen></iframe>' 83 `title="${playlistDisplayName}" src="http://localhost:${server.port}/video-playlists/embed/${playlistUUID}" ` +
82 84 'frameborder="0" allowfullscreen></iframe>'
83 expect(res.body.html).to.equal(expectedHtml) 85
84 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck') 86 expect(res.body.html).to.equal(expectedHtml)
85 expect(res.body.author_name).to.equal(server.store.channel.displayName) 87 expect(res.body.title).to.equal('The Life and Times of Scrooge McDuck')
86 expect(res.body.width).to.equal(560) 88 expect(res.body.author_name).to.equal(server.store.channel.displayName)
87 expect(res.body.height).to.equal(315) 89 expect(res.body.width).to.equal(560)
88 expect(res.body.thumbnail_url).exist 90 expect(res.body.height).to.equal(315)
89 expect(res.body.thumbnail_width).to.equal(280) 91 expect(res.body.thumbnail_url).exist
90 expect(res.body.thumbnail_height).to.equal(157) 92 expect(res.body.thumbnail_width).to.equal(280)
93 expect(res.body.thumbnail_height).to.equal(157)
94 }
91 } 95 }
92 }) 96 })
93 97
diff --git a/server/tests/cli/create-transcoding-job.ts b/server/tests/cli/create-transcoding-job.ts
index 3fd624091..2b388ab0c 100644
--- a/server/tests/cli/create-transcoding-job.ts
+++ b/server/tests/cli/create-transcoding-job.ts
@@ -33,9 +33,10 @@ async function checkFilesInObjectStorage (files: VideoFile[], type: 'webtorrent'
33function runTests (objectStorage: boolean) { 33function runTests (objectStorage: boolean) {
34 let servers: PeerTubeServer[] = [] 34 let servers: PeerTubeServer[] = []
35 const videosUUID: string[] = [] 35 const videosUUID: string[] = []
36 const publishedAt: string[] = []
36 37
37 before(async function () { 38 before(async function () {
38 this.timeout(60000) 39 this.timeout(120000)
39 40
40 const config = objectStorage 41 const config = objectStorage
41 ? ObjectStorageCommand.getDefaultConfig() 42 ? ObjectStorageCommand.getDefaultConfig()
@@ -54,6 +55,11 @@ function runTests (objectStorage: boolean) {
54 for (let i = 1; i <= 5; i++) { 55 for (let i = 1; i <= 5; i++) {
55 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'video' + i } }) 56 const { uuid, shortUUID } = await servers[0].videos.upload({ attributes: { name: 'video' + i } })
56 57
58 await waitJobs(servers)
59
60 const video = await servers[0].videos.get({ id: uuid })
61 publishedAt.push(video.publishedAt as string)
62
57 if (i > 2) { 63 if (i > 2) {
58 videosUUID.push(uuid) 64 videosUUID.push(uuid)
59 } else { 65 } else {
@@ -225,6 +231,14 @@ function runTests (objectStorage: boolean) {
225 } 231 }
226 }) 232 })
227 233
234 it('Should not have updated published at attributes', async function () {
235 for (const id of videosUUID) {
236 const video = await servers[0].videos.get({ id })
237
238 expect(publishedAt.some(p => video.publishedAt === p)).to.be.true
239 }
240 })
241
228 after(async function () { 242 after(async function () {
229 await cleanupTests(servers) 243 await cleanupTests(servers)
230 }) 244 })
diff --git a/server/tests/client.ts b/server/tests/client.ts
index a91bec906..6c32c81db 100644
--- a/server/tests/client.ts
+++ b/server/tests/client.ts
@@ -482,6 +482,16 @@ describe('Test a client controllers', function () {
482 } 482 }
483 } 483 }
484 }) 484 })
485
486 it('Should add noindex header for some paths', async function () {
487 const paths = [ '/about/peertube' ]
488
489 for (const path of paths) {
490 const { headers } = await makeHTMLRequest(servers[0].url, path)
491
492 expect(headers['x-robots-tag']).to.equal('noindex')
493 }
494 })
485 }) 495 })
486 496
487 describe('Embed HTML', function () { 497 describe('Embed HTML', function () {