diff options
author | Chocobozzz <me@florianbigard.com> | 2019-03-19 16:23:02 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2019-03-19 16:23:02 +0100 |
commit | 2ba92871319d7af63472c1380664a9f9eeb1c690 (patch) | |
tree | d593b2dfea29a8171b9f6afaaef076321f5edf71 /server | |
parent | d74d29ad9e35929491cf37223398d2535ab23de0 (diff) | |
download | PeerTube-2ba92871319d7af63472c1380664a9f9eeb1c690.tar.gz PeerTube-2ba92871319d7af63472c1380664a9f9eeb1c690.tar.zst PeerTube-2ba92871319d7af63472c1380664a9f9eeb1c690.zip |
Cleanup invalid rates/comments/shares
Diffstat (limited to 'server')
-rw-r--r-- | server/controllers/api/users/my-blocklist.ts | 4 | ||||
-rw-r--r-- | server/controllers/api/videos/watching.ts | 1 | ||||
-rw-r--r-- | server/helpers/audit-logger.ts | 5 | ||||
-rw-r--r-- | server/lib/activitypub/crawl.ts | 9 | ||||
-rw-r--r-- | server/lib/activitypub/share.ts | 7 | ||||
-rw-r--r-- | server/lib/activitypub/video-comments.ts | 10 | ||||
-rw-r--r-- | server/lib/activitypub/video-rates.ts | 21 | ||||
-rw-r--r-- | server/lib/activitypub/videos.ts | 25 | ||||
-rw-r--r-- | server/lib/job-queue/handlers/activitypub-http-fetcher.ts | 13 | ||||
-rw-r--r-- | server/middlewares/validators/users.ts | 2 | ||||
-rw-r--r-- | server/models/account/account-video-rate.ts | 29 | ||||
-rw-r--r-- | server/models/video/video-comment.ts | 14 | ||||
-rw-r--r-- | server/models/video/video-share.ts | 14 | ||||
-rw-r--r-- | server/models/video/video.ts | 2 | ||||
-rw-r--r-- | server/tests/api/activitypub/refresher.ts | 2 | ||||
-rw-r--r-- | server/tests/api/videos/multiple-servers.ts | 8 |
16 files changed, 117 insertions, 49 deletions
diff --git a/server/controllers/api/users/my-blocklist.ts b/server/controllers/api/users/my-blocklist.ts index 481e75139..713c16022 100644 --- a/server/controllers/api/users/my-blocklist.ts +++ b/server/controllers/api/users/my-blocklist.ts | |||
@@ -17,11 +17,9 @@ import { | |||
17 | serversBlocklistSortValidator, | 17 | serversBlocklistSortValidator, |
18 | unblockServerByAccountValidator | 18 | unblockServerByAccountValidator |
19 | } from '../../../middlewares/validators' | 19 | } from '../../../middlewares/validators' |
20 | import { AccountModel } from '../../../models/account/account' | ||
21 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' | 20 | import { AccountBlocklistModel } from '../../../models/account/account-blocklist' |
22 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' | 21 | import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist' |
23 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' | 22 | import { ServerBlocklistModel } from '../../../models/server/server-blocklist' |
24 | import { ServerModel } from '../../../models/server/server' | ||
25 | 23 | ||
26 | const myBlocklistRouter = express.Router() | 24 | const myBlocklistRouter = express.Router() |
27 | 25 | ||
@@ -83,7 +81,7 @@ async function listBlockedAccounts (req: express.Request, res: express.Response) | |||
83 | 81 | ||
84 | async function blockAccount (req: express.Request, res: express.Response) { | 82 | async function blockAccount (req: express.Request, res: express.Response) { |
85 | const user = res.locals.oauth.token.User | 83 | const user = res.locals.oauth.token.User |
86 | const accountToBlock = res.locals.account | 84 | const accountToBlock = res.locals.account |
87 | 85 | ||
88 | await addAccountInBlocklist(user.Account.id, accountToBlock.id) | 86 | await addAccountInBlocklist(user.Account.id, accountToBlock.id) |
89 | 87 | ||
diff --git a/server/controllers/api/videos/watching.ts b/server/controllers/api/videos/watching.ts index 6bc60e045..dcd1f070d 100644 --- a/server/controllers/api/videos/watching.ts +++ b/server/controllers/api/videos/watching.ts | |||
@@ -2,7 +2,6 @@ import * as express from 'express' | |||
2 | import { UserWatchingVideo } from '../../../../shared' | 2 | import { UserWatchingVideo } from '../../../../shared' |
3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' | 3 | import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares' |
4 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' | 4 | import { UserVideoHistoryModel } from '../../../models/account/user-video-history' |
5 | import { UserModel } from '../../../models/account/user' | ||
6 | 5 | ||
7 | const watchingRouter = express.Router() | 6 | const watchingRouter = express.Router() |
8 | 7 | ||
diff --git a/server/helpers/audit-logger.ts b/server/helpers/audit-logger.ts index a121f0b8a..af37bce16 100644 --- a/server/helpers/audit-logger.ts +++ b/server/helpers/audit-logger.ts | |||
@@ -6,13 +6,12 @@ import * as flatten from 'flat' | |||
6 | import * as winston from 'winston' | 6 | import * as winston from 'winston' |
7 | import { CONFIG } from '../initializers' | 7 | import { CONFIG } from '../initializers' |
8 | import { jsonLoggerFormat, labelFormatter } from './logger' | 8 | import { jsonLoggerFormat, labelFormatter } from './logger' |
9 | import { VideoDetails, User, VideoChannel, VideoAbuse, VideoImport } from '../../shared' | 9 | import { User, VideoAbuse, VideoChannel, VideoDetails, VideoImport } from '../../shared' |
10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' | 10 | import { VideoComment } from '../../shared/models/videos/video-comment.model' |
11 | import { CustomConfig } from '../../shared/models/server/custom-config.model' | 11 | import { CustomConfig } from '../../shared/models/server/custom-config.model' |
12 | import { UserModel } from '../models/account/user' | ||
13 | 12 | ||
14 | function getAuditIdFromRes (res: express.Response) { | 13 | function getAuditIdFromRes (res: express.Response) { |
15 | return (res.locals.oauth.token.User as UserModel).username | 14 | return res.locals.oauth.token.User.username |
16 | } | 15 | } |
17 | 16 | ||
18 | enum AUDIT_TYPE { | 17 | enum AUDIT_TYPE { |
diff --git a/server/lib/activitypub/crawl.ts b/server/lib/activitypub/crawl.ts index 2675524c6..9f4ca98ba 100644 --- a/server/lib/activitypub/crawl.ts +++ b/server/lib/activitypub/crawl.ts | |||
@@ -4,7 +4,10 @@ import { logger } from '../../helpers/logger' | |||
4 | import * as Bluebird from 'bluebird' | 4 | import * as Bluebird from 'bluebird' |
5 | import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' | 5 | import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub' |
6 | 6 | ||
7 | async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => (Promise<any> | Bluebird<any>)) { | 7 | type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>) |
8 | type CleanerFunction = (startedDate: Date) => (Promise<any> | Bluebird<any>) | ||
9 | |||
10 | async function crawlCollectionPage <T> (uri: string, handler: HandlerFunction<T>, cleaner?: CleanerFunction) { | ||
8 | logger.info('Crawling ActivityPub data on %s.', uri) | 11 | logger.info('Crawling ActivityPub data on %s.', uri) |
9 | 12 | ||
10 | const options = { | 13 | const options = { |
@@ -15,6 +18,8 @@ async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => (P | |||
15 | timeout: JOB_REQUEST_TIMEOUT | 18 | timeout: JOB_REQUEST_TIMEOUT |
16 | } | 19 | } |
17 | 20 | ||
21 | const startDate = new Date() | ||
22 | |||
18 | const response = await doRequest<ActivityPubOrderedCollection<T>>(options) | 23 | const response = await doRequest<ActivityPubOrderedCollection<T>>(options) |
19 | const firstBody = response.body | 24 | const firstBody = response.body |
20 | 25 | ||
@@ -35,6 +40,8 @@ async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => (P | |||
35 | await handler(items) | 40 | await handler(items) |
36 | } | 41 | } |
37 | } | 42 | } |
43 | |||
44 | if (cleaner) await cleaner(startDate) | ||
38 | } | 45 | } |
39 | 46 | ||
40 | export { | 47 | export { |
diff --git a/server/lib/activitypub/share.ts b/server/lib/activitypub/share.ts index 1767df0ae..3bece0ff7 100644 --- a/server/lib/activitypub/share.ts +++ b/server/lib/activitypub/share.ts | |||
@@ -54,12 +54,7 @@ async function addVideoShares (shareUrls: string[], instance: VideoModel) { | |||
54 | url: shareUrl | 54 | url: shareUrl |
55 | } | 55 | } |
56 | 56 | ||
57 | await VideoShareModel.findOrCreate({ | 57 | await VideoShareModel.upsert(entry) |
58 | where: { | ||
59 | url: shareUrl | ||
60 | }, | ||
61 | defaults: entry | ||
62 | }) | ||
63 | } catch (err) { | 58 | } catch (err) { |
64 | logger.warn('Cannot add share %s.', shareUrl, { err }) | 59 | logger.warn('Cannot add share %s.', shareUrl, { err }) |
65 | } | 60 | } |
diff --git a/server/lib/activitypub/video-comments.ts b/server/lib/activitypub/video-comments.ts index e87301fe7..3f9d8f0fc 100644 --- a/server/lib/activitypub/video-comments.ts +++ b/server/lib/activitypub/video-comments.ts | |||
@@ -34,8 +34,7 @@ async function videoCommentActivityObjectToDBAttributes (video: VideoModel, acto | |||
34 | accountId: actor.Account.id, | 34 | accountId: actor.Account.id, |
35 | inReplyToCommentId, | 35 | inReplyToCommentId, |
36 | originCommentId, | 36 | originCommentId, |
37 | createdAt: new Date(comment.published), | 37 | createdAt: new Date(comment.published) |
38 | updatedAt: new Date(comment.updated) | ||
39 | } | 38 | } |
40 | } | 39 | } |
41 | 40 | ||
@@ -74,12 +73,7 @@ async function addVideoComment (videoInstance: VideoModel, commentUrl: string) { | |||
74 | const entry = await videoCommentActivityObjectToDBAttributes(videoInstance, actor, body) | 73 | const entry = await videoCommentActivityObjectToDBAttributes(videoInstance, actor, body) |
75 | if (!entry) return { created: false } | 74 | if (!entry) return { created: false } |
76 | 75 | ||
77 | const [ comment, created ] = await VideoCommentModel.findOrCreate({ | 76 | const [ comment, created ] = await VideoCommentModel.upsert<VideoCommentModel>(entry, { returning: true }) |
78 | where: { | ||
79 | url: body.id | ||
80 | }, | ||
81 | defaults: entry | ||
82 | }) | ||
83 | comment.Account = actor.Account | 77 | comment.Account = actor.Account |
84 | comment.Video = videoInstance | 78 | comment.Video = videoInstance |
85 | 79 | ||
diff --git a/server/lib/activitypub/video-rates.ts b/server/lib/activitypub/video-rates.ts index 7aac79118..ad7d81df6 100644 --- a/server/lib/activitypub/video-rates.ts +++ b/server/lib/activitypub/video-rates.ts | |||
@@ -38,19 +38,14 @@ async function createRates (ratesUrl: string[], video: VideoModel, rate: VideoRa | |||
38 | 38 | ||
39 | const actor = await getOrCreateActorAndServerAndModel(actorUrl) | 39 | const actor = await getOrCreateActorAndServerAndModel(actorUrl) |
40 | 40 | ||
41 | const [ , created ] = await AccountVideoRateModel | 41 | const entry = { |
42 | .findOrCreate({ | 42 | videoId: video.id, |
43 | where: { | 43 | accountId: actor.Account.id, |
44 | videoId: video.id, | 44 | type: rate, |
45 | accountId: actor.Account.id | 45 | url: body.id |
46 | }, | 46 | } |
47 | defaults: { | 47 | |
48 | videoId: video.id, | 48 | const created = await AccountVideoRateModel.upsert(entry) |
49 | accountId: actor.Account.id, | ||
50 | type: rate, | ||
51 | url: body.id | ||
52 | } | ||
53 | }) | ||
54 | 49 | ||
55 | if (created) rateCounts += 1 | 50 | if (created) rateCounts += 1 |
56 | } catch (err) { | 51 | } catch (err) { |
diff --git a/server/lib/activitypub/videos.ts b/server/lib/activitypub/videos.ts index 66d0abf35..2c932371b 100644 --- a/server/lib/activitypub/videos.ts +++ b/server/lib/activitypub/videos.ts | |||
@@ -40,6 +40,9 @@ import { Notifier } from '../notifier' | |||
40 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' | 40 | import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist' |
41 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' | 41 | import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type' |
42 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' | 42 | import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model' |
43 | import { AccountVideoRateModel } from '../../models/account/account-video-rate' | ||
44 | import { VideoShareModel } from '../../models/video/video-share' | ||
45 | import { VideoCommentModel } from '../../models/video/video-comment' | ||
43 | 46 | ||
44 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { | 47 | async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) { |
45 | // If the video is not private and published, we federate it | 48 | // If the video is not private and published, we federate it |
@@ -134,31 +137,43 @@ async function syncVideoExternalAttributes (video: VideoModel, fetchedVideo: Vid | |||
134 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] | 137 | const jobPayloads: ActivitypubHttpFetcherPayload[] = [] |
135 | 138 | ||
136 | if (syncParam.likes === true) { | 139 | if (syncParam.likes === true) { |
137 | await crawlCollectionPage<string>(fetchedVideo.likes, items => createRates(items, video, 'like')) | 140 | const handler = items => createRates(items, video, 'like') |
141 | const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate) | ||
142 | |||
143 | await crawlCollectionPage<string>(fetchedVideo.likes, handler, cleaner) | ||
138 | .catch(err => logger.error('Cannot add likes of video %s.', video.uuid, { err })) | 144 | .catch(err => logger.error('Cannot add likes of video %s.', video.uuid, { err })) |
139 | } else { | 145 | } else { |
140 | jobPayloads.push({ uri: fetchedVideo.likes, videoId: video.id, type: 'video-likes' as 'video-likes' }) | 146 | jobPayloads.push({ uri: fetchedVideo.likes, videoId: video.id, type: 'video-likes' as 'video-likes' }) |
141 | } | 147 | } |
142 | 148 | ||
143 | if (syncParam.dislikes === true) { | 149 | if (syncParam.dislikes === true) { |
144 | await crawlCollectionPage<string>(fetchedVideo.dislikes, items => createRates(items, video, 'dislike')) | 150 | const handler = items => createRates(items, video, 'dislike') |
151 | const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate) | ||
152 | |||
153 | await crawlCollectionPage<string>(fetchedVideo.dislikes, handler, cleaner) | ||
145 | .catch(err => logger.error('Cannot add dislikes of video %s.', video.uuid, { err })) | 154 | .catch(err => logger.error('Cannot add dislikes of video %s.', video.uuid, { err })) |
146 | } else { | 155 | } else { |
147 | jobPayloads.push({ uri: fetchedVideo.dislikes, videoId: video.id, type: 'video-dislikes' as 'video-dislikes' }) | 156 | jobPayloads.push({ uri: fetchedVideo.dislikes, videoId: video.id, type: 'video-dislikes' as 'video-dislikes' }) |
148 | } | 157 | } |
149 | 158 | ||
150 | if (syncParam.shares === true) { | 159 | if (syncParam.shares === true) { |
151 | await crawlCollectionPage<string>(fetchedVideo.shares, items => addVideoShares(items, video)) | 160 | const handler = items => addVideoShares(items, video) |
161 | const cleaner = crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate) | ||
162 | |||
163 | await crawlCollectionPage<string>(fetchedVideo.shares, handler, cleaner) | ||
152 | .catch(err => logger.error('Cannot add shares of video %s.', video.uuid, { err })) | 164 | .catch(err => logger.error('Cannot add shares of video %s.', video.uuid, { err })) |
153 | } else { | 165 | } else { |
154 | jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' }) | 166 | jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' }) |
155 | } | 167 | } |
156 | 168 | ||
157 | if (syncParam.comments === true) { | 169 | if (syncParam.comments === true) { |
158 | await crawlCollectionPage<string>(fetchedVideo.comments, items => addVideoComments(items, video)) | 170 | const handler = items => addVideoComments(items, video) |
171 | const cleaner = crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate) | ||
172 | |||
173 | await crawlCollectionPage<string>(fetchedVideo.comments, handler, cleaner) | ||
159 | .catch(err => logger.error('Cannot add comments of video %s.', video.uuid, { err })) | 174 | .catch(err => logger.error('Cannot add comments of video %s.', video.uuid, { err })) |
160 | } else { | 175 | } else { |
161 | jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' }) | 176 | jobPayloads.push({ uri: fetchedVideo.comments, videoId: video.id, type: 'video-comments' as 'video-comments' }) |
162 | } | 177 | } |
163 | 178 | ||
164 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) | 179 | await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload })) |
diff --git a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts index 52225f64f..23d33c26f 100644 --- a/server/lib/job-queue/handlers/activitypub-http-fetcher.ts +++ b/server/lib/job-queue/handlers/activitypub-http-fetcher.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import * as Bull from 'bull' | 1 | import * as Bull from 'bull' |
2 | import * as Bluebird from 'bluebird' | ||
2 | import { logger } from '../../../helpers/logger' | 3 | import { logger } from '../../../helpers/logger' |
3 | import { processActivities } from '../../activitypub/process' | 4 | import { processActivities } from '../../activitypub/process' |
4 | import { addVideoComments } from '../../activitypub/video-comments' | 5 | import { addVideoComments } from '../../activitypub/video-comments' |
@@ -7,6 +8,9 @@ import { VideoModel } from '../../../models/video/video' | |||
7 | import { addVideoShares, createRates } from '../../activitypub' | 8 | import { addVideoShares, createRates } from '../../activitypub' |
8 | import { createAccountPlaylists } from '../../activitypub/playlist' | 9 | import { createAccountPlaylists } from '../../activitypub/playlist' |
9 | import { AccountModel } from '../../../models/account/account' | 10 | import { AccountModel } from '../../../models/account/account' |
11 | import { AccountVideoRateModel } from '../../../models/account/account-video-rate' | ||
12 | import { VideoShareModel } from '../../../models/video/video-share' | ||
13 | import { VideoCommentModel } from '../../../models/video/video-comment' | ||
10 | 14 | ||
11 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' | 15 | type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists' |
12 | 16 | ||
@@ -37,7 +41,14 @@ async function processActivityPubHttpFetcher (job: Bull.Job) { | |||
37 | 'account-playlists': items => createAccountPlaylists(items, account) | 41 | 'account-playlists': items => createAccountPlaylists(items, account) |
38 | } | 42 | } |
39 | 43 | ||
40 | return crawlCollectionPage(payload.uri, fetcherType[payload.type]) | 44 | const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Bluebird<any> } = { |
45 | 'video-likes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate), | ||
46 | 'video-dislikes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate), | ||
47 | 'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate), | ||
48 | 'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate) | ||
49 | } | ||
50 | |||
51 | return crawlCollectionPage(payload.uri, fetcherType[payload.type], cleanerType[payload.type]) | ||
41 | } | 52 | } |
42 | 53 | ||
43 | // --------------------------------------------------------------------------- | 54 | // --------------------------------------------------------------------------- |
diff --git a/server/middlewares/validators/users.ts b/server/middlewares/validators/users.ts index e8ade0f97..4be446732 100644 --- a/server/middlewares/validators/users.ts +++ b/server/middlewares/validators/users.ts | |||
@@ -160,7 +160,7 @@ const usersUpdateMeValidator = [ | |||
160 | .end() | 160 | .end() |
161 | } | 161 | } |
162 | 162 | ||
163 | const user= res.locals.oauth.token.User | 163 | const user = res.locals.oauth.token.User |
164 | if (await user.isPasswordMatch(req.body.currentPassword) !== true) { | 164 | if (await user.isPasswordMatch(req.body.currentPassword) !== true) { |
165 | return res.status(401) | 165 | return res.status(401) |
166 | .send({ error: 'currentPassword is invalid.' }) | 166 | .send({ error: 'currentPassword is invalid.' }) |
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts index 18762f0c5..e5d39582b 100644 --- a/server/models/account/account-video-rate.ts +++ b/server/models/account/account-video-rate.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { values } from 'lodash' | 1 | import { values } from 'lodash' |
2 | import { Transaction } from 'sequelize' | 2 | import { Transaction, Op } from 'sequelize' |
3 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' | 3 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions' | 4 | import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions' |
5 | import { VideoRateType } from '../../../shared/models/videos' | 5 | import { VideoRateType } from '../../../shared/models/videos' |
@@ -158,4 +158,31 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> { | |||
158 | 158 | ||
159 | return AccountVideoRateModel.findAndCountAll(query) | 159 | return AccountVideoRateModel.findAndCountAll(query) |
160 | } | 160 | } |
161 | |||
162 | static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) { | ||
163 | return AccountVideoRateModel.sequelize.transaction(async t => { | ||
164 | const query = { | ||
165 | where: { | ||
166 | updatedAt: { | ||
167 | [Op.lt]: beforeUpdatedAt | ||
168 | }, | ||
169 | videoId, | ||
170 | type | ||
171 | }, | ||
172 | transaction: t | ||
173 | } | ||
174 | |||
175 | const deleted = await AccountVideoRateModel.destroy(query) | ||
176 | |||
177 | const options = { | ||
178 | transaction: t, | ||
179 | where: { | ||
180 | id: videoId | ||
181 | } | ||
182 | } | ||
183 | |||
184 | if (type === 'like') await VideoModel.increment({ likes: -deleted }, options) | ||
185 | else if (type === 'dislike') await VideoModel.increment({ dislikes: -deleted }, options) | ||
186 | }) | ||
187 | } | ||
161 | } | 188 | } |
diff --git a/server/models/video/video-comment.ts b/server/models/video/video-comment.ts index 1163f9a0e..e733138c1 100644 --- a/server/models/video/video-comment.ts +++ b/server/models/video/video-comment.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { Op } from 'sequelize' | ||
2 | import { | 3 | import { |
3 | AllowNull, | 4 | AllowNull, |
4 | BeforeDestroy, | 5 | BeforeDestroy, |
@@ -453,6 +454,19 @@ export class VideoCommentModel extends Model<VideoCommentModel> { | |||
453 | } | 454 | } |
454 | } | 455 | } |
455 | 456 | ||
457 | static cleanOldCommentsOf (videoId: number, beforeUpdatedAt: Date) { | ||
458 | const query = { | ||
459 | where: { | ||
460 | updatedAt: { | ||
461 | [Op.lt]: beforeUpdatedAt | ||
462 | }, | ||
463 | videoId | ||
464 | } | ||
465 | } | ||
466 | |||
467 | return VideoCommentModel.destroy(query) | ||
468 | } | ||
469 | |||
456 | getCommentStaticPath () { | 470 | getCommentStaticPath () { |
457 | return this.Video.getWatchStaticPath() + ';threadId=' + this.getThreadId() | 471 | return this.Video.getWatchStaticPath() + ';threadId=' + this.getThreadId() |
458 | } | 472 | } |
diff --git a/server/models/video/video-share.ts b/server/models/video/video-share.ts index 7df0ed18d..fb52b35d9 100644 --- a/server/models/video/video-share.ts +++ b/server/models/video/video-share.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | import * as Sequelize from 'sequelize' | 1 | import * as Sequelize from 'sequelize' |
2 | import { Op } from 'sequelize' | ||
2 | import * as Bluebird from 'bluebird' | 3 | import * as Bluebird from 'bluebird' |
3 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' | 4 | import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' |
4 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 5 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
@@ -200,4 +201,17 @@ export class VideoShareModel extends Model<VideoShareModel> { | |||
200 | 201 | ||
201 | return VideoShareModel.findAndCountAll(query) | 202 | return VideoShareModel.findAndCountAll(query) |
202 | } | 203 | } |
204 | |||
205 | static cleanOldSharesOf (videoId: number, beforeUpdatedAt: Date) { | ||
206 | const query = { | ||
207 | where: { | ||
208 | updatedAt: { | ||
209 | [Op.lt]: beforeUpdatedAt | ||
210 | }, | ||
211 | videoId | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return VideoShareModel.destroy(query) | ||
216 | } | ||
203 | } | 217 | } |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index fb037c21a..b0d92b674 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -1547,7 +1547,7 @@ export class VideoModel extends Model<VideoModel> { | |||
1547 | attributes: query.attributes, | 1547 | attributes: query.attributes, |
1548 | order: [ // Keep original order | 1548 | order: [ // Keep original order |
1549 | Sequelize.literal( | 1549 | Sequelize.literal( |
1550 | ids.map(id => `"VideoModel".id = ${id}`).join(', ') | 1550 | ids.map(id => `"VideoModel".id = ${id} DESC`).join(', ') |
1551 | ) | 1551 | ) |
1552 | ] | 1552 | ] |
1553 | } | 1553 | } |
diff --git a/server/tests/api/activitypub/refresher.ts b/server/tests/api/activitypub/refresher.ts index ae4859076..665a9f9f0 100644 --- a/server/tests/api/activitypub/refresher.ts +++ b/server/tests/api/activitypub/refresher.ts | |||
@@ -8,7 +8,7 @@ import { | |||
8 | generateUserAccessToken, | 8 | generateUserAccessToken, |
9 | getVideo, | 9 | getVideo, |
10 | getVideoPlaylist, | 10 | getVideoPlaylist, |
11 | killallServers, | 11 | killallServers, rateVideo, |
12 | reRunServer, | 12 | reRunServer, |
13 | ServerInfo, | 13 | ServerInfo, |
14 | setAccessTokensToServers, | 14 | setAccessTokensToServers, |
diff --git a/server/tests/api/videos/multiple-servers.ts b/server/tests/api/videos/multiple-servers.ts index 7e2fcb630..f91678140 100644 --- a/server/tests/api/videos/multiple-servers.ts +++ b/server/tests/api/videos/multiple-servers.ts | |||
@@ -579,15 +579,15 @@ describe('Test multiple servers', function () { | |||
579 | this.timeout(20000) | 579 | this.timeout(20000) |
580 | 580 | ||
581 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') | 581 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') |
582 | await wait(200) | 582 | await wait(500) |
583 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike') | 583 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike') |
584 | await wait(200) | 584 | await wait(500) |
585 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') | 585 | await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like') |
586 | await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like') | 586 | await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like') |
587 | await wait(200) | 587 | await wait(500) |
588 | await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike') | 588 | await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike') |
589 | await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike') | 589 | await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike') |
590 | await wait(200) | 590 | await wait(500) |
591 | await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like') | 591 | await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like') |
592 | 592 | ||
593 | await waitJobs(servers) | 593 | await waitJobs(servers) |