serversBlocklistSortValidator,
unblockServerByAccountValidator
} from '../../../middlewares/validators'
-import { AccountModel } from '../../../models/account/account'
import { AccountBlocklistModel } from '../../../models/account/account-blocklist'
import { addAccountInBlocklist, addServerInBlocklist, removeAccountFromBlocklist, removeServerFromBlocklist } from '../../../lib/blocklist'
import { ServerBlocklistModel } from '../../../models/server/server-blocklist'
-import { ServerModel } from '../../../models/server/server'
const myBlocklistRouter = express.Router()
async function blockAccount (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.User
- const accountToBlock = res.locals.account
+ const accountToBlock = res.locals.account
await addAccountInBlocklist(user.Account.id, accountToBlock.id)
import { UserWatchingVideo } from '../../../../shared'
import { asyncMiddleware, asyncRetryTransactionMiddleware, authenticate, videoWatchingValidator } from '../../../middlewares'
import { UserVideoHistoryModel } from '../../../models/account/user-video-history'
-import { UserModel } from '../../../models/account/user'
const watchingRouter = express.Router()
import * as winston from 'winston'
import { CONFIG } from '../initializers'
import { jsonLoggerFormat, labelFormatter } from './logger'
-import { VideoDetails, User, VideoChannel, VideoAbuse, VideoImport } from '../../shared'
+import { User, VideoAbuse, VideoChannel, VideoDetails, VideoImport } from '../../shared'
import { VideoComment } from '../../shared/models/videos/video-comment.model'
import { CustomConfig } from '../../shared/models/server/custom-config.model'
-import { UserModel } from '../models/account/user'
function getAuditIdFromRes (res: express.Response) {
- return (res.locals.oauth.token.User as UserModel).username
+ return res.locals.oauth.token.User.username
}
enum AUDIT_TYPE {
import * as Bluebird from 'bluebird'
import { ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
-async function crawlCollectionPage <T> (uri: string, handler: (items: T[]) => (Promise<any> | Bluebird<any>)) {
+type HandlerFunction<T> = (items: T[]) => (Promise<any> | Bluebird<any>)
+type CleanerFunction = (startedDate: Date) => (Promise<any> | Bluebird<any>)
+
+async function crawlCollectionPage <T> (uri: string, handler: HandlerFunction<T>, cleaner?: CleanerFunction) {
logger.info('Crawling ActivityPub data on %s.', uri)
const options = {
timeout: JOB_REQUEST_TIMEOUT
}
+ const startDate = new Date()
+
const response = await doRequest<ActivityPubOrderedCollection<T>>(options)
const firstBody = response.body
await handler(items)
}
}
+
+ if (cleaner) await cleaner(startDate)
}
export {
url: shareUrl
}
- await VideoShareModel.findOrCreate({
- where: {
- url: shareUrl
- },
- defaults: entry
- })
+ await VideoShareModel.upsert(entry)
} catch (err) {
logger.warn('Cannot add share %s.', shareUrl, { err })
}
accountId: actor.Account.id,
inReplyToCommentId,
originCommentId,
- createdAt: new Date(comment.published),
- updatedAt: new Date(comment.updated)
+ createdAt: new Date(comment.published)
}
}
const entry = await videoCommentActivityObjectToDBAttributes(videoInstance, actor, body)
if (!entry) return { created: false }
- const [ comment, created ] = await VideoCommentModel.findOrCreate({
- where: {
- url: body.id
- },
- defaults: entry
- })
+ const [ comment, created ] = await VideoCommentModel.upsert<VideoCommentModel>(entry, { returning: true })
comment.Account = actor.Account
comment.Video = videoInstance
const actor = await getOrCreateActorAndServerAndModel(actorUrl)
- const [ , created ] = await AccountVideoRateModel
- .findOrCreate({
- where: {
- videoId: video.id,
- accountId: actor.Account.id
- },
- defaults: {
- videoId: video.id,
- accountId: actor.Account.id,
- type: rate,
- url: body.id
- }
- })
+ const entry = {
+ videoId: video.id,
+ accountId: actor.Account.id,
+ type: rate,
+ url: body.id
+ }
+
+ const created = await AccountVideoRateModel.upsert(entry)
if (created) rateCounts += 1
} catch (err) {
import { VideoStreamingPlaylistModel } from '../../models/video/video-streaming-playlist'
import { VideoStreamingPlaylistType } from '../../../shared/models/videos/video-streaming-playlist.type'
import { FilteredModelAttributes } from 'sequelize-typescript/lib/models/Model'
+import { AccountVideoRateModel } from '../../models/account/account-video-rate'
+import { VideoShareModel } from '../../models/video/video-share'
+import { VideoCommentModel } from '../../models/video/video-comment'
async function federateVideoIfNeeded (video: VideoModel, isNewVideo: boolean, transaction?: sequelize.Transaction) {
// If the video is not private and published, we federate it
const jobPayloads: ActivitypubHttpFetcherPayload[] = []
if (syncParam.likes === true) {
- await crawlCollectionPage<string>(fetchedVideo.likes, items => createRates(items, video, 'like'))
+ const handler = items => createRates(items, video, 'like')
+ const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate)
+
+ await crawlCollectionPage<string>(fetchedVideo.likes, handler, cleaner)
.catch(err => logger.error('Cannot add likes of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.likes, videoId: video.id, type: 'video-likes' as 'video-likes' })
}
if (syncParam.dislikes === true) {
- await crawlCollectionPage<string>(fetchedVideo.dislikes, items => createRates(items, video, 'dislike'))
+ const handler = items => createRates(items, video, 'dislike')
+ const cleaner = crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate)
+
+ await crawlCollectionPage<string>(fetchedVideo.dislikes, handler, cleaner)
.catch(err => logger.error('Cannot add dislikes of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.dislikes, videoId: video.id, type: 'video-dislikes' as 'video-dislikes' })
}
if (syncParam.shares === true) {
- await crawlCollectionPage<string>(fetchedVideo.shares, items => addVideoShares(items, video))
+ const handler = items => addVideoShares(items, video)
+ const cleaner = crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate)
+
+ await crawlCollectionPage<string>(fetchedVideo.shares, handler, cleaner)
.catch(err => logger.error('Cannot add shares of video %s.', video.uuid, { err }))
} else {
jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' })
}
if (syncParam.comments === true) {
- await crawlCollectionPage<string>(fetchedVideo.comments, items => addVideoComments(items, video))
+ const handler = items => addVideoComments(items, video)
+ const cleaner = crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate)
+
+ await crawlCollectionPage<string>(fetchedVideo.comments, handler, cleaner)
.catch(err => logger.error('Cannot add comments of video %s.', video.uuid, { err }))
} else {
- jobPayloads.push({ uri: fetchedVideo.shares, videoId: video.id, type: 'video-shares' as 'video-shares' })
+ jobPayloads.push({ uri: fetchedVideo.comments, videoId: video.id, type: 'video-comments' as 'video-comments' })
}
await Bluebird.map(jobPayloads, payload => JobQueue.Instance.createJob({ type: 'activitypub-http-fetcher', payload }))
import * as Bull from 'bull'
+import * as Bluebird from 'bluebird'
import { logger } from '../../../helpers/logger'
import { processActivities } from '../../activitypub/process'
import { addVideoComments } from '../../activitypub/video-comments'
import { addVideoShares, createRates } from '../../activitypub'
import { createAccountPlaylists } from '../../activitypub/playlist'
import { AccountModel } from '../../../models/account/account'
+import { AccountVideoRateModel } from '../../../models/account/account-video-rate'
+import { VideoShareModel } from '../../../models/video/video-share'
+import { VideoCommentModel } from '../../../models/video/video-comment'
type FetchType = 'activity' | 'video-likes' | 'video-dislikes' | 'video-shares' | 'video-comments' | 'account-playlists'
'account-playlists': items => createAccountPlaylists(items, account)
}
- return crawlCollectionPage(payload.uri, fetcherType[payload.type])
+ const cleanerType: { [ id in FetchType ]?: (crawlStartDate: Date) => Bluebird<any> } = {
+ 'video-likes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'like' as 'like', crawlStartDate),
+ 'video-dislikes': crawlStartDate => AccountVideoRateModel.cleanOldRatesOf(video.id, 'dislike' as 'dislike', crawlStartDate),
+ 'video-shares': crawlStartDate => VideoShareModel.cleanOldSharesOf(video.id, crawlStartDate),
+ 'video-comments': crawlStartDate => VideoCommentModel.cleanOldCommentsOf(video.id, crawlStartDate)
+ }
+
+ return crawlCollectionPage(payload.uri, fetcherType[payload.type], cleanerType[payload.type])
}
// ---------------------------------------------------------------------------
.end()
}
- const user= res.locals.oauth.token.User
+ const user = res.locals.oauth.token.User
if (await user.isPasswordMatch(req.body.currentPassword) !== true) {
return res.status(401)
.send({ error: 'currentPassword is invalid.' })
import { values } from 'lodash'
-import { Transaction } from 'sequelize'
+import { Transaction, Op } from 'sequelize'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { IFindOptions } from 'sequelize-typescript/lib/interfaces/IFindOptions'
import { VideoRateType } from '../../../shared/models/videos'
return AccountVideoRateModel.findAndCountAll(query)
}
+
+ static cleanOldRatesOf (videoId: number, type: VideoRateType, beforeUpdatedAt: Date) {
+ return AccountVideoRateModel.sequelize.transaction(async t => {
+ const query = {
+ where: {
+ updatedAt: {
+ [Op.lt]: beforeUpdatedAt
+ },
+ videoId,
+ type
+ },
+ transaction: t
+ }
+
+ const deleted = await AccountVideoRateModel.destroy(query)
+
+ const options = {
+ transaction: t,
+ where: {
+ id: videoId
+ }
+ }
+
+ if (type === 'like') await VideoModel.increment({ likes: -deleted }, options)
+ else if (type === 'dislike') await VideoModel.increment({ dislikes: -deleted }, options)
+ })
+ }
}
import * as Sequelize from 'sequelize'
+import { Op } from 'sequelize'
import {
AllowNull,
BeforeDestroy,
}
}
+ static cleanOldCommentsOf (videoId: number, beforeUpdatedAt: Date) {
+ const query = {
+ where: {
+ updatedAt: {
+ [Op.lt]: beforeUpdatedAt
+ },
+ videoId
+ }
+ }
+
+ return VideoCommentModel.destroy(query)
+ }
+
getCommentStaticPath () {
return this.Video.getWatchStaticPath() + ';threadId=' + this.getThreadId()
}
import * as Sequelize from 'sequelize'
+import { Op } from 'sequelize'
import * as Bluebird from 'bluebird'
import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
return VideoShareModel.findAndCountAll(query)
}
+
+ static cleanOldSharesOf (videoId: number, beforeUpdatedAt: Date) {
+ const query = {
+ where: {
+ updatedAt: {
+ [Op.lt]: beforeUpdatedAt
+ },
+ videoId
+ }
+ }
+
+ return VideoShareModel.destroy(query)
+ }
}
attributes: query.attributes,
order: [ // Keep original order
Sequelize.literal(
- ids.map(id => `"VideoModel".id = ${id}`).join(', ')
+ ids.map(id => `"VideoModel".id = ${id} DESC`).join(', ')
)
]
}
generateUserAccessToken,
getVideo,
getVideoPlaylist,
- killallServers,
+ killallServers, rateVideo,
reRunServer,
ServerInfo,
setAccessTokensToServers,
this.timeout(20000)
await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like')
- await wait(200)
+ await wait(500)
await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'dislike')
- await wait(200)
+ await wait(500)
await rateVideo(servers[0].url, servers[0].accessToken, remoteVideosServer1[0], 'like')
await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'like')
- await wait(200)
+ await wait(500)
await rateVideo(servers[2].url, servers[2].accessToken, localVideosServer3[1], 'dislike')
await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[1], 'dislike')
- await wait(200)
+ await wait(500)
await rateVideo(servers[2].url, servers[2].accessToken, remoteVideosServer3[0], 'like')
await waitJobs(servers)