aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models
diff options
context:
space:
mode:
Diffstat (limited to 'server/models')
-rw-r--r--server/models/account/account.ts8
-rw-r--r--server/models/actor/actor-follow.ts2
-rw-r--r--server/models/shared/index.ts2
-rw-r--r--server/models/shared/query.ts17
-rw-r--r--server/models/shared/update.ts18
-rw-r--r--server/models/video/video-channel.ts59
-rw-r--r--server/models/video/video-file.ts2
-rw-r--r--server/models/video/video-playlist.ts24
-rw-r--r--server/models/video/video-streaming-playlist.ts2
-rw-r--r--server/models/video/video.ts2
10 files changed, 107 insertions, 29 deletions
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 665ecd595..37194a119 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -52,6 +52,7 @@ export enum ScopeNames {
52export type SummaryOptions = { 52export type SummaryOptions = {
53 actorRequired?: boolean // Default: true 53 actorRequired?: boolean // Default: true
54 whereActor?: WhereOptions 54 whereActor?: WhereOptions
55 whereServer?: WhereOptions
55 withAccountBlockerIds?: number[] 56 withAccountBlockerIds?: number[]
56} 57}
57 58
@@ -65,12 +66,11 @@ export type SummaryOptions = {
65})) 66}))
66@Scopes(() => ({ 67@Scopes(() => ({
67 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => { 68 [ScopeNames.SUMMARY]: (options: SummaryOptions = {}) => {
68 const whereActor = options.whereActor || undefined
69
70 const serverInclude: IncludeOptions = { 69 const serverInclude: IncludeOptions = {
71 attributes: [ 'host' ], 70 attributes: [ 'host' ],
72 model: ServerModel.unscoped(), 71 model: ServerModel.unscoped(),
73 required: false 72 required: !!options.whereServer,
73 where: options.whereServer
74 } 74 }
75 75
76 const queryInclude: Includeable[] = [ 76 const queryInclude: Includeable[] = [
@@ -78,7 +78,7 @@ export type SummaryOptions = {
78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], 78 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
79 model: ActorModel.unscoped(), 79 model: ActorModel.unscoped(),
80 required: options.actorRequired ?? true, 80 required: options.actorRequired ?? true,
81 where: whereActor, 81 where: options.whereActor,
82 include: [ 82 include: [
83 serverInclude, 83 serverInclude,
84 84
diff --git a/server/models/actor/actor-follow.ts b/server/models/actor/actor-follow.ts
index 3080e02a6..283856d3f 100644
--- a/server/models/actor/actor-follow.ts
+++ b/server/models/actor/actor-follow.ts
@@ -19,7 +19,6 @@ import {
19 UpdatedAt 19 UpdatedAt
20} from 'sequelize-typescript' 20} from 'sequelize-typescript'
21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc' 21import { isActivityPubUrlValid } from '@server/helpers/custom-validators/activitypub/misc'
22import { doesExist } from '@server/helpers/database-utils'
23import { getServerActor } from '@server/models/application/application' 22import { getServerActor } from '@server/models/application/application'
24import { 23import {
25 MActorFollowActorsDefault, 24 MActorFollowActorsDefault,
@@ -36,6 +35,7 @@ import { logger } from '../../helpers/logger'
36import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants' 35import { ACTOR_FOLLOW_SCORE, CONSTRAINTS_FIELDS, FOLLOW_STATES, SERVER_ACTOR_NAME } from '../../initializers/constants'
37import { AccountModel } from '../account/account' 36import { AccountModel } from '../account/account'
38import { ServerModel } from '../server/server' 37import { ServerModel } from '../server/server'
38import { doesExist } from '../shared/query'
39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils' 39import { createSafeIn, getFollowsSort, getSort, searchAttribute, throwIfNotValid } from '../utils'
40import { VideoChannelModel } from '../video/video-channel' 40import { VideoChannelModel } from '../video/video-channel'
41import { ActorModel, unusedActorAttributesForAPI } from './actor' 41import { ActorModel, unusedActorAttributesForAPI } from './actor'
diff --git a/server/models/shared/index.ts b/server/models/shared/index.ts
new file mode 100644
index 000000000..5b97510e0
--- /dev/null
+++ b/server/models/shared/index.ts
@@ -0,0 +1,2 @@
1export * from './query'
2export * from './update'
diff --git a/server/models/shared/query.ts b/server/models/shared/query.ts
new file mode 100644
index 000000000..036cc13c6
--- /dev/null
+++ b/server/models/shared/query.ts
@@ -0,0 +1,17 @@
1import { BindOrReplacements, QueryTypes } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4function doesExist (query: string, bind?: BindOrReplacements) {
5 const options = {
6 type: QueryTypes.SELECT as QueryTypes.SELECT,
7 bind,
8 raw: true
9 }
10
11 return sequelizeTypescript.query(query, options)
12 .then(results => results.length === 1)
13}
14
15export {
16 doesExist
17}
diff --git a/server/models/shared/update.ts b/server/models/shared/update.ts
new file mode 100644
index 000000000..d338211e3
--- /dev/null
+++ b/server/models/shared/update.ts
@@ -0,0 +1,18 @@
1import { QueryTypes, Transaction } from 'sequelize'
2import { sequelizeTypescript } from '@server/initializers/database'
3
4// Sequelize always skip the update if we only update updatedAt field
5function setAsUpdated (table: string, id: number, transaction?: Transaction) {
6 return sequelizeTypescript.query(
7 `UPDATE "${table}" SET "updatedAt" = :updatedAt WHERE id = :id`,
8 {
9 replacements: { table, id, updatedAt: new Date() },
10 type: QueryTypes.UPDATE,
11 transaction
12 }
13 )
14}
15
16export {
17 setAsUpdated
18}
diff --git a/server/models/video/video-channel.ts b/server/models/video/video-channel.ts
index 183e7448c..9aa271711 100644
--- a/server/models/video/video-channel.ts
+++ b/server/models/video/video-channel.ts
@@ -1,4 +1,4 @@
1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction } from 'sequelize' 1import { FindOptions, Includeable, literal, Op, QueryTypes, ScopeOptions, Transaction, WhereOptions } from 'sequelize'
2import { 2import {
3 AllowNull, 3 AllowNull,
4 BeforeDestroy, 4 BeforeDestroy,
@@ -17,7 +17,6 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { MAccountActor } from '@server/types/models' 20import { MAccountActor } from '@server/types/models'
22import { AttributesOnly } from '@shared/core-utils' 21import { AttributesOnly } from '@shared/core-utils'
23import { ActivityPubActor } from '../../../shared/models/activitypub' 22import { ActivityPubActor } from '../../../shared/models/activitypub'
@@ -41,6 +40,7 @@ import { ActorModel, unusedActorAttributesForAPI } from '../actor/actor'
41import { ActorFollowModel } from '../actor/actor-follow' 40import { ActorFollowModel } from '../actor/actor-follow'
42import { ActorImageModel } from '../actor/actor-image' 41import { ActorImageModel } from '../actor/actor-image'
43import { ServerModel } from '../server/server' 42import { ServerModel } from '../server/server'
43import { setAsUpdated } from '../shared'
44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils' 44import { buildServerIdsFollowedBy, buildTrigramSearchIndex, createSimilarityAttribute, getSort, throwIfNotValid } from '../utils'
45import { VideoModel } from './video' 45import { VideoModel } from './video'
46import { VideoPlaylistModel } from './video-playlist' 46import { VideoPlaylistModel } from './video-playlist'
@@ -58,6 +58,7 @@ export enum ScopeNames {
58type AvailableForListOptions = { 58type AvailableForListOptions = {
59 actorId: number 59 actorId: number
60 search?: string 60 search?: string
61 host?: string
61} 62}
62 63
63type AvailableWithStatsOptions = { 64type AvailableWithStatsOptions = {
@@ -83,6 +84,33 @@ export type SummaryOptions = {
83 // Only list local channels OR channels that are on an instance followed by actorId 84 // Only list local channels OR channels that are on an instance followed by actorId
84 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId) 85 const inQueryInstanceFollow = buildServerIdsFollowedBy(options.actorId)
85 86
87 const whereActor = {
88 [Op.or]: [
89 {
90 serverId: null
91 },
92 {
93 serverId: {
94 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
95 }
96 }
97 ]
98 }
99
100 let serverRequired = false
101 let whereServer: WhereOptions
102
103 if (options.host && options.host !== WEBSERVER.HOST) {
104 serverRequired = true
105 whereServer = { host: options.host }
106 }
107
108 if (options.host === WEBSERVER.HOST) {
109 Object.assign(whereActor, {
110 [Op.and]: [ { serverId: null } ]
111 })
112 }
113
86 return { 114 return {
87 include: [ 115 include: [
88 { 116 {
@@ -90,20 +118,19 @@ export type SummaryOptions = {
90 exclude: unusedActorAttributesForAPI 118 exclude: unusedActorAttributesForAPI
91 }, 119 },
92 model: ActorModel, 120 model: ActorModel,
93 where: { 121 where: whereActor,
94 [Op.or]: [
95 {
96 serverId: null
97 },
98 {
99 serverId: {
100 [Op.in]: Sequelize.literal(inQueryInstanceFollow)
101 }
102 }
103 ]
104 },
105 include: [ 122 include: [
106 { 123 {
124 model: ServerModel,
125 required: serverRequired,
126 where: whereServer
127 },
128 {
129 model: ActorImageModel,
130 as: 'Avatar',
131 required: false
132 },
133 {
107 model: ActorImageModel, 134 model: ActorImageModel,
108 as: 'Banner', 135 as: 'Banner',
109 required: false 136 required: false
@@ -431,6 +458,8 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
431 start: number 458 start: number
432 count: number 459 count: number
433 sort: string 460 sort: string
461
462 host?: string
434 }) { 463 }) {
435 const attributesInclude = [] 464 const attributesInclude = []
436 const escapedSearch = VideoChannelModel.sequelize.escape(options.search) 465 const escapedSearch = VideoChannelModel.sequelize.escape(options.search)
@@ -458,7 +487,7 @@ ON "Account->Actor"."serverId" = "Account->Actor->Server"."id"`
458 487
459 return VideoChannelModel 488 return VideoChannelModel
460 .scope({ 489 .scope({
461 method: [ ScopeNames.FOR_API, { actorId: options.actorId } as AvailableForListOptions ] 490 method: [ ScopeNames.FOR_API, { actorId: options.actorId, host: options.host } as AvailableForListOptions ]
462 }) 491 })
463 .findAndCountAll(query) 492 .findAndCountAll(query)
464 .then(({ rows, count }) => { 493 .then(({ rows, count }) => {
diff --git a/server/models/video/video-file.ts b/server/models/video/video-file.ts
index 797a85a4e..09fc5288b 100644
--- a/server/models/video/video-file.ts
+++ b/server/models/video/video-file.ts
@@ -21,7 +21,6 @@ import {
21import { Where } from 'sequelize/types/lib/utils' 21import { Where } from 'sequelize/types/lib/utils'
22import validator from 'validator' 22import validator from 'validator'
23import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub' 23import { buildRemoteVideoBaseUrl } from '@server/helpers/activitypub'
24import { doesExist } from '@server/helpers/database-utils'
25import { logger } from '@server/helpers/logger' 24import { logger } from '@server/helpers/logger'
26import { extractVideo } from '@server/helpers/video' 25import { extractVideo } from '@server/helpers/video'
27import { getTorrentFilePath } from '@server/lib/video-paths' 26import { getTorrentFilePath } from '@server/lib/video-paths'
@@ -45,6 +44,7 @@ import {
45} from '../../initializers/constants' 44} from '../../initializers/constants'
46import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file' 45import { MVideoFile, MVideoFileStreamingPlaylistVideo, MVideoFileVideo } from '../../types/models/video/video-file'
47import { VideoRedundancyModel } from '../redundancy/video-redundancy' 46import { VideoRedundancyModel } from '../redundancy/video-redundancy'
47import { doesExist } from '../shared'
48import { parseAggregateResult, throwIfNotValid } from '../utils' 48import { parseAggregateResult, throwIfNotValid } from '../utils'
49import { VideoModel } from './video' 49import { VideoModel } from './video'
50import { VideoStreamingPlaylistModel } from './video-streaming-playlist' 50import { VideoStreamingPlaylistModel } from './video-streaming-playlist'
diff --git a/server/models/video/video-playlist.ts b/server/models/video/video-playlist.ts
index 72ba474b4..a2dc7075d 100644
--- a/server/models/video/video-playlist.ts
+++ b/server/models/video/video-playlist.ts
@@ -17,7 +17,6 @@ import {
17 Table, 17 Table,
18 UpdatedAt 18 UpdatedAt
19} from 'sequelize-typescript' 19} from 'sequelize-typescript'
20import { setAsUpdated } from '@server/helpers/database-utils'
21import { buildUUID, uuidToShort } from '@server/helpers/uuid' 20import { buildUUID, uuidToShort } from '@server/helpers/uuid'
22import { MAccountId, MChannelId } from '@server/types/models' 21import { MAccountId, MChannelId } from '@server/types/models'
23import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath } from '@shared/core-utils' 22import { AttributesOnly, buildPlaylistEmbedPath, buildPlaylistWatchPath } from '@shared/core-utils'
@@ -53,6 +52,7 @@ import {
53} from '../../types/models/video/video-playlist' 52} from '../../types/models/video/video-playlist'
54import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account' 53import { AccountModel, ScopeNames as AccountScopeNames, SummaryOptions } from '../account/account'
55import { ActorModel } from '../actor/actor' 54import { ActorModel } from '../actor/actor'
55import { setAsUpdated } from '../shared'
56import { 56import {
57 buildServerIdsFollowedBy, 57 buildServerIdsFollowedBy,
58 buildTrigramSearchIndex, 58 buildTrigramSearchIndex,
@@ -82,6 +82,7 @@ type AvailableForListOptions = {
82 videoChannelId?: number 82 videoChannelId?: number
83 listMyPlaylists?: boolean 83 listMyPlaylists?: boolean
84 search?: string 84 search?: string
85 host?: string
85 withVideos?: boolean 86 withVideos?: boolean
86} 87}
87 88
@@ -141,9 +142,19 @@ function getVideoLengthSelect () {
141 ] 142 ]
142 }, 143 },
143 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => { 144 [ScopeNames.AVAILABLE_FOR_LIST]: (options: AvailableForListOptions) => {
145 const whereAnd: WhereOptions[] = []
146
147 const whereServer = options.host && options.host !== WEBSERVER.HOST
148 ? { host: options.host }
149 : undefined
150
144 let whereActor: WhereOptions = {} 151 let whereActor: WhereOptions = {}
145 152
146 const whereAnd: WhereOptions[] = [] 153 if (options.host === WEBSERVER.HOST) {
154 whereActor = {
155 [Op.and]: [ { serverId: null } ]
156 }
157 }
147 158
148 if (options.listMyPlaylists !== true) { 159 if (options.listMyPlaylists !== true) {
149 whereAnd.push({ 160 whereAnd.push({
@@ -168,9 +179,7 @@ function getVideoLengthSelect () {
168 }) 179 })
169 } 180 }
170 181
171 whereActor = { 182 Object.assign(whereActor, { [Op.or]: whereActorOr })
172 [Op.or]: whereActorOr
173 }
174 } 183 }
175 184
176 if (options.accountId) { 185 if (options.accountId) {
@@ -228,7 +237,7 @@ function getVideoLengthSelect () {
228 include: [ 237 include: [
229 { 238 {
230 model: AccountModel.scope({ 239 model: AccountModel.scope({
231 method: [ AccountScopeNames.SUMMARY, { whereActor } as SummaryOptions ] 240 method: [ AccountScopeNames.SUMMARY, { whereActor, whereServer } as SummaryOptions ]
232 }), 241 }),
233 required: true 242 required: true
234 }, 243 },
@@ -349,6 +358,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
349 videoChannelId?: number 358 videoChannelId?: number
350 listMyPlaylists?: boolean 359 listMyPlaylists?: boolean
351 search?: string 360 search?: string
361 host?: string
352 withVideos?: boolean // false by default 362 withVideos?: boolean // false by default
353 }) { 363 }) {
354 const query = { 364 const query = {
@@ -368,6 +378,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
368 videoChannelId: options.videoChannelId, 378 videoChannelId: options.videoChannelId,
369 listMyPlaylists: options.listMyPlaylists, 379 listMyPlaylists: options.listMyPlaylists,
370 search: options.search, 380 search: options.search,
381 host: options.host,
371 withVideos: options.withVideos || false 382 withVideos: options.withVideos || false
372 } as AvailableForListOptions 383 } as AvailableForListOptions
373 ] 384 ]
@@ -390,6 +401,7 @@ export class VideoPlaylistModel extends Model<Partial<AttributesOnly<VideoPlayli
390 count: number 401 count: number
391 sort: string 402 sort: string
392 search?: string 403 search?: string
404 host?: string
393 }) { 405 }) {
394 return VideoPlaylistModel.listForApi({ 406 return VideoPlaylistModel.listForApi({
395 ...options, 407 ...options,
diff --git a/server/models/video/video-streaming-playlist.ts b/server/models/video/video-streaming-playlist.ts
index b15d20cf9..d591a3134 100644
--- a/server/models/video/video-streaming-playlist.ts
+++ b/server/models/video/video-streaming-playlist.ts
@@ -2,7 +2,6 @@ import * as memoizee from 'memoizee'
2import { join } from 'path' 2import { join } from 'path'
3import { Op } from 'sequelize' 3import { Op } from 'sequelize'
4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 4import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, HasMany, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
5import { doesExist } from '@server/helpers/database-utils'
6import { VideoFileModel } from '@server/models/video/video-file' 5import { VideoFileModel } from '@server/models/video/video-file'
7import { MStreamingPlaylist, MVideo } from '@server/types/models' 6import { MStreamingPlaylist, MVideo } from '@server/types/models'
8import { AttributesOnly } from '@shared/core-utils' 7import { AttributesOnly } from '@shared/core-utils'
@@ -20,6 +19,7 @@ import {
20 WEBSERVER 19 WEBSERVER
21} from '../../initializers/constants' 20} from '../../initializers/constants'
22import { VideoRedundancyModel } from '../redundancy/video-redundancy' 21import { VideoRedundancyModel } from '../redundancy/video-redundancy'
22import { doesExist } from '../shared'
23import { throwIfNotValid } from '../utils' 23import { throwIfNotValid } from '../utils'
24import { VideoModel } from './video' 24import { VideoModel } from './video'
25 25
diff --git a/server/models/video/video.ts b/server/models/video/video.ts
index c006a91af..c444f381e 100644
--- a/server/models/video/video.ts
+++ b/server/models/video/video.ts
@@ -24,7 +24,6 @@ import {
24 Table, 24 Table,
25 UpdatedAt 25 UpdatedAt
26} from 'sequelize-typescript' 26} from 'sequelize-typescript'
27import { setAsUpdated } from '@server/helpers/database-utils'
28import { buildNSFWFilter } from '@server/helpers/express-utils' 27import { buildNSFWFilter } from '@server/helpers/express-utils'
29import { uuidToShort } from '@server/helpers/uuid' 28import { uuidToShort } from '@server/helpers/uuid'
30import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video' 29import { getPrivaciesForFederation, isPrivacyForFederation, isStateForFederation } from '@server/helpers/video'
@@ -92,6 +91,7 @@ import { VideoRedundancyModel } from '../redundancy/video-redundancy'
92import { ServerModel } from '../server/server' 91import { ServerModel } from '../server/server'
93import { TrackerModel } from '../server/tracker' 92import { TrackerModel } from '../server/tracker'
94import { VideoTrackerModel } from '../server/video-tracker' 93import { VideoTrackerModel } from '../server/video-tracker'
94import { setAsUpdated } from '../shared'
95import { UserModel } from '../user/user' 95import { UserModel } from '../user/user'
96import { UserVideoHistoryModel } from '../user/user-video-history' 96import { UserVideoHistoryModel } from '../user/user-video-history'
97import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils' 97import { buildTrigramSearchIndex, buildWhereIdOrUUID, getVideoSort, isOutdated, throwIfNotValid } from '../utils'