aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--package.json2
-rwxr-xr-xscripts/release.sh13
-rw-r--r--server/controllers/api/accounts.ts4
-rw-r--r--server/controllers/api/jobs.ts7
-rw-r--r--server/controllers/api/server/follows.ts6
-rw-r--r--server/controllers/api/users.ts22
-rw-r--r--server/controllers/api/videos/abuse.ts4
-rw-r--r--server/controllers/api/videos/blacklist.ts4
-rw-r--r--server/controllers/api/videos/channel.ts4
-rw-r--r--server/controllers/api/videos/comment.ts4
-rw-r--r--server/controllers/api/videos/index.ts6
-rw-r--r--server/initializers/constants.ts2
-rw-r--r--server/lib/activitypub/actor.ts5
-rw-r--r--server/lib/activitypub/process/process-delete.ts9
-rw-r--r--server/lib/activitypub/send/send-delete.ts5
-rw-r--r--server/lib/activitypub/videos.ts3
-rw-r--r--server/lib/user.ts12
-rw-r--r--server/middlewares/pagination.ts4
-rw-r--r--server/models/account/account.ts26
-rw-r--r--server/models/account/user.ts3
-rw-r--r--server/models/activitypub/actor.ts9
-rw-r--r--server/models/server/server.ts13
-rw-r--r--server/models/video/video-channel.ts29
-rw-r--r--server/models/video/video-comment.ts15
-rw-r--r--server/models/video/video.ts194
-rw-r--r--server/tests/api/check-params/users.ts2
-rw-r--r--server/tests/api/server/follows.ts24
-rw-r--r--server/tests/api/users/users-multiple-servers.ts66
-rw-r--r--server/tests/api/users/users.ts4
-rw-r--r--server/tests/api/videos/multiple-servers.ts14
-rw-r--r--server/tests/api/videos/single-server.ts17
-rw-r--r--server/tests/api/videos/video-comments.ts4
-rw-r--r--server/tests/utils/miscs/miscs.ts9
-rw-r--r--server/tests/utils/users/accounts.ts25
-rw-r--r--server/tests/utils/users/users.ts2
-rw-r--r--server/tests/utils/videos/videos.ts31
-rw-r--r--yarn.lock6
38 files changed, 401 insertions, 210 deletions
diff --git a/.gitignore b/.gitignore
index b373b368b..3dcb42ffe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,7 @@
7/test6/ 7/test6/
8/storage/ 8/storage/
9/config/production.yaml 9/config/production.yaml
10/config/local.json 10/config/local*.json
11/ffmpeg/ 11/ffmpeg/
12/*.sublime-project 12/*.sublime-project
13/*.sublime-workspace 13/*.sublime-workspace
diff --git a/package.json b/package.json
index 4aec05798..5c0e25d91 100644
--- a/package.json
+++ b/package.json
@@ -81,7 +81,7 @@
81 "rimraf": "^2.5.4", 81 "rimraf": "^2.5.4",
82 "safe-buffer": "^5.0.1", 82 "safe-buffer": "^5.0.1",
83 "scripty": "^1.5.0", 83 "scripty": "^1.5.0",
84 "sequelize": "4.25.2", 84 "sequelize": "4.31.2",
85 "sequelize-typescript": "^0.6.1", 85 "sequelize-typescript": "^0.6.1",
86 "sharp": "^0.18.4", 86 "sharp": "^0.18.4",
87 "ts-node": "^3.3.0", 87 "ts-node": "^3.3.0",
diff --git a/scripts/release.sh b/scripts/release.sh
index ec76bb846..667df5a5f 100755
--- a/scripts/release.sh
+++ b/scripts/release.sh
@@ -21,6 +21,12 @@ if [ -z $GITHUB_TOKEN ]; then
21 exit -1 21 exit -1
22fi 22fi
23 23
24branch=$(git symbolic-ref --short -q HEAD)
25if [ "$branch" != "develop" ]; then
26 echo "Need to be on develop branch."
27 exit -1
28fi
29
24version="v$1" 30version="v$1"
25directory_name="peertube-$version" 31directory_name="peertube-$version"
26zip_name="peertube-$version.zip" 32zip_name="peertube-$version.zip"
@@ -56,3 +62,10 @@ git push origin --tag
56 62
57github-release release --user chocobozzz --repo peertube --tag "$version" --name "$version" 63github-release release --user chocobozzz --repo peertube --tag "$version" --name "$version"
58github-release upload --user chocobozzz --repo peertube --tag "$version" --name "$zip_name" --file "$zip_name" 64github-release upload --user chocobozzz --repo peertube --tag "$version" --name "$zip_name" --file "$zip_name"
65
66# Update master branch
67git checkout master
68git rebase develop
69git git push origin master
70git checkout develop
71
diff --git a/server/controllers/api/accounts.ts b/server/controllers/api/accounts.ts
index 3bc929db8..4dc0cc16d 100644
--- a/server/controllers/api/accounts.ts
+++ b/server/controllers/api/accounts.ts
@@ -1,6 +1,6 @@
1import * as express from 'express' 1import * as express from 'express'
2import { getFormattedObjects } from '../../helpers/utils' 2import { getFormattedObjects } from '../../helpers/utils'
3import { asyncMiddleware, paginationValidator, setDefaultSort, setPagination } from '../../middlewares' 3import { asyncMiddleware, paginationValidator, setDefaultSort, setDefaultPagination } from '../../middlewares'
4import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators' 4import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators'
5import { AccountModel } from '../../models/account/account' 5import { AccountModel } from '../../models/account/account'
6 6
@@ -10,7 +10,7 @@ accountsRouter.get('/',
10 paginationValidator, 10 paginationValidator,
11 accountsSortValidator, 11 accountsSortValidator,
12 setDefaultSort, 12 setDefaultSort,
13 setPagination, 13 setDefaultPagination,
14 asyncMiddleware(listAccounts) 14 asyncMiddleware(listAccounts)
15) 15)
16 16
diff --git a/server/controllers/api/jobs.ts b/server/controllers/api/jobs.ts
index 180a3fca6..de37dea39 100644
--- a/server/controllers/api/jobs.ts
+++ b/server/controllers/api/jobs.ts
@@ -1,7 +1,10 @@
1import * as express from 'express' 1import * as express from 'express'
2import { UserRight } from '../../../shared/models/users' 2import { UserRight } from '../../../shared/models/users'
3import { getFormattedObjects } from '../../helpers/utils' 3import { getFormattedObjects } from '../../helpers/utils'
4import { asyncMiddleware, authenticate, ensureUserHasRight, jobsSortValidator, setDefaultSort, setPagination } from '../../middlewares' 4import {
5 asyncMiddleware, authenticate, ensureUserHasRight, jobsSortValidator, setDefaultPagination,
6 setDefaultSort
7} from '../../middlewares'
5import { paginationValidator } from '../../middlewares/validators' 8import { paginationValidator } from '../../middlewares/validators'
6import { JobModel } from '../../models/job/job' 9import { JobModel } from '../../models/job/job'
7 10
@@ -13,7 +16,7 @@ jobsRouter.get('/',
13 paginationValidator, 16 paginationValidator,
14 jobsSortValidator, 17 jobsSortValidator,
15 setDefaultSort, 18 setDefaultSort,
16 setPagination, 19 setDefaultPagination,
17 asyncMiddleware(listJobs) 20 asyncMiddleware(listJobs)
18) 21)
19 22
diff --git a/server/controllers/api/server/follows.ts b/server/controllers/api/server/follows.ts
index 0fbcc4b80..40b62d977 100644
--- a/server/controllers/api/server/follows.ts
+++ b/server/controllers/api/server/follows.ts
@@ -10,7 +10,7 @@ import { getOrCreateActorAndServerAndModel } from '../../../lib/activitypub/acto
10import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send' 10import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send'
11import { 11import {
12 asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, removeFollowingValidator, setBodyHostsPort, setDefaultSort, 12 asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, removeFollowingValidator, setBodyHostsPort, setDefaultSort,
13 setPagination 13 setDefaultPagination
14} from '../../../middlewares' 14} from '../../../middlewares'
15import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' 15import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators'
16import { ActorModel } from '../../../models/activitypub/actor' 16import { ActorModel } from '../../../models/activitypub/actor'
@@ -22,7 +22,7 @@ serverFollowsRouter.get('/following',
22 paginationValidator, 22 paginationValidator,
23 followingSortValidator, 23 followingSortValidator,
24 setDefaultSort, 24 setDefaultSort,
25 setPagination, 25 setDefaultPagination,
26 asyncMiddleware(listFollowing) 26 asyncMiddleware(listFollowing)
27) 27)
28 28
@@ -45,7 +45,7 @@ serverFollowsRouter.get('/followers',
45 paginationValidator, 45 paginationValidator,
46 followersSortValidator, 46 followersSortValidator,
47 setDefaultSort, 47 setDefaultSort,
48 setPagination, 48 setDefaultPagination,
49 asyncMiddleware(listFollowers) 49 asyncMiddleware(listFollowers)
50) 50)
51 51
diff --git a/server/controllers/api/users.ts b/server/controllers/api/users.ts
index 0ca9b337a..aced4639e 100644
--- a/server/controllers/api/users.ts
+++ b/server/controllers/api/users.ts
@@ -13,7 +13,7 @@ import { sendUpdateUser } from '../../lib/activitypub/send'
13import { createUserAccountAndChannel } from '../../lib/user' 13import { createUserAccountAndChannel } from '../../lib/user'
14import { 14import {
15 asyncMiddleware, authenticate, ensureUserHasRight, ensureUserRegistrationAllowed, paginationValidator, setDefaultSort, 15 asyncMiddleware, authenticate, ensureUserHasRight, ensureUserRegistrationAllowed, paginationValidator, setDefaultSort,
16 setPagination, token, usersAddValidator, usersGetValidator, usersRegisterValidator, usersRemoveValidator, usersSortValidator, 16 setDefaultPagination, token, usersAddValidator, usersGetValidator, usersRegisterValidator, usersRemoveValidator, usersSortValidator,
17 usersUpdateMeValidator, usersUpdateValidator, usersVideoRatingValidator 17 usersUpdateMeValidator, usersUpdateValidator, usersVideoRatingValidator
18} from '../../middlewares' 18} from '../../middlewares'
19import { usersUpdateMyAvatarValidator, videosSortValidator } from '../../middlewares/validators' 19import { usersUpdateMyAvatarValidator, videosSortValidator } from '../../middlewares/validators'
@@ -40,7 +40,7 @@ usersRouter.get('/me/videos',
40 paginationValidator, 40 paginationValidator,
41 videosSortValidator, 41 videosSortValidator,
42 setDefaultSort, 42 setDefaultSort,
43 setPagination, 43 setDefaultPagination,
44 asyncMiddleware(getUserVideos) 44 asyncMiddleware(getUserVideos)
45) 45)
46 46
@@ -56,7 +56,7 @@ usersRouter.get('/',
56 paginationValidator, 56 paginationValidator,
57 usersSortValidator, 57 usersSortValidator,
58 setDefaultSort, 58 setDefaultSort,
59 setPagination, 59 setDefaultPagination,
60 asyncMiddleware(listUsers) 60 asyncMiddleware(listUsers)
61) 61)
62 62
@@ -129,15 +129,19 @@ async function createUserRetryWrapper (req: express.Request, res: express.Respon
129 errorMessage: 'Cannot insert the user with many retries.' 129 errorMessage: 'Cannot insert the user with many retries.'
130 } 130 }
131 131
132 await retryTransactionWrapper(createUser, options) 132 const { user, account } = await retryTransactionWrapper(createUser, options)
133 133
134 // TODO : include Location of the new user -> 201 134 return res.json({
135 return res.type('json').status(204).end() 135 user: {
136 id: user.id,
137 uuid: account.uuid
138 }
139 }).end()
136} 140}
137 141
138async function createUser (req: express.Request) { 142async function createUser (req: express.Request) {
139 const body: UserCreate = req.body 143 const body: UserCreate = req.body
140 const user = new UserModel({ 144 const userToCreate = new UserModel({
141 username: body.username, 145 username: body.username,
142 password: body.password, 146 password: body.password,
143 email: body.email, 147 email: body.email,
@@ -147,9 +151,11 @@ async function createUser (req: express.Request) {
147 videoQuota: body.videoQuota 151 videoQuota: body.videoQuota
148 }) 152 })
149 153
150 await createUserAccountAndChannel(user) 154 const { user, account } = await createUserAccountAndChannel(userToCreate)
151 155
152 logger.info('User %s with its channel and account created.', body.username) 156 logger.info('User %s with its channel and account created.', body.username)
157
158 return { user, account }
153} 159}
154 160
155async function registerUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { 161async function registerUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) {
diff --git a/server/controllers/api/videos/abuse.ts b/server/controllers/api/videos/abuse.ts
index 91594490b..61ff3af4f 100644
--- a/server/controllers/api/videos/abuse.ts
+++ b/server/controllers/api/videos/abuse.ts
@@ -6,7 +6,7 @@ import { getFormattedObjects } from '../../../helpers/utils'
6import { sequelizeTypescript } from '../../../initializers' 6import { sequelizeTypescript } from '../../../initializers'
7import { sendVideoAbuse } from '../../../lib/activitypub/send' 7import { sendVideoAbuse } from '../../../lib/activitypub/send'
8import { 8import {
9 asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, setDefaultSort, setPagination, videoAbuseReportValidator, 9 asyncMiddleware, authenticate, ensureUserHasRight, paginationValidator, setDefaultSort, setDefaultPagination, videoAbuseReportValidator,
10 videoAbusesSortValidator 10 videoAbusesSortValidator
11} from '../../../middlewares' 11} from '../../../middlewares'
12import { AccountModel } from '../../../models/account/account' 12import { AccountModel } from '../../../models/account/account'
@@ -21,7 +21,7 @@ abuseVideoRouter.get('/abuse',
21 paginationValidator, 21 paginationValidator,
22 videoAbusesSortValidator, 22 videoAbusesSortValidator,
23 setDefaultSort, 23 setDefaultSort,
24 setPagination, 24 setDefaultPagination,
25 asyncMiddleware(listVideoAbuses) 25 asyncMiddleware(listVideoAbuses)
26) 26)
27abuseVideoRouter.post('/:id/abuse', 27abuseVideoRouter.post('/:id/abuse',
diff --git a/server/controllers/api/videos/blacklist.ts b/server/controllers/api/videos/blacklist.ts
index c9087fd97..7eee460d4 100644
--- a/server/controllers/api/videos/blacklist.ts
+++ b/server/controllers/api/videos/blacklist.ts
@@ -3,7 +3,7 @@ import { BlacklistedVideo, UserRight } from '../../../../shared'
3import { logger } from '../../../helpers/logger' 3import { logger } from '../../../helpers/logger'
4import { getFormattedObjects } from '../../../helpers/utils' 4import { getFormattedObjects } from '../../../helpers/utils'
5import { 5import {
6 asyncMiddleware, authenticate, blacklistSortValidator, ensureUserHasRight, paginationValidator, setBlacklistSort, setPagination, 6 asyncMiddleware, authenticate, blacklistSortValidator, ensureUserHasRight, paginationValidator, setBlacklistSort, setDefaultPagination,
7 videosBlacklistAddValidator, videosBlacklistRemoveValidator 7 videosBlacklistAddValidator, videosBlacklistRemoveValidator
8} from '../../../middlewares' 8} from '../../../middlewares'
9import { VideoBlacklistModel } from '../../../models/video/video-blacklist' 9import { VideoBlacklistModel } from '../../../models/video/video-blacklist'
@@ -23,7 +23,7 @@ blacklistRouter.get('/blacklist',
23 paginationValidator, 23 paginationValidator,
24 blacklistSortValidator, 24 blacklistSortValidator,
25 setBlacklistSort, 25 setBlacklistSort,
26 setPagination, 26 setDefaultPagination,
27 asyncMiddleware(listBlacklist) 27 asyncMiddleware(listBlacklist)
28) 28)
29 29
diff --git a/server/controllers/api/videos/channel.ts b/server/controllers/api/videos/channel.ts
index 2012575c8..8ec53d9ae 100644
--- a/server/controllers/api/videos/channel.ts
+++ b/server/controllers/api/videos/channel.ts
@@ -7,7 +7,7 @@ import { sequelizeTypescript } from '../../../initializers'
7import { setAsyncActorKeys } from '../../../lib/activitypub' 7import { setAsyncActorKeys } from '../../../lib/activitypub'
8import { createVideoChannel } from '../../../lib/video-channel' 8import { createVideoChannel } from '../../../lib/video-channel'
9import { 9import {
10 asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setPagination, 10 asyncMiddleware, authenticate, listVideoAccountChannelsValidator, paginationValidator, setDefaultSort, setDefaultPagination,
11 videoChannelsAddValidator, videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator, 11 videoChannelsAddValidator, videoChannelsGetValidator, videoChannelsRemoveValidator, videoChannelsSortValidator,
12 videoChannelsUpdateValidator 12 videoChannelsUpdateValidator
13} from '../../../middlewares' 13} from '../../../middlewares'
@@ -20,7 +20,7 @@ videoChannelRouter.get('/channels',
20 paginationValidator, 20 paginationValidator,
21 videoChannelsSortValidator, 21 videoChannelsSortValidator,
22 setDefaultSort, 22 setDefaultSort,
23 setPagination, 23 setDefaultPagination,
24 asyncMiddleware(listVideoChannels) 24 asyncMiddleware(listVideoChannels)
25) 25)
26 26
diff --git a/server/controllers/api/videos/comment.ts b/server/controllers/api/videos/comment.ts
index 3c2727530..f8a669e35 100644
--- a/server/controllers/api/videos/comment.ts
+++ b/server/controllers/api/videos/comment.ts
@@ -6,7 +6,7 @@ import { logger } from '../../../helpers/logger'
6import { getFormattedObjects } from '../../../helpers/utils' 6import { getFormattedObjects } from '../../../helpers/utils'
7import { sequelizeTypescript } from '../../../initializers' 7import { sequelizeTypescript } from '../../../initializers'
8import { buildFormattedCommentTree, createVideoComment } from '../../../lib/video-comment' 8import { buildFormattedCommentTree, createVideoComment } from '../../../lib/video-comment'
9import { asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setPagination } from '../../../middlewares' 9import { asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setDefaultPagination } from '../../../middlewares'
10import { videoCommentThreadsSortValidator } from '../../../middlewares/validators' 10import { videoCommentThreadsSortValidator } from '../../../middlewares/validators'
11import { 11import {
12 addVideoCommentReplyValidator, addVideoCommentThreadValidator, listVideoCommentThreadsValidator, listVideoThreadCommentsValidator, 12 addVideoCommentReplyValidator, addVideoCommentThreadValidator, listVideoCommentThreadsValidator, listVideoThreadCommentsValidator,
@@ -21,7 +21,7 @@ videoCommentRouter.get('/:videoId/comment-threads',
21 paginationValidator, 21 paginationValidator,
22 videoCommentThreadsSortValidator, 22 videoCommentThreadsSortValidator,
23 setDefaultSort, 23 setDefaultSort,
24 setPagination, 24 setDefaultPagination,
25 asyncMiddleware(listVideoCommentThreadsValidator), 25 asyncMiddleware(listVideoCommentThreadsValidator),
26 asyncMiddleware(listVideoThreads) 26 asyncMiddleware(listVideoThreads)
27) 27)
diff --git a/server/controllers/api/videos/index.ts b/server/controllers/api/videos/index.ts
index 6a7f1f184..c2fdb4f95 100644
--- a/server/controllers/api/videos/index.ts
+++ b/server/controllers/api/videos/index.ts
@@ -14,7 +14,7 @@ import { fetchRemoteVideoDescription, getVideoActivityPubUrl, shareVideoByServer
14import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' 14import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send'
15import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler' 15import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler'
16import { 16import {
17 asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setPagination, videosAddValidator, videosGetValidator, 17 asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setDefaultPagination, videosAddValidator, videosGetValidator,
18 videosRemoveValidator, videosSearchValidator, videosSortValidator, videosUpdateValidator 18 videosRemoveValidator, videosSearchValidator, videosSortValidator, videosUpdateValidator
19} from '../../../middlewares' 19} from '../../../middlewares'
20import { TagModel } from '../../../models/video/tag' 20import { TagModel } from '../../../models/video/tag'
@@ -45,7 +45,7 @@ videosRouter.get('/',
45 paginationValidator, 45 paginationValidator,
46 videosSortValidator, 46 videosSortValidator,
47 setDefaultSort, 47 setDefaultSort,
48 setPagination, 48 setDefaultPagination,
49 asyncMiddleware(listVideos) 49 asyncMiddleware(listVideos)
50) 50)
51videosRouter.get('/search', 51videosRouter.get('/search',
@@ -53,7 +53,7 @@ videosRouter.get('/search',
53 paginationValidator, 53 paginationValidator,
54 videosSortValidator, 54 videosSortValidator,
55 setDefaultSort, 55 setDefaultSort,
56 setPagination, 56 setDefaultPagination,
57 asyncMiddleware(searchVideos) 57 asyncMiddleware(searchVideos)
58) 58)
59videosRouter.put('/:id', 59videosRouter.put('/:id',
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts
index 7b63a9ccd..c10213890 100644
--- a/server/initializers/constants.ts
+++ b/server/initializers/constants.ts
@@ -283,7 +283,7 @@ const ACTIVITY_PUB = {
283 FETCH_PAGE_LIMIT: 100, 283 FETCH_PAGE_LIMIT: 100,
284 MAX_HTTP_ATTEMPT: 5, 284 MAX_HTTP_ATTEMPT: 5,
285 URL_MIME_TYPES: { 285 URL_MIME_TYPES: {
286 VIDEO: [ 'video/mp4', 'video/webm', 'video/ogg' ], // TODO: Merge with VIDEO_MIMETYPE_EXT 286 VIDEO: Object.keys(VIDEO_MIMETYPE_EXT),
287 TORRENT: [ 'application/x-bittorrent' ], 287 TORRENT: [ 'application/x-bittorrent' ],
288 MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ] 288 MAGNET: [ 'application/x-bittorrent;x-scheme-handler/magnet' ]
289 }, 289 },
diff --git a/server/lib/activitypub/actor.ts b/server/lib/activitypub/actor.ts
index 2e0f3cfc2..a39b4e137 100644
--- a/server/lib/activitypub/actor.ts
+++ b/server/lib/activitypub/actor.ts
@@ -309,7 +309,10 @@ async function refreshActorIfNeeded (actor: ActorModel) {
309 309
310 const actorUrl = await getUrlFromWebfinger(actor.preferredUsername, actor.getHost()) 310 const actorUrl = await getUrlFromWebfinger(actor.preferredUsername, actor.getHost())
311 const result = await fetchRemoteActor(actorUrl) 311 const result = await fetchRemoteActor(actorUrl)
312 if (result === undefined) throw new Error('Cannot fetch remote actor in refresh actor.') 312 if (result === undefined) {
313 logger.warn('Cannot fetch remote actor in refresh actor.')
314 return actor
315 }
313 316
314 return sequelizeTypescript.transaction(async t => { 317 return sequelizeTypescript.transaction(async t => {
315 updateInstanceWithAnother(actor, result.actor) 318 updateInstanceWithAnother(actor, result.actor)
diff --git a/server/lib/activitypub/process/process-delete.ts b/server/lib/activitypub/process/process-delete.ts
index 07e6a0075..03eadcbfc 100644
--- a/server/lib/activitypub/process/process-delete.ts
+++ b/server/lib/activitypub/process/process-delete.ts
@@ -10,21 +10,26 @@ import { VideoCommentModel } from '../../../models/video/video-comment'
10import { getOrCreateActorAndServerAndModel } from '../actor' 10import { getOrCreateActorAndServerAndModel } from '../actor'
11 11
12async function processDeleteActivity (activity: ActivityDelete) { 12async function processDeleteActivity (activity: ActivityDelete) {
13 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
14 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id 13 const objectUrl = typeof activity.object === 'string' ? activity.object : activity.object.id
15 14
16 if (actor.url === objectUrl) { 15 if (activity.actor === objectUrl) {
16 let actor = await ActorModel.loadByUrl(activity.actor)
17 if (!actor) return
18
17 if (actor.type === 'Person') { 19 if (actor.type === 'Person') {
18 if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.') 20 if (!actor.Account) throw new Error('Actor ' + actor.url + ' is a person but we cannot find it in database.')
19 21
22 actor.Account.Actor = await actor.Account.$get('Actor') as ActorModel
20 return processDeleteAccount(actor.Account) 23 return processDeleteAccount(actor.Account)
21 } else if (actor.type === 'Group') { 24 } else if (actor.type === 'Group') {
22 if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.') 25 if (!actor.VideoChannel) throw new Error('Actor ' + actor.url + ' is a group but we cannot find it in database.')
23 26
27 actor.VideoChannel.Actor = await actor.VideoChannel.$get('Actor') as ActorModel
24 return processDeleteVideoChannel(actor.VideoChannel) 28 return processDeleteVideoChannel(actor.VideoChannel)
25 } 29 }
26 } 30 }
27 31
32 const actor = await getOrCreateActorAndServerAndModel(activity.actor)
28 { 33 {
29 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl) 34 const videoCommentInstance = await VideoCommentModel.loadByUrlAndPopulateAccount(objectUrl)
30 if (videoCommentInstance) { 35 if (videoCommentInstance) {
diff --git a/server/lib/activitypub/send/send-delete.ts b/server/lib/activitypub/send/send-delete.ts
index 995a534a6..9f1ea3bd0 100644
--- a/server/lib/activitypub/send/send-delete.ts
+++ b/server/lib/activitypub/send/send-delete.ts
@@ -23,7 +23,10 @@ async function sendDeleteActor (byActor: ActorModel, t: Transaction) {
23 const url = getDeleteActivityPubUrl(byActor.url) 23 const url = getDeleteActivityPubUrl(byActor.url)
24 const data = deleteActivityData(url, byActor.url, byActor) 24 const data = deleteActivityData(url, byActor.url, byActor)
25 25
26 return broadcastToFollowers(data, byActor, [ byActor ], t) 26 const actorsInvolved = await VideoShareModel.loadActorsByVideoOwner(byActor.id, t)
27 actorsInvolved.push(byActor)
28
29 return broadcastToFollowers(data, byActor, actorsInvolved, t)
27} 30}
28 31
29async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { 32async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) {
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts
index 5b429709f..1d2d46cbc 100644
--- a/server/lib/activitypub/videos.ts
+++ b/server/lib/activitypub/videos.ts
@@ -20,17 +20,16 @@ import { VideoShareModel } from '../../models/video/video-share'
20import { getOrCreateActorAndServerAndModel } from './actor' 20import { getOrCreateActorAndServerAndModel } from './actor'
21 21
22function fetchRemoteVideoPreview (video: VideoModel, reject: Function) { 22function fetchRemoteVideoPreview (video: VideoModel, reject: Function) {
23 // FIXME: use url
24 const host = video.VideoChannel.Account.Actor.Server.host 23 const host = video.VideoChannel.Account.Actor.Server.host
25 const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName()) 24 const path = join(STATIC_PATHS.PREVIEWS, video.getPreviewName())
26 25
26 // We need to provide a callback, if no we could have an uncaught exception
27 return request.get(REMOTE_SCHEME.HTTP + '://' + host + path, err => { 27 return request.get(REMOTE_SCHEME.HTTP + '://' + host + path, err => {
28 if (err) reject(err) 28 if (err) reject(err)
29 }) 29 })
30} 30}
31 31
32async function fetchRemoteVideoDescription (video: VideoModel) { 32async function fetchRemoteVideoDescription (video: VideoModel) {
33 // FIXME: use url
34 const host = video.VideoChannel.Account.Actor.Server.host 33 const host = video.VideoChannel.Account.Actor.Server.host
35 const path = video.getDescriptionPath() 34 const path = video.getDescriptionPath()
36 const options = { 35 const options = {
diff --git a/server/lib/user.ts b/server/lib/user.ts
index ec1466c6f..aa029cce7 100644
--- a/server/lib/user.ts
+++ b/server/lib/user.ts
@@ -6,15 +6,15 @@ import { UserModel } from '../models/account/user'
6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' 6import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub'
7import { createVideoChannel } from './video-channel' 7import { createVideoChannel } from './video-channel'
8 8
9async function createUserAccountAndChannel (user: UserModel, validateUser = true) { 9async function createUserAccountAndChannel (userToCreate: UserModel, validateUser = true) {
10 const { account, videoChannel } = await sequelizeTypescript.transaction(async t => { 10 const { user, account, videoChannel } = await sequelizeTypescript.transaction(async t => {
11 const userOptions = { 11 const userOptions = {
12 transaction: t, 12 transaction: t,
13 validate: validateUser 13 validate: validateUser
14 } 14 }
15 15
16 const userCreated = await user.save(userOptions) 16 const userCreated = await userToCreate.save(userOptions)
17 const accountCreated = await createLocalAccountWithoutKeys(user.username, user.id, null, t) 17 const accountCreated = await createLocalAccountWithoutKeys(userToCreate.username, userToCreate.id, null, t)
18 18
19 const videoChannelName = `Default ${userCreated.username} channel` 19 const videoChannelName = `Default ${userCreated.username} channel`
20 const videoChannelInfo = { 20 const videoChannelInfo = {
@@ -22,13 +22,13 @@ async function createUserAccountAndChannel (user: UserModel, validateUser = true
22 } 22 }
23 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t) 23 const videoChannel = await createVideoChannel(videoChannelInfo, accountCreated, t)
24 24
25 return { account: accountCreated, videoChannel } 25 return { user: userCreated, account: accountCreated, videoChannel }
26 }) 26 })
27 27
28 account.Actor = await setAsyncActorKeys(account.Actor) 28 account.Actor = await setAsyncActorKeys(account.Actor)
29 videoChannel.Actor = await setAsyncActorKeys(videoChannel.Actor) 29 videoChannel.Actor = await setAsyncActorKeys(videoChannel.Actor)
30 30
31 return { account, videoChannel } 31 return { user, account, videoChannel }
32} 32}
33 33
34async function createLocalAccountWithoutKeys ( 34async function createLocalAccountWithoutKeys (
diff --git a/server/middlewares/pagination.ts b/server/middlewares/pagination.ts
index 26a8cacf0..2ea2a6b82 100644
--- a/server/middlewares/pagination.ts
+++ b/server/middlewares/pagination.ts
@@ -3,7 +3,7 @@ import * as express from 'express'
3 3
4import { PAGINATION_COUNT_DEFAULT } from '../initializers' 4import { PAGINATION_COUNT_DEFAULT } from '../initializers'
5 5
6function setPagination (req: express.Request, res: express.Response, next: express.NextFunction) { 6function setDefaultPagination (req: express.Request, res: express.Response, next: express.NextFunction) {
7 if (!req.query.start) req.query.start = 0 7 if (!req.query.start) req.query.start = 0
8 else req.query.start = parseInt(req.query.start, 10) 8 else req.query.start = parseInt(req.query.start, 10)
9 9
@@ -16,5 +16,5 @@ function setPagination (req: express.Request, res: express.Response, next: expre
16// --------------------------------------------------------------------------- 16// ---------------------------------------------------------------------------
17 17
18export { 18export {
19 setPagination 19 setDefaultPagination
20} 20}
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index f81c50180..20724ae0c 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -1,9 +1,10 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table, 3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Model, Table,
4 UpdatedAt 4 UpdatedAt
5} from 'sequelize-typescript' 5} from 'sequelize-typescript'
6import { Account } from '../../../shared/models/actors' 6import { Account } from '../../../shared/models/actors'
7import { logger } from '../../helpers/logger'
7import { sendDeleteActor } from '../../lib/activitypub/send' 8import { sendDeleteActor } from '../../lib/activitypub/send'
8import { ActorModel } from '../activitypub/actor' 9import { ActorModel } from '../activitypub/actor'
9import { ApplicationModel } from '../application/application' 10import { ApplicationModel } from '../application/application'
@@ -11,6 +12,7 @@ import { AvatarModel } from '../avatar/avatar'
11import { ServerModel } from '../server/server' 12import { ServerModel } from '../server/server'
12import { getSort } from '../utils' 13import { getSort } from '../utils'
13import { VideoChannelModel } from '../video/video-channel' 14import { VideoChannelModel } from '../video/video-channel'
15import { VideoCommentModel } from '../video/video-comment'
14import { UserModel } from './user' 16import { UserModel } from './user'
15 17
16@DefaultScope({ 18@DefaultScope({
@@ -80,7 +82,7 @@ export class AccountModel extends Model<AccountModel> {
80 }, 82 },
81 onDelete: 'cascade' 83 onDelete: 'cascade'
82 }) 84 })
83 Account: ApplicationModel 85 Application: ApplicationModel
84 86
85 @HasMany(() => VideoChannelModel, { 87 @HasMany(() => VideoChannelModel, {
86 foreignKey: { 88 foreignKey: {
@@ -91,10 +93,24 @@ export class AccountModel extends Model<AccountModel> {
91 }) 93 })
92 VideoChannels: VideoChannelModel[] 94 VideoChannels: VideoChannelModel[]
93 95
94 @AfterDestroy 96 @HasMany(() => VideoCommentModel, {
95 static sendDeleteIfOwned (instance: AccountModel) { 97 foreignKey: {
98 allowNull: false
99 },
100 onDelete: 'cascade',
101 hooks: true
102 })
103 VideoComments: VideoCommentModel[]
104
105 @BeforeDestroy
106 static async sendDeleteIfOwned (instance: AccountModel, options) {
107 if (!instance.Actor) {
108 instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
109 }
110
96 if (instance.isOwned()) { 111 if (instance.isOwned()) {
97 return sendDeleteActor(instance.Actor, undefined) 112 logger.debug('Sending delete of actor of account %s.', instance.Actor.url)
113 return sendDeleteActor(instance.Actor, options.transaction)
98 } 114 }
99 115
100 return undefined 116 return undefined
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index e37fd4d3b..8eb88062a 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -94,7 +94,8 @@ export class UserModel extends Model<UserModel> {
94 94
95 @HasOne(() => AccountModel, { 95 @HasOne(() => AccountModel, {
96 foreignKey: 'userId', 96 foreignKey: 'userId',
97 onDelete: 'cascade' 97 onDelete: 'cascade',
98 hooks: true
98 }) 99 })
99 Account: AccountModel 100 Account: AccountModel
100 101
diff --git a/server/models/activitypub/actor.ts b/server/models/activitypub/actor.ts
index b7be9c32c..408d4df23 100644
--- a/server/models/activitypub/actor.ts
+++ b/server/models/activitypub/actor.ts
@@ -155,7 +155,8 @@ export class ActorModel extends Model<ActorModel> {
155 foreignKey: { 155 foreignKey: {
156 allowNull: true 156 allowNull: true
157 }, 157 },
158 onDelete: 'set null' 158 onDelete: 'set null',
159 hooks: true
159 }) 160 })
160 Avatar: AvatarModel 161 Avatar: AvatarModel
161 162
@@ -194,7 +195,8 @@ export class ActorModel extends Model<ActorModel> {
194 foreignKey: { 195 foreignKey: {
195 allowNull: true 196 allowNull: true
196 }, 197 },
197 onDelete: 'cascade' 198 onDelete: 'cascade',
199 hooks: true
198 }) 200 })
199 Account: AccountModel 201 Account: AccountModel
200 202
@@ -202,7 +204,8 @@ export class ActorModel extends Model<ActorModel> {
202 foreignKey: { 204 foreignKey: {
203 allowNull: true 205 allowNull: true
204 }, 206 },
205 onDelete: 'cascade' 207 onDelete: 'cascade',
208 hooks: true
206 }) 209 })
207 VideoChannel: VideoChannelModel 210 VideoChannel: VideoChannelModel
208 211
diff --git a/server/models/server/server.ts b/server/models/server/server.ts
index c43146156..9749f503e 100644
--- a/server/models/server/server.ts
+++ b/server/models/server/server.ts
@@ -1,5 +1,6 @@
1import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 1import { AllowNull, Column, CreatedAt, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
2import { isHostValid } from '../../helpers/custom-validators/servers' 2import { isHostValid } from '../../helpers/custom-validators/servers'
3import { ActorModel } from '../activitypub/actor'
3import { throwIfNotValid } from '../utils' 4import { throwIfNotValid } from '../utils'
4 5
5@Table({ 6@Table({
@@ -23,4 +24,14 @@ export class ServerModel extends Model<ServerModel> {
23 24
24 @UpdatedAt 25 @UpdatedAt
25 updatedAt: Date 26 updatedAt: Date
27
28 @HasMany(() => ActorModel, {
29 foreignKey: {
30 name: 'serverId',
31 allowNull: true
32 },
33 onDelete: 'CASCADE',
34 hooks: true
35 })
36 Actors: ActorModel[]
26} 37}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index e2cbf0422..7c161c864 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,20 +1,10 @@
1import { 1import {
2 AfterDestroy, 2 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DefaultScope, ForeignKey, HasMany, Is, Model, Scopes, Table,
3 AllowNull,
4 BelongsTo,
5 Column,
6 CreatedAt,
7 DefaultScope,
8 ForeignKey,
9 HasMany,
10 Is,
11 Model,
12 Scopes,
13 Table,
14 UpdatedAt 3 UpdatedAt
15} from 'sequelize-typescript' 4} from 'sequelize-typescript'
16import { ActivityPubActor } from '../../../shared/models/activitypub' 5import { ActivityPubActor } from '../../../shared/models/activitypub'
17import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' 6import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels'
7import { logger } from '../../helpers/logger'
18import { sendDeleteActor } from '../../lib/activitypub/send' 8import { sendDeleteActor } from '../../lib/activitypub/send'
19import { AccountModel } from '../account/account' 9import { AccountModel } from '../account/account'
20import { ActorModel } from '../activitypub/actor' 10import { ActorModel } from '../activitypub/actor'
@@ -116,14 +106,21 @@ export class VideoChannelModel extends Model<VideoChannelModel> {
116 name: 'channelId', 106 name: 'channelId',
117 allowNull: false 107 allowNull: false
118 }, 108 },
119 onDelete: 'CASCADE' 109 onDelete: 'CASCADE',
110 hooks: true
120 }) 111 })
121 Videos: VideoModel[] 112 Videos: VideoModel[]
122 113
123 @AfterDestroy 114 @BeforeDestroy
124 static sendDeleteIfOwned (instance: VideoChannelModel) { 115 static async sendDeleteIfOwned (instance: VideoChannelModel, options) {
116 if (!instance.Actor) {
117 instance.Actor = await instance.$get('Actor', { transaction: options.transaction }) as ActorModel
118 }
119
125 if (instance.Actor.isOwned()) { 120 if (instance.Actor.isOwned()) {
126 return sendDeleteActor(instance.Actor, undefined) 121 logger.debug('Sending delete of actor of video channel %s.', instance.Actor.url)
122
123 return sendDeleteActor(instance.Actor, options.transaction)
127 } 124 }
128 125
129 return undefined 126 return undefined
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts
index c10d7c7c8..ab909b0b8 100644
--- a/server/models/video/video-comment.ts
+++ b/server/models/video/video-comment.ts
@@ -1,6 +1,6 @@
1import * as Sequelize from 'sequelize' 1import * as Sequelize from 'sequelize'
2import { 2import {
3 AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table, 3 AllowNull, BeforeDestroy, BelongsTo, Column, CreatedAt, DataType, ForeignKey, IFindOptions, Is, Model, Scopes, Table,
4 UpdatedAt 4 UpdatedAt
5} from 'sequelize-typescript' 5} from 'sequelize-typescript'
6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' 6import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects'
@@ -175,10 +175,17 @@ export class VideoCommentModel extends Model<VideoCommentModel> {
175 }) 175 })
176 Account: AccountModel 176 Account: AccountModel
177 177
178 @AfterDestroy 178 @BeforeDestroy
179 static async sendDeleteIfOwned (instance: VideoCommentModel) { 179 static async sendDeleteIfOwned (instance: VideoCommentModel, options) {
180 if (!instance.Account || !instance.Account.Actor) {
181 instance.Account = await instance.$get('Account', {
182 include: [ ActorModel ],
183 transaction: options.transaction
184 }) as AccountModel
185 }
186
180 if (instance.isOwned()) { 187 if (instance.isOwned()) {
181 await sendDeleteVideoComment(instance, undefined) 188 await sendDeleteVideoComment(instance, options.transaction)
182 } 189 }
183 } 190 }
184 191
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index 3e2b4ce64..514edfd9c 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -5,10 +5,9 @@ import * as parseTorrent from 'parse-torrent'
5import { join } from 'path' 5import { join } from 'path'
6import * as Sequelize from 'sequelize' 6import * as Sequelize from 'sequelize'
7import { 7import {
8 AfterDestroy, AllowNull, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany, IFindOptions, Is, 8 AfterDestroy, AllowNull, BeforeDestroy, BelongsTo, BelongsToMany, Column, CreatedAt, DataType, Default, ForeignKey, HasMany,
9 IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt 9 IFindOptions, Is, IsInt, IsUUID, Min, Model, Scopes, Table, UpdatedAt
10} from 'sequelize-typescript' 10} from 'sequelize-typescript'
11import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions'
12import { VideoPrivacy, VideoResolution } from '../../../shared' 11import { VideoPrivacy, VideoResolution } from '../../../shared'
13import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' 12import { VideoTorrentObject } from '../../../shared/models/activitypub/objects'
14import { Video, VideoDetails } from '../../../shared/models/videos' 13import { Video, VideoDetails } from '../../../shared/models/videos'
@@ -22,6 +21,7 @@ import {
22} from '../../helpers/custom-validators/videos' 21} from '../../helpers/custom-validators/videos'
23import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' 22import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils'
24import { logger } from '../../helpers/logger' 23import { logger } from '../../helpers/logger'
24import { getServerActor } from '../../helpers/utils'
25import { 25import {
26 API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES, 26 API_VERSION, CONFIG, CONSTRAINTS_FIELDS, PREVIEWS_SIZE, REMOTE_SCHEME, STATIC_PATHS, THUMBNAILS_SIZE, VIDEO_CATEGORIES,
27 VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES 27 VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES
@@ -31,6 +31,7 @@ import { sendDeleteVideo } from '../../lib/activitypub/send'
31import { AccountModel } from '../account/account' 31import { AccountModel } from '../account/account'
32import { AccountVideoRateModel } from '../account/account-video-rate' 32import { AccountVideoRateModel } from '../account/account-video-rate'
33import { ActorModel } from '../activitypub/actor' 33import { ActorModel } from '../activitypub/actor'
34import { ActorFollowModel } from '../activitypub/actor-follow'
34import { ServerModel } from '../server/server' 35import { ServerModel } from '../server/server'
35import { getSort, throwIfNotValid } from '../utils' 36import { getSort, throwIfNotValid } from '../utils'
36import { TagModel } from './tag' 37import { TagModel } from './tag'
@@ -43,7 +44,6 @@ import { VideoTagModel } from './video-tag'
43 44
44enum ScopeNames { 45enum ScopeNames {
45 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST', 46 AVAILABLE_FOR_LIST = 'AVAILABLE_FOR_LIST',
46 WITH_ACCOUNT_API = 'WITH_ACCOUNT_API',
47 WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS', 47 WITH_ACCOUNT_DETAILS = 'WITH_ACCOUNT_DETAILS',
48 WITH_TAGS = 'WITH_TAGS', 48 WITH_TAGS = 'WITH_TAGS',
49 WITH_FILES = 'WITH_FILES', 49 WITH_FILES = 'WITH_FILES',
@@ -53,34 +53,60 @@ enum ScopeNames {
53} 53}
54 54
55@Scopes({ 55@Scopes({
56 [ScopeNames.AVAILABLE_FOR_LIST]: { 56 [ScopeNames.AVAILABLE_FOR_LIST]: (actorId: number) => ({
57 subQuery: false,
57 where: { 58 where: {
58 id: { 59 id: {
59 [Sequelize.Op.notIn]: Sequelize.literal( 60 [Sequelize.Op.notIn]: Sequelize.literal(
60 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")' 61 '(SELECT "videoBlacklist"."videoId" FROM "videoBlacklist")'
61 ) 62 )
62 }, 63 },
63 privacy: VideoPrivacy.PUBLIC 64 privacy: VideoPrivacy.PUBLIC,
64 } 65 [Sequelize.Op.or]: [
65 }, 66 {
66 [ScopeNames.WITH_ACCOUNT_API]: { 67 '$VideoChannel.Account.Actor.serverId$': null
68 },
69 {
70 '$VideoChannel.Account.Actor.followers.actorId$': actorId
71 },
72 {
73 id: {
74 [ Sequelize.Op.in ]: Sequelize.literal(
75 '(' +
76 'SELECT "videoShare"."videoId" FROM "videoShare" ' +
77 'INNER JOIN "actorFollow" ON "actorFollow"."targetActorId" = "videoShare"."actorId" ' +
78 'WHERE "actorFollow"."actorId" = ' + parseInt(actorId.toString(), 10) +
79 ')'
80 )
81 }
82 }
83 ]
84 },
67 include: [ 85 include: [
68 { 86 {
69 model: () => VideoChannelModel.unscoped(), 87 attributes: [ 'name', 'description' ],
88 model: VideoChannelModel.unscoped(),
70 required: true, 89 required: true,
71 include: [ 90 include: [
72 { 91 {
73 attributes: [ 'name' ], 92 attributes: [ 'name' ],
74 model: () => AccountModel.unscoped(), 93 model: AccountModel.unscoped(),
75 required: true, 94 required: true,
76 include: [ 95 include: [
77 { 96 {
78 attributes: [ 'serverId' ], 97 attributes: [ 'serverId' ],
79 model: () => ActorModel.unscoped(), 98 model: ActorModel.unscoped(),
80 required: true, 99 required: true,
81 include: [ 100 include: [
82 { 101 {
83 model: () => ServerModel.unscoped(), 102 attributes: [ 'host' ],
103 model: ServerModel.unscoped(),
104 required: false
105 },
106 {
107 attributes: [ ],
108 model: ActorFollowModel.unscoped(),
109 as: 'followers',
84 required: false 110 required: false
85 } 111 }
86 ] 112 ]
@@ -90,7 +116,7 @@ enum ScopeNames {
90 ] 116 ]
91 } 117 }
92 ] 118 ]
93 }, 119 }),
94 [ScopeNames.WITH_ACCOUNT_DETAILS]: { 120 [ScopeNames.WITH_ACCOUNT_DETAILS]: {
95 include: [ 121 include: [
96 { 122 {
@@ -347,23 +373,46 @@ export class VideoModel extends Model<VideoModel> {
347 name: 'videoId', 373 name: 'videoId',
348 allowNull: false 374 allowNull: false
349 }, 375 },
350 onDelete: 'cascade' 376 onDelete: 'cascade',
377 hooks: true
351 }) 378 })
352 VideoComments: VideoCommentModel[] 379 VideoComments: VideoCommentModel[]
353 380
381 @BeforeDestroy
382 static async sendDelete (instance: VideoModel, options) {
383 if (instance.isOwned()) {
384 if (!instance.VideoChannel) {
385 instance.VideoChannel = await instance.$get('VideoChannel', {
386 include: [
387 {
388 model: AccountModel,
389 include: [ ActorModel ]
390 }
391 ],
392 transaction: options.transaction
393 }) as VideoChannelModel
394 }
395
396 logger.debug('Sending delete of video %s.', instance.url)
397
398 return sendDeleteVideo(instance, options.transaction)
399 }
400
401 return undefined
402 }
403
354 @AfterDestroy 404 @AfterDestroy
355 static removeFilesAndSendDelete (instance: VideoModel) { 405 static async removeFilesAndSendDelete (instance: VideoModel) {
356 const tasks = [] 406 const tasks: Promise<any>[] = []
357 407
358 tasks.push( 408 tasks.push(instance.removeThumbnail())
359 instance.removeThumbnail()
360 )
361 409
362 if (instance.isOwned()) { 410 if (instance.isOwned()) {
363 tasks.push( 411 if (!Array.isArray(instance.VideoFiles)) {
364 instance.removePreview(), 412 instance.VideoFiles = await instance.$get('VideoFiles') as VideoFileModel[]
365 sendDeleteVideo(instance, undefined) 413 }
366 ) 414
415 tasks.push(instance.removePreview())
367 416
368 // Remove physical files and torrents 417 // Remove physical files and torrents
369 instance.VideoFiles.forEach(file => { 418 instance.VideoFiles.forEach(file => {
@@ -500,14 +549,16 @@ export class VideoModel extends Model<VideoModel> {
500 }) 549 })
501 } 550 }
502 551
503 static listForApi (start: number, count: number, sort: string) { 552 static async listForApi (start: number, count: number, sort: string) {
504 const query = { 553 const query = {
505 offset: start, 554 offset: start,
506 limit: count, 555 limit: count,
507 order: [ getSort(sort) ] 556 order: [ getSort(sort) ]
508 } 557 }
509 558
510 return VideoModel.scope([ ScopeNames.AVAILABLE_FOR_LIST, ScopeNames.WITH_ACCOUNT_API ]) 559 const serverActor = await getServerActor()
560
561 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] })
511 .findAndCountAll(query) 562 .findAndCountAll(query)
512 .then(({ rows, count }) => { 563 .then(({ rows, count }) => {
513 return { 564 return {
@@ -517,6 +568,29 @@ export class VideoModel extends Model<VideoModel> {
517 }) 568 })
518 } 569 }
519 570
571 static async searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
572 const query: IFindOptions<VideoModel> = {
573 offset: start,
574 limit: count,
575 order: [ getSort(sort) ],
576 where: {
577 name: {
578 [Sequelize.Op.iLike]: '%' + value + '%'
579 }
580 }
581 }
582
583 const serverActor = await getServerActor()
584
585 return VideoModel.scope({ method: [ ScopeNames.AVAILABLE_FOR_LIST, serverActor.id ] })
586 .findAndCountAll(query).then(({ rows, count }) => {
587 return {
588 data: rows,
589 total: count
590 }
591 })
592 }
593
520 static load (id: number) { 594 static load (id: number) {
521 return VideoModel.findById(id) 595 return VideoModel.findById(id)
522 } 596 }
@@ -603,74 +677,6 @@ export class VideoModel extends Model<VideoModel> {
603 .findOne(options) 677 .findOne(options)
604 } 678 }
605 679
606 static searchAndPopulateAccountAndServerAndTags (value: string, start: number, count: number, sort: string) {
607 const serverInclude: IIncludeOptions = {
608 model: ServerModel,
609 required: false
610 }
611
612 const accountInclude: IIncludeOptions = {
613 model: AccountModel,
614 include: [
615 {
616 model: ActorModel,
617 required: true,
618 include: [ serverInclude ]
619 }
620 ]
621 }
622
623 const videoChannelInclude: IIncludeOptions = {
624 model: VideoChannelModel,
625 include: [ accountInclude ],
626 required: true
627 }
628
629 const tagInclude: IIncludeOptions = {
630 model: TagModel
631 }
632
633 const query: IFindOptions<VideoModel> = {
634 distinct: true, // Because we have tags
635 offset: start,
636 limit: count,
637 order: [ getSort(sort) ],
638 where: {}
639 }
640
641 // TODO: search on tags too
642 // const escapedValue = Video['sequelize'].escape('%' + value + '%')
643 // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal(
644 // `(SELECT "VideoTags"."videoId"
645 // FROM "Tags"
646 // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId"
647 // WHERE name ILIKE ${escapedValue}
648 // )`
649 // )
650
651 // TODO: search on account too
652 // accountInclude.where = {
653 // name: {
654 // [Sequelize.Op.iLike]: '%' + value + '%'
655 // }
656 // }
657 query.where['name'] = {
658 [Sequelize.Op.iLike]: '%' + value + '%'
659 }
660
661 query.include = [
662 videoChannelInclude, tagInclude
663 ]
664
665 return VideoModel.scope([ ScopeNames.AVAILABLE_FOR_LIST ])
666 .findAndCountAll(query).then(({ rows, count }) => {
667 return {
668 data: rows,
669 total: count
670 }
671 })
672 }
673
674 getOriginalFile () { 680 getOriginalFile () {
675 if (Array.isArray(this.VideoFiles) === false) return undefined 681 if (Array.isArray(this.VideoFiles) === false) return undefined
676 682
diff --git a/server/tests/api/check-params/users.ts b/server/tests/api/check-params/users.ts
index 14fcf8703..0c9d933a7 100644
--- a/server/tests/api/check-params/users.ts
+++ b/server/tests/api/check-params/users.ts
@@ -185,7 +185,7 @@ describe('Test users API validators', function () {
185 path, 185 path,
186 token: server.accessToken, 186 token: server.accessToken,
187 fields: baseCorrectParams, 187 fields: baseCorrectParams,
188 statusCodeExpected: 204 188 statusCodeExpected: 200
189 }) 189 })
190 }) 190 })
191 191
diff --git a/server/tests/api/server/follows.ts b/server/tests/api/server/follows.ts
index fad58e840..ac614d605 100644
--- a/server/tests/api/server/follows.ts
+++ b/server/tests/api/server/follows.ts
@@ -4,7 +4,7 @@ import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { Video, VideoPrivacy } from '../../../../shared/models/videos' 5import { Video, VideoPrivacy } from '../../../../shared/models/videos'
6import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' 6import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
7import { completeVideoCheck } from '../../utils' 7import { checkVideoFilesWereRemoved, completeVideoCheck, getVideoChannelsList } from '../../utils'
8 8
9import { 9import {
10 flushAndRunMultipleServers, flushTests, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo, 10 flushAndRunMultipleServers, flushTests, getVideosList, killallServers, ServerInfo, setAccessTokensToServers, uploadVideo,
@@ -12,7 +12,7 @@ import {
12} from '../../utils/index' 12} from '../../utils/index'
13import { dateIsValid } from '../../utils/miscs/miscs' 13import { dateIsValid } from '../../utils/miscs/miscs'
14import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' 14import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows'
15import { expectAccountFollows } from '../../utils/users/accounts' 15import { expectAccountFollows, getAccountsList } from '../../utils/users/accounts'
16import { userLogin } from '../../utils/users/login' 16import { userLogin } from '../../utils/users/login'
17import { createUser } from '../../utils/users/users' 17import { createUser } from '../../utils/users/users'
18import { 18import {
@@ -343,6 +343,26 @@ describe('Test follows', function () {
343 expect(secondChild.comment.text).to.equal('my second answer to thread 1') 343 expect(secondChild.comment.text).to.equal('my second answer to thread 1')
344 expect(secondChild.children).to.have.lengthOf(0) 344 expect(secondChild.children).to.have.lengthOf(0)
345 }) 345 })
346
347 it('Should unfollow server 3 on server 1 and does not list server 3 videos', async function () {
348 this.timeout(5000)
349
350 await unfollow(servers[0].url, servers[0].accessToken, servers[2])
351
352 await wait(3000)
353
354 let res = await getVideosList(servers[ 0 ].url)
355 expect(res.body.total).to.equal(1)
356
357 res = await getVideoChannelsList(servers[0].url, 0, 1)
358 expect(res.body.total).to.equal(2)
359
360 res = await getAccountsList(servers[0].url)
361 expect(res.body.total).to.equal(2)
362
363 await checkVideoFilesWereRemoved(video4.uuid, servers[0].serverNumber)
364 })
365
346 }) 366 })
347 367
348 after(async function () { 368 after(async function () {
diff --git a/server/tests/api/users/users-multiple-servers.ts b/server/tests/api/users/users-multiple-servers.ts
index 1c7f011a8..0483b9c3d 100644
--- a/server/tests/api/users/users-multiple-servers.ts
+++ b/server/tests/api/users/users-multiple-servers.ts
@@ -3,18 +3,20 @@
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { Account } from '../../../../shared/models/actors' 5import { Account } from '../../../../shared/models/actors'
6import { doubleFollow, flushAndRunMultipleServers, wait } from '../../utils' 6import { checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, userLogin, wait } from '../../utils'
7import { 7import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index'
8 flushTests, getMyUserInformation, killallServers, ServerInfo, testVideoImage, updateMyAvatar, 8import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts'
9 uploadVideo
10} from '../../utils/index'
11import { getAccount, getAccountsList } from '../../utils/users/accounts'
12import { setAccessTokensToServers } from '../../utils/users/login' 9import { setAccessTokensToServers } from '../../utils/users/login'
13 10
14const expect = chai.expect 11const expect = chai.expect
15 12
16describe('Test users with multiple servers', function () { 13describe('Test users with multiple servers', function () {
17 let servers: ServerInfo[] = [] 14 let servers: ServerInfo[] = []
15 let user
16 let userUUID
17 let userId
18 let videoUUID
19 let userAccessToken
18 20
19 before(async function () { 21 before(async function () {
20 this.timeout(120000) 22 this.timeout(120000)
@@ -34,6 +36,18 @@ describe('Test users with multiple servers', function () {
34 // The root user of server 1 is propagated to servers 2 and 3 36 // The root user of server 1 is propagated to servers 2 and 3
35 await uploadVideo(servers[0].url, servers[0].accessToken, {}) 37 await uploadVideo(servers[0].url, servers[0].accessToken, {})
36 38
39 const user = {
40 username: 'user1',
41 password: 'password'
42 }
43 const resUser = await createUser(servers[0].url, servers[0].accessToken, user.username, user.password)
44 userUUID = resUser.body.user.uuid
45 userId = resUser.body.user.id
46 userAccessToken = await userLogin(servers[0], user)
47
48 const resVideo = await uploadVideo(servers[0].url, userAccessToken, {})
49 videoUUID = resVideo.body.uuid
50
37 await wait(5000) 51 await wait(5000)
38 }) 52 })
39 53
@@ -49,9 +63,9 @@ describe('Test users with multiple servers', function () {
49 }) 63 })
50 64
51 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken) 65 const res = await getMyUserInformation(servers[0].url, servers[0].accessToken)
52 const user = res.body 66 user = res.body
53 67
54 const test = await testVideoImage(servers[0].url, 'avatar2-resized', user.account.avatar.path, '.png') 68 const test = await testImage(servers[0].url, 'avatar2-resized', user.account.avatar.path, '.png')
55 expect(test).to.equal(true) 69 expect(test).to.equal(true)
56 70
57 await wait(5000) 71 await wait(5000)
@@ -69,11 +83,45 @@ describe('Test users with multiple servers', function () {
69 expect(rootServer1Get.name).to.equal('root') 83 expect(rootServer1Get.name).to.equal('root')
70 expect(rootServer1Get.host).to.equal('localhost:9001') 84 expect(rootServer1Get.host).to.equal('localhost:9001')
71 85
72 const test = await testVideoImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png') 86 const test = await testImage(server.url, 'avatar2-resized', rootServer1Get.avatar.path, '.png')
73 expect(test).to.equal(true) 87 expect(test).to.equal(true)
74 } 88 }
75 }) 89 })
76 90
91 it('Should remove the user', async function () {
92 this.timeout(10000)
93
94 for (const server of servers) {
95 const resAccounts = await getAccountsList(server.url, '-createdAt')
96
97 const userServer1List = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
98 expect(userServer1List).not.to.be.undefined
99 }
100
101 await removeUser(servers[0].url, userId, servers[0].accessToken)
102
103 await wait(5000)
104
105 for (const server of servers) {
106 const resAccounts = await getAccountsList(server.url, '-createdAt')
107
108 const userServer1List = resAccounts.body.data.find(a => a.name === 'user1' && a.host === 'localhost:9001') as Account
109 expect(userServer1List).to.be.undefined
110 }
111 })
112
113 it('Should not have actor files', async () => {
114 for (const server of servers) {
115 await checkActorFilesWereRemoved(userUUID, server.serverNumber)
116 }
117 })
118
119 it('Should not have video files', async () => {
120 for (const server of servers) {
121 await checkVideoFilesWereRemoved(videoUUID, server.serverNumber)
122 }
123 })
124
77 after(async function () { 125 after(async function () {
78 killallServers(servers) 126 killallServers(servers)
79 127
diff --git a/server/tests/api/users/users.ts b/server/tests/api/users/users.ts
index b788637e7..d8004ff24 100644
--- a/server/tests/api/users/users.ts
+++ b/server/tests/api/users/users.ts
@@ -7,7 +7,7 @@ import {
7 createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating, getUserInformation, 7 createUser, flushTests, getBlacklistedVideosList, getMyUserInformation, getMyUserVideoQuotaUsed, getMyUserVideoRating, getUserInformation,
8 getUsersList, 8 getUsersList,
9 getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo, registerUser, removeUser, removeVideo, 9 getUsersListPaginationAndSort, getVideosList, killallServers, login, makePutBodyRequest, rateVideo, registerUser, removeUser, removeVideo,
10 runServer, ServerInfo, serverLogin, testVideoImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo 10 runServer, ServerInfo, serverLogin, testImage, updateMyAvatar, updateMyUser, updateUser, uploadVideo
11} from '../../utils/index' 11} from '../../utils/index'
12import { follow } from '../../utils/server/follows' 12import { follow } from '../../utils/server/follows'
13import { setAccessTokensToServers } from '../../utils/users/login' 13import { setAccessTokensToServers } from '../../utils/users/login'
@@ -361,7 +361,7 @@ describe('Test users', function () {
361 const res = await getMyUserInformation(server.url, accessTokenUser) 361 const res = await getMyUserInformation(server.url, accessTokenUser)
362 const user = res.body 362 const user = res.body
363 363
364 const test = await testVideoImage(server.url, 'avatar-resized', user.account.avatar.path, '.png') 364 const test = await testImage(server.url, 'avatar-resized', user.account.avatar.path, '.png')
365 expect(test).to.equal(true) 365 expect(test).to.equal(true)
366 }) 366 })
367 367
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts
index 6712829d4..4c4b5123d 100644
--- a/server/tests/api/videos/multiple-servers.ts
+++ b/server/tests/api/videos/multiple-servers.ts
@@ -8,8 +8,9 @@ import { VideoPrivacy } from '../../../../shared/models/videos'
8import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' 8import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
9 9
10import { 10import {
11 addVideoChannel, completeVideoCheck, createUser, dateIsValid, doubleFollow, flushAndRunMultipleServers, flushTests, getVideo, 11 addVideoChannel, checkVideoFilesWereRemoved, completeVideoCheck, createUser, dateIsValid, doubleFollow, flushAndRunMultipleServers,
12 getVideoChannelsList, getVideosList, killallServers, rateVideo, removeVideo, ServerInfo, setAccessTokensToServers, testVideoImage, 12 flushTests, getVideo,
13 getVideoChannelsList, getVideosList, killallServers, rateVideo, removeVideo, ServerInfo, setAccessTokensToServers, testImage,
13 updateVideo, uploadVideo, userLogin, viewVideo, wait, webtorrentAdd 14 updateVideo, uploadVideo, userLogin, viewVideo, wait, webtorrentAdd
14} from '../../utils' 15} from '../../utils'
15import { 16import {
@@ -578,6 +579,13 @@ describe('Test multiple servers', function () {
578 await wait(5000) 579 await wait(5000)
579 }) 580 })
580 581
582 it('Should not have files of videos 3 and 3-2 on each server', async function () {
583 for (const server of servers) {
584 await checkVideoFilesWereRemoved(toRemove[0].uuid, server.serverNumber)
585 await checkVideoFilesWereRemoved(toRemove[1].uuid, server.serverNumber)
586 }
587 })
588
581 it('Should have videos 1 and 3 on each server', async function () { 589 it('Should have videos 1 and 3 on each server', async function () {
582 for (const server of servers) { 590 for (const server of servers) {
583 const res = await getVideosList(server.url) 591 const res = await getVideosList(server.url)
@@ -624,7 +632,7 @@ describe('Test multiple servers', function () {
624 const res = await getVideo(server.url, videoUUID) 632 const res = await getVideo(server.url, videoUUID)
625 const video = res.body 633 const video = res.body
626 634
627 const test = await testVideoImage(server.url, 'video_short1-preview.webm', video.previewPath) 635 const test = await testImage(server.url, 'video_short1-preview.webm', video.previewPath)
628 expect(test).to.equal(true) 636 expect(test).to.equal(true)
629 } 637 }
630 }) 638 })
diff --git a/server/tests/api/videos/single-server.ts b/server/tests/api/videos/single-server.ts
index ca20f39a0..76d265ec5 100644
--- a/server/tests/api/videos/single-server.ts
+++ b/server/tests/api/videos/single-server.ts
@@ -3,13 +3,12 @@
3import * as chai from 'chai' 3import * as chai from 'chai'
4import { keyBy } from 'lodash' 4import { keyBy } from 'lodash'
5import 'mocha' 5import 'mocha'
6import { join } from 'path'
7import { VideoPrivacy } from '../../../../shared/models/videos' 6import { VideoPrivacy } from '../../../../shared/models/videos'
8import { readdirPromise } from '../../../helpers/core-utils'
9import { 7import {
10 completeVideoCheck, flushTests, getVideo, getVideoCategories, getVideoLanguages, getVideoLicences, getVideoPrivacies, 8 checkVideoFilesWereRemoved, completeVideoCheck, flushTests, getVideo, getVideoCategories, getVideoLanguages, getVideoLicences,
11 getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, removeVideo, runServer, searchVideo, 9 getVideoPrivacies, getVideosList, getVideosListPagination, getVideosListSort, killallServers, rateVideo, removeVideo, runServer,
12 searchVideoWithPagination, searchVideoWithSort, ServerInfo, setAccessTokensToServers, testVideoImage, updateVideo, uploadVideo, viewVideo 10 searchVideo, searchVideoWithPagination, searchVideoWithSort, ServerInfo, setAccessTokensToServers, testImage, updateVideo, uploadVideo,
11 viewVideo
13} from '../../utils' 12} from '../../utils'
14 13
15const expect = chai.expect 14const expect = chai.expect
@@ -277,11 +276,7 @@ describe('Test a single server', function () {
277 it('Should remove the video', async function () { 276 it('Should remove the video', async function () {
278 await removeVideo(server.url, server.accessToken, videoId) 277 await removeVideo(server.url, server.accessToken, videoId)
279 278
280 const files1 = await readdirPromise(join(__dirname, '..', '..', '..', '..', 'test1', 'videos')) 279 await checkVideoFilesWereRemoved(videoUUID, 1)
281 expect(files1).to.have.lengthOf(0)
282
283 const files2 = await readdirPromise(join(__dirname, '..', '..', '..', '..', 'test1', 'thumbnails'))
284 expect(files2).to.have.lengthOf(0)
285 }) 280 })
286 281
287 it('Should not have videos', async function () { 282 it('Should not have videos', async function () {
@@ -346,7 +341,7 @@ describe('Test a single server', function () {
346 341
347 for (const video of videos) { 342 for (const video of videos) {
348 const videoName = video.name.replace(' name', '') 343 const videoName = video.name.replace(' name', '')
349 const test = await testVideoImage(server.url, videoName, video.thumbnailPath) 344 const test = await testImage(server.url, videoName, video.thumbnailPath)
350 345
351 expect(test).to.equal(true) 346 expect(test).to.equal(true)
352 } 347 }
diff --git a/server/tests/api/videos/video-comments.ts b/server/tests/api/videos/video-comments.ts
index 18d484ccf..0eddac35b 100644
--- a/server/tests/api/videos/video-comments.ts
+++ b/server/tests/api/videos/video-comments.ts
@@ -3,7 +3,7 @@
3import * as chai from 'chai' 3import * as chai from 'chai'
4import 'mocha' 4import 'mocha'
5import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' 5import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model'
6import { testVideoImage } from '../../utils' 6import { testImage } from '../../utils'
7import { 7import {
8 dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, updateMyAvatar, 8 dateIsValid, flushTests, killallServers, runServer, ServerInfo, setAccessTokensToServers, updateMyAvatar,
9 uploadVideo 9 uploadVideo
@@ -83,7 +83,7 @@ describe('Test video comments', function () {
83 expect(comment.account.name).to.equal('root') 83 expect(comment.account.name).to.equal('root')
84 expect(comment.account.host).to.equal('localhost:9001') 84 expect(comment.account.host).to.equal('localhost:9001')
85 85
86 const test = await testVideoImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png') 86 const test = await testImage(server.url, 'avatar-resized', comment.account.avatar.path, '.png')
87 expect(test).to.equal(true) 87 expect(test).to.equal(true)
88 88
89 expect(comment.totalReplies).to.equal(0) 89 expect(comment.totalReplies).to.equal(0)
diff --git a/server/tests/utils/miscs/miscs.ts b/server/tests/utils/miscs/miscs.ts
index 2aac37791..e6666619b 100644
--- a/server/tests/utils/miscs/miscs.ts
+++ b/server/tests/utils/miscs/miscs.ts
@@ -1,3 +1,4 @@
1import { join } from 'path'
1import * as WebTorrent from 'webtorrent' 2import * as WebTorrent from 'webtorrent'
2 3
3let webtorrent = new WebTorrent() 4let webtorrent = new WebTorrent()
@@ -24,11 +25,17 @@ function webtorrentAdd (torrent: string, refreshWebTorrent = false) {
24 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res)) 25 return new Promise<WebTorrent.Torrent>(res => webtorrent.add(torrent, res))
25} 26}
26 27
28function root () {
29 // We are in server/tests/utils/miscs
30 return join(__dirname, '..', '..', '..', '..')
31}
32
27// --------------------------------------------------------------------------- 33// ---------------------------------------------------------------------------
28 34
29export { 35export {
30 dateIsValid, 36 dateIsValid,
31 wait, 37 wait,
32 webtorrentAdd, 38 webtorrentAdd,
33 immutableAssign 39 immutableAssign,
40 root
34} 41}
diff --git a/server/tests/utils/users/accounts.ts b/server/tests/utils/users/accounts.ts
index 0ec7992b3..a5c13c319 100644
--- a/server/tests/utils/users/accounts.ts
+++ b/server/tests/utils/users/accounts.ts
@@ -1,5 +1,11 @@
1/* tslint:disable:no-unused-expression */
2
1import { expect } from 'chai' 3import { expect } from 'chai'
4import { existsSync } from 'fs'
5import { join } from 'path'
2import { Account } from '../../../../shared/models/actors' 6import { Account } from '../../../../shared/models/actors'
7import { readdirPromise } from '../../../helpers/core-utils'
8import { root } from '../index'
3import { makeGetRequest } from '../requests/requests' 9import { makeGetRequest } from '../requests/requests'
4 10
5function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) { 11function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) {
@@ -32,10 +38,27 @@ async function expectAccountFollows (url: string, nameWithDomain: string, follow
32 expect(account.followingCount).to.equal(followingCount, message) 38 expect(account.followingCount).to.equal(followingCount, message)
33} 39}
34 40
41async function checkActorFilesWereRemoved (actorUUID: string, serverNumber: number) {
42 const testDirectory = 'test' + serverNumber
43
44 for (const directory of [ 'avatars' ]) {
45 const directoryPath = join(root(), testDirectory, directory)
46
47 const directoryExists = existsSync(directoryPath)
48 expect(directoryExists).to.be.true
49
50 const files = await readdirPromise(directoryPath)
51 for (const file of files) {
52 expect(file).to.not.contain(actorUUID)
53 }
54 }
55}
56
35// --------------------------------------------------------------------------- 57// ---------------------------------------------------------------------------
36 58
37export { 59export {
38 getAccount, 60 getAccount,
39 expectAccountFollows, 61 expectAccountFollows,
40 getAccountsList 62 getAccountsList,
63 checkActorFilesWereRemoved
41} 64}
diff --git a/server/tests/utils/users/users.ts b/server/tests/utils/users/users.ts
index 12945a805..25351e2c7 100644
--- a/server/tests/utils/users/users.ts
+++ b/server/tests/utils/users/users.ts
@@ -11,7 +11,7 @@ function createUser (
11 password: string, 11 password: string,
12 videoQuota = 1000000, 12 videoQuota = 1000000,
13 role: UserRole = UserRole.USER, 13 role: UserRole = UserRole.USER,
14 specialStatus = 204 14 specialStatus = 200
15) { 15) {
16 const path = '/api/v1/users' 16 const path = '/api/v1/users'
17 const body = { 17 const body = {
diff --git a/server/tests/utils/videos/videos.ts b/server/tests/utils/videos/videos.ts
index 095d4e29d..270a6042b 100644
--- a/server/tests/utils/videos/videos.ts
+++ b/server/tests/utils/videos/videos.ts
@@ -1,13 +1,13 @@
1/* tslint:disable:no-unused-expression */ 1/* tslint:disable:no-unused-expression */
2 2
3import { expect } from 'chai' 3import { expect } from 'chai'
4import { readFile } from 'fs' 4import { existsSync, readFile } from 'fs'
5import * as parseTorrent from 'parse-torrent' 5import * as parseTorrent from 'parse-torrent'
6import { extname, isAbsolute, join } from 'path' 6import { extname, isAbsolute, join } from 'path'
7import * as request from 'supertest' 7import * as request from 'supertest'
8import { getMyUserInformation, makeGetRequest, ServerInfo } from '../' 8import { getMyUserInformation, makeGetRequest, root, ServerInfo } from '../'
9import { VideoPrivacy } from '../../../../shared/models/videos' 9import { VideoPrivacy } from '../../../../shared/models/videos'
10import { readFileBufferPromise } from '../../../helpers/core-utils' 10import { readdirPromise, readFileBufferPromise } from '../../../helpers/core-utils'
11import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers' 11import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers'
12import { dateIsValid, webtorrentAdd } from '../index' 12import { dateIsValid, webtorrentAdd } from '../index'
13 13
@@ -203,7 +203,23 @@ function searchVideoWithSort (url: string, search: string, sort: string) {
203 .expect('Content-Type', /json/) 203 .expect('Content-Type', /json/)
204} 204}
205 205
206async function testVideoImage (url: string, imageName: string, imagePath: string, extension = '.jpg') { 206async function checkVideoFilesWereRemoved (videoUUID: string, serverNumber: number) {
207 const testDirectory = 'test' + serverNumber
208
209 for (const directory of [ 'videos', 'thumbnails', 'torrents', 'previews' ]) {
210 const directoryPath = join(root(), testDirectory, directory)
211
212 const directoryExists = existsSync(directoryPath)
213 expect(directoryExists).to.be.true
214
215 const files = await readdirPromise(directoryPath)
216 for (const file of files) {
217 expect(file).to.not.contain(videoUUID)
218 }
219 }
220}
221
222async function testImage (url: string, imageName: string, imagePath: string, extension = '.jpg') {
207 // Don't test images if the node env is not set 223 // Don't test images if the node env is not set
208 // Because we need a special ffmpeg version for this test 224 // Because we need a special ffmpeg version for this test
209 if (process.env['NODE_TEST_IMAGE']) { 225 if (process.env['NODE_TEST_IMAGE']) {
@@ -409,7 +425,7 @@ async function completeVideoCheck (
409 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100) 425 const maxSize = attributeFile.size + ((10 * attributeFile.size) / 100)
410 expect(file.size).to.be.above(minSize).and.below(maxSize) 426 expect(file.size).to.be.above(minSize).and.below(maxSize)
411 427
412 const test = await testVideoImage(url, attributes.fixture, videoDetails.thumbnailPath) 428 const test = await testImage(url, attributes.fixture, videoDetails.thumbnailPath)
413 expect(test).to.equal(true) 429 expect(test).to.equal(true)
414 430
415 const torrent = await webtorrentAdd(magnetUri, true) 431 const torrent = await webtorrentAdd(magnetUri, true)
@@ -437,11 +453,12 @@ export {
437 searchVideo, 453 searchVideo,
438 searchVideoWithPagination, 454 searchVideoWithPagination,
439 searchVideoWithSort, 455 searchVideoWithSort,
440 testVideoImage, 456 testImage,
441 uploadVideo, 457 uploadVideo,
442 updateVideo, 458 updateVideo,
443 rateVideo, 459 rateVideo,
444 viewVideo, 460 viewVideo,
445 parseTorrentVideo, 461 parseTorrentVideo,
446 completeVideoCheck 462 completeVideoCheck,
463 checkVideoFilesWereRemoved
447} 464}
diff --git a/yarn.lock b/yarn.lock
index 74cbadeef..5fb157e08 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3796,9 +3796,9 @@ sequelize-typescript@^0.6.1:
3796 es6-shim "0.35.3" 3796 es6-shim "0.35.3"
3797 glob "7.1.2" 3797 glob "7.1.2"
3798 3798
3799sequelize@4.25.2: 3799sequelize@4.31.2:
3800 version "4.25.2" 3800 version "4.31.2"
3801 resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.25.2.tgz#fa4a95b9ec3cefb948ecb2dc5965ccf716f98c68" 3801 resolved "https://registry.yarnpkg.com/sequelize/-/sequelize-4.31.2.tgz#4b414c39bac18ae74946ed49b300f5bc7e423462"
3802 dependencies: 3802 dependencies:
3803 bluebird "^3.4.6" 3803 bluebird "^3.4.6"
3804 cls-bluebird "^2.0.1" 3804 cls-bluebird "^2.0.1"