diff options
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 |
22 | fi | 22 | fi |
23 | 23 | ||
24 | branch=$(git symbolic-ref --short -q HEAD) | ||
25 | if [ "$branch" != "develop" ]; then | ||
26 | echo "Need to be on develop branch." | ||
27 | exit -1 | ||
28 | fi | ||
29 | |||
24 | version="v$1" | 30 | version="v$1" |
25 | directory_name="peertube-$version" | 31 | directory_name="peertube-$version" |
26 | zip_name="peertube-$version.zip" | 32 | zip_name="peertube-$version.zip" |
@@ -56,3 +62,10 @@ git push origin --tag | |||
56 | 62 | ||
57 | github-release release --user chocobozzz --repo peertube --tag "$version" --name "$version" | 63 | github-release release --user chocobozzz --repo peertube --tag "$version" --name "$version" |
58 | github-release upload --user chocobozzz --repo peertube --tag "$version" --name "$zip_name" --file "$zip_name" | 64 | github-release upload --user chocobozzz --repo peertube --tag "$version" --name "$zip_name" --file "$zip_name" |
65 | |||
66 | # Update master branch | ||
67 | git checkout master | ||
68 | git rebase develop | ||
69 | git git push origin master | ||
70 | git 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 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { getFormattedObjects } from '../../helpers/utils' | 2 | import { getFormattedObjects } from '../../helpers/utils' |
3 | import { asyncMiddleware, paginationValidator, setDefaultSort, setPagination } from '../../middlewares' | 3 | import { asyncMiddleware, paginationValidator, setDefaultSort, setDefaultPagination } from '../../middlewares' |
4 | import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators' | 4 | import { accountsGetValidator, accountsSortValidator } from '../../middlewares/validators' |
5 | import { AccountModel } from '../../models/account/account' | 5 | import { 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 @@ | |||
1 | import * as express from 'express' | 1 | import * as express from 'express' |
2 | import { UserRight } from '../../../shared/models/users' | 2 | import { UserRight } from '../../../shared/models/users' |
3 | import { getFormattedObjects } from '../../helpers/utils' | 3 | import { getFormattedObjects } from '../../helpers/utils' |
4 | import { asyncMiddleware, authenticate, ensureUserHasRight, jobsSortValidator, setDefaultSort, setPagination } from '../../middlewares' | 4 | import { |
5 | asyncMiddleware, authenticate, ensureUserHasRight, jobsSortValidator, setDefaultPagination, | ||
6 | setDefaultSort | ||
7 | } from '../../middlewares' | ||
5 | import { paginationValidator } from '../../middlewares/validators' | 8 | import { paginationValidator } from '../../middlewares/validators' |
6 | import { JobModel } from '../../models/job/job' | 9 | import { 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 | |||
10 | import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send' | 10 | import { sendFollow, sendUndoFollow } from '../../../lib/activitypub/send' |
11 | import { | 11 | import { |
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' |
15 | import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' | 15 | import { followersSortValidator, followingSortValidator, followValidator } from '../../../middlewares/validators' |
16 | import { ActorModel } from '../../../models/activitypub/actor' | 16 | import { 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' | |||
13 | import { createUserAccountAndChannel } from '../../lib/user' | 13 | import { createUserAccountAndChannel } from '../../lib/user' |
14 | import { | 14 | import { |
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' |
19 | import { usersUpdateMyAvatarValidator, videosSortValidator } from '../../middlewares/validators' | 19 | import { 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 | ||
138 | async function createUser (req: express.Request) { | 142 | async 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 | ||
155 | async function registerUserRetryWrapper (req: express.Request, res: express.Response, next: express.NextFunction) { | 161 | async 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' | |||
6 | import { sequelizeTypescript } from '../../../initializers' | 6 | import { sequelizeTypescript } from '../../../initializers' |
7 | import { sendVideoAbuse } from '../../../lib/activitypub/send' | 7 | import { sendVideoAbuse } from '../../../lib/activitypub/send' |
8 | import { | 8 | import { |
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' |
12 | import { AccountModel } from '../../../models/account/account' | 12 | import { 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 | ) |
27 | abuseVideoRouter.post('/:id/abuse', | 27 | abuseVideoRouter.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' | |||
3 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
4 | import { getFormattedObjects } from '../../../helpers/utils' | 4 | import { getFormattedObjects } from '../../../helpers/utils' |
5 | import { | 5 | import { |
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' |
9 | import { VideoBlacklistModel } from '../../../models/video/video-blacklist' | 9 | import { 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' | |||
7 | import { setAsyncActorKeys } from '../../../lib/activitypub' | 7 | import { setAsyncActorKeys } from '../../../lib/activitypub' |
8 | import { createVideoChannel } from '../../../lib/video-channel' | 8 | import { createVideoChannel } from '../../../lib/video-channel' |
9 | import { | 9 | import { |
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' | |||
6 | import { getFormattedObjects } from '../../../helpers/utils' | 6 | import { getFormattedObjects } from '../../../helpers/utils' |
7 | import { sequelizeTypescript } from '../../../initializers' | 7 | import { sequelizeTypescript } from '../../../initializers' |
8 | import { buildFormattedCommentTree, createVideoComment } from '../../../lib/video-comment' | 8 | import { buildFormattedCommentTree, createVideoComment } from '../../../lib/video-comment' |
9 | import { asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setPagination } from '../../../middlewares' | 9 | import { asyncMiddleware, authenticate, paginationValidator, setDefaultSort, setDefaultPagination } from '../../../middlewares' |
10 | import { videoCommentThreadsSortValidator } from '../../../middlewares/validators' | 10 | import { videoCommentThreadsSortValidator } from '../../../middlewares/validators' |
11 | import { | 11 | import { |
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 | |||
14 | import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' | 14 | import { sendCreateVideo, sendCreateViewToOrigin, sendCreateViewToVideoFollowers, sendUpdateVideo } from '../../../lib/activitypub/send' |
15 | import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler' | 15 | import { transcodingJobScheduler } from '../../../lib/jobs/transcoding-job-scheduler' |
16 | import { | 16 | import { |
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' |
20 | import { TagModel } from '../../../models/video/tag' | 20 | import { 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 | ) |
51 | videosRouter.get('/search', | 51 | videosRouter.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 | ) |
59 | videosRouter.put('/:id', | 59 | videosRouter.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' | |||
10 | import { getOrCreateActorAndServerAndModel } from '../actor' | 10 | import { getOrCreateActorAndServerAndModel } from '../actor' |
11 | 11 | ||
12 | async function processDeleteActivity (activity: ActivityDelete) { | 12 | async 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 | ||
29 | async function sendDeleteVideoComment (videoComment: VideoCommentModel, t: Transaction) { | 32 | async 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' | |||
20 | import { getOrCreateActorAndServerAndModel } from './actor' | 20 | import { getOrCreateActorAndServerAndModel } from './actor' |
21 | 21 | ||
22 | function fetchRemoteVideoPreview (video: VideoModel, reject: Function) { | 22 | function 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 | ||
32 | async function fetchRemoteVideoDescription (video: VideoModel) { | 32 | async 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' | |||
6 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' | 6 | import { buildActorInstance, getAccountActivityPubUrl, setAsyncActorKeys } from './activitypub' |
7 | import { createVideoChannel } from './video-channel' | 7 | import { createVideoChannel } from './video-channel' |
8 | 8 | ||
9 | async function createUserAccountAndChannel (user: UserModel, validateUser = true) { | 9 | async 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 | ||
34 | async function createLocalAccountWithoutKeys ( | 34 | async 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 | ||
4 | import { PAGINATION_COUNT_DEFAULT } from '../initializers' | 4 | import { PAGINATION_COUNT_DEFAULT } from '../initializers' |
5 | 5 | ||
6 | function setPagination (req: express.Request, res: express.Response, next: express.NextFunction) { | 6 | function 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 | ||
18 | export { | 18 | export { |
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 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { | 2 | import { |
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' |
6 | import { Account } from '../../../shared/models/actors' | 6 | import { Account } from '../../../shared/models/actors' |
7 | import { logger } from '../../helpers/logger' | ||
7 | import { sendDeleteActor } from '../../lib/activitypub/send' | 8 | import { sendDeleteActor } from '../../lib/activitypub/send' |
8 | import { ActorModel } from '../activitypub/actor' | 9 | import { ActorModel } from '../activitypub/actor' |
9 | import { ApplicationModel } from '../application/application' | 10 | import { ApplicationModel } from '../application/application' |
@@ -11,6 +12,7 @@ import { AvatarModel } from '../avatar/avatar' | |||
11 | import { ServerModel } from '../server/server' | 12 | import { ServerModel } from '../server/server' |
12 | import { getSort } from '../utils' | 13 | import { getSort } from '../utils' |
13 | import { VideoChannelModel } from '../video/video-channel' | 14 | import { VideoChannelModel } from '../video/video-channel' |
15 | import { VideoCommentModel } from '../video/video-comment' | ||
14 | import { UserModel } from './user' | 16 | import { 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 @@ | |||
1 | import { AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 1 | import { AllowNull, Column, CreatedAt, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
2 | import { isHostValid } from '../../helpers/custom-validators/servers' | 2 | import { isHostValid } from '../../helpers/custom-validators/servers' |
3 | import { ActorModel } from '../activitypub/actor' | ||
3 | import { throwIfNotValid } from '../utils' | 4 | import { 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 @@ | |||
1 | import { | 1 | import { |
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' |
16 | import { ActivityPubActor } from '../../../shared/models/activitypub' | 5 | import { ActivityPubActor } from '../../../shared/models/activitypub' |
17 | import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' | 6 | import { isVideoChannelDescriptionValid, isVideoChannelNameValid } from '../../helpers/custom-validators/video-channels' |
7 | import { logger } from '../../helpers/logger' | ||
18 | import { sendDeleteActor } from '../../lib/activitypub/send' | 8 | import { sendDeleteActor } from '../../lib/activitypub/send' |
19 | import { AccountModel } from '../account/account' | 9 | import { AccountModel } from '../account/account' |
20 | import { ActorModel } from '../activitypub/actor' | 10 | import { 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 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { | 2 | import { |
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' |
6 | import { ActivityTagObject } from '../../../shared/models/activitypub/objects/common-objects' | 6 | import { 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' | |||
5 | import { join } from 'path' | 5 | import { join } from 'path' |
6 | import * as Sequelize from 'sequelize' | 6 | import * as Sequelize from 'sequelize' |
7 | import { | 7 | import { |
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' |
11 | import { IIncludeOptions } from 'sequelize-typescript/lib/interfaces/IIncludeOptions' | ||
12 | import { VideoPrivacy, VideoResolution } from '../../../shared' | 11 | import { VideoPrivacy, VideoResolution } from '../../../shared' |
13 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' | 12 | import { VideoTorrentObject } from '../../../shared/models/activitypub/objects' |
14 | import { Video, VideoDetails } from '../../../shared/models/videos' | 13 | import { Video, VideoDetails } from '../../../shared/models/videos' |
@@ -22,6 +21,7 @@ import { | |||
22 | } from '../../helpers/custom-validators/videos' | 21 | } from '../../helpers/custom-validators/videos' |
23 | import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' | 22 | import { generateImageFromVideoFile, getVideoFileHeight, transcode } from '../../helpers/ffmpeg-utils' |
24 | import { logger } from '../../helpers/logger' | 23 | import { logger } from '../../helpers/logger' |
24 | import { getServerActor } from '../../helpers/utils' | ||
25 | import { | 25 | import { |
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' | |||
31 | import { AccountModel } from '../account/account' | 31 | import { AccountModel } from '../account/account' |
32 | import { AccountVideoRateModel } from '../account/account-video-rate' | 32 | import { AccountVideoRateModel } from '../account/account-video-rate' |
33 | import { ActorModel } from '../activitypub/actor' | 33 | import { ActorModel } from '../activitypub/actor' |
34 | import { ActorFollowModel } from '../activitypub/actor-follow' | ||
34 | import { ServerModel } from '../server/server' | 35 | import { ServerModel } from '../server/server' |
35 | import { getSort, throwIfNotValid } from '../utils' | 36 | import { getSort, throwIfNotValid } from '../utils' |
36 | import { TagModel } from './tag' | 37 | import { TagModel } from './tag' |
@@ -43,7 +44,6 @@ import { VideoTagModel } from './video-tag' | |||
43 | 44 | ||
44 | enum ScopeNames { | 45 | enum 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' | |||
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' | 5 | import { Video, VideoPrivacy } from '../../../../shared/models/videos' |
6 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 6 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' |
7 | import { completeVideoCheck } from '../../utils' | 7 | import { checkVideoFilesWereRemoved, completeVideoCheck, getVideoChannelsList } from '../../utils' |
8 | 8 | ||
9 | import { | 9 | import { |
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' |
13 | import { dateIsValid } from '../../utils/miscs/miscs' | 13 | import { dateIsValid } from '../../utils/miscs/miscs' |
14 | import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' | 14 | import { follow, getFollowersListPaginationAndSort, getFollowingListPaginationAndSort, unfollow } from '../../utils/server/follows' |
15 | import { expectAccountFollows } from '../../utils/users/accounts' | 15 | import { expectAccountFollows, getAccountsList } from '../../utils/users/accounts' |
16 | import { userLogin } from '../../utils/users/login' | 16 | import { userLogin } from '../../utils/users/login' |
17 | import { createUser } from '../../utils/users/users' | 17 | import { createUser } from '../../utils/users/users' |
18 | import { | 18 | import { |
@@ -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 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { Account } from '../../../../shared/models/actors' | 5 | import { Account } from '../../../../shared/models/actors' |
6 | import { doubleFollow, flushAndRunMultipleServers, wait } from '../../utils' | 6 | import { checkVideoFilesWereRemoved, createUser, doubleFollow, flushAndRunMultipleServers, removeUser, userLogin, wait } from '../../utils' |
7 | import { | 7 | import { flushTests, getMyUserInformation, killallServers, ServerInfo, testImage, updateMyAvatar, uploadVideo } from '../../utils/index' |
8 | flushTests, getMyUserInformation, killallServers, ServerInfo, testVideoImage, updateMyAvatar, | 8 | import { checkActorFilesWereRemoved, getAccount, getAccountsList } from '../../utils/users/accounts' |
9 | uploadVideo | ||
10 | } from '../../utils/index' | ||
11 | import { getAccount, getAccountsList } from '../../utils/users/accounts' | ||
12 | import { setAccessTokensToServers } from '../../utils/users/login' | 9 | import { setAccessTokensToServers } from '../../utils/users/login' |
13 | 10 | ||
14 | const expect = chai.expect | 11 | const expect = chai.expect |
15 | 12 | ||
16 | describe('Test users with multiple servers', function () { | 13 | describe('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' |
12 | import { follow } from '../../utils/server/follows' | 12 | import { follow } from '../../utils/server/follows' |
13 | import { setAccessTokensToServers } from '../../utils/users/login' | 13 | import { 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' | |||
8 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 8 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' |
9 | 9 | ||
10 | import { | 10 | import { |
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' |
15 | import { | 16 | import { |
@@ -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 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import { keyBy } from 'lodash' | 4 | import { keyBy } from 'lodash' |
5 | import 'mocha' | 5 | import 'mocha' |
6 | import { join } from 'path' | ||
7 | import { VideoPrivacy } from '../../../../shared/models/videos' | 6 | import { VideoPrivacy } from '../../../../shared/models/videos' |
8 | import { readdirPromise } from '../../../helpers/core-utils' | ||
9 | import { | 7 | import { |
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 | ||
15 | const expect = chai.expect | 14 | const 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 @@ | |||
3 | import * as chai from 'chai' | 3 | import * as chai from 'chai' |
4 | import 'mocha' | 4 | import 'mocha' |
5 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' | 5 | import { VideoComment, VideoCommentThreadTree } from '../../../../shared/models/videos/video-comment.model' |
6 | import { testVideoImage } from '../../utils' | 6 | import { testImage } from '../../utils' |
7 | import { | 7 | import { |
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 @@ | |||
1 | import { join } from 'path' | ||
1 | import * as WebTorrent from 'webtorrent' | 2 | import * as WebTorrent from 'webtorrent' |
2 | 3 | ||
3 | let webtorrent = new WebTorrent() | 4 | let 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 | ||
28 | function root () { | ||
29 | // We are in server/tests/utils/miscs | ||
30 | return join(__dirname, '..', '..', '..', '..') | ||
31 | } | ||
32 | |||
27 | // --------------------------------------------------------------------------- | 33 | // --------------------------------------------------------------------------- |
28 | 34 | ||
29 | export { | 35 | export { |
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 | |||
1 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { existsSync } from 'fs' | ||
5 | import { join } from 'path' | ||
2 | import { Account } from '../../../../shared/models/actors' | 6 | import { Account } from '../../../../shared/models/actors' |
7 | import { readdirPromise } from '../../../helpers/core-utils' | ||
8 | import { root } from '../index' | ||
3 | import { makeGetRequest } from '../requests/requests' | 9 | import { makeGetRequest } from '../requests/requests' |
4 | 10 | ||
5 | function getAccountsList (url: string, sort = '-createdAt', statusCodeExpected = 200) { | 11 | function 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 | ||
41 | async 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 | ||
37 | export { | 59 | export { |
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 | ||
3 | import { expect } from 'chai' | 3 | import { expect } from 'chai' |
4 | import { readFile } from 'fs' | 4 | import { existsSync, readFile } from 'fs' |
5 | import * as parseTorrent from 'parse-torrent' | 5 | import * as parseTorrent from 'parse-torrent' |
6 | import { extname, isAbsolute, join } from 'path' | 6 | import { extname, isAbsolute, join } from 'path' |
7 | import * as request from 'supertest' | 7 | import * as request from 'supertest' |
8 | import { getMyUserInformation, makeGetRequest, ServerInfo } from '../' | 8 | import { getMyUserInformation, makeGetRequest, root, ServerInfo } from '../' |
9 | import { VideoPrivacy } from '../../../../shared/models/videos' | 9 | import { VideoPrivacy } from '../../../../shared/models/videos' |
10 | import { readFileBufferPromise } from '../../../helpers/core-utils' | 10 | import { readdirPromise, readFileBufferPromise } from '../../../helpers/core-utils' |
11 | import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers' | 11 | import { VIDEO_CATEGORIES, VIDEO_LANGUAGES, VIDEO_LICENCES, VIDEO_PRIVACIES } from '../../../initializers' |
12 | import { dateIsValid, webtorrentAdd } from '../index' | 12 | import { 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 | ||
206 | async function testVideoImage (url: string, imageName: string, imagePath: string, extension = '.jpg') { | 206 | async 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 | |||
222 | async 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 | } |
@@ -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 | ||
3799 | sequelize@4.25.2: | 3799 | sequelize@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" |