aboutsummaryrefslogtreecommitdiffhomepage
path: root/server/models/account
diff options
context:
space:
mode:
Diffstat (limited to 'server/models/account')
-rw-r--r--server/models/account/account-blocklist.ts9
-rw-r--r--server/models/account/account-video-rate.ts53
-rw-r--r--server/models/account/account.ts58
-rw-r--r--server/models/account/user-notification-setting.ts2
-rw-r--r--server/models/account/user-notification.ts2
-rw-r--r--server/models/account/user-video-history.ts2
-rw-r--r--server/models/account/user.ts35
7 files changed, 83 insertions, 78 deletions
diff --git a/server/models/account/account-blocklist.ts b/server/models/account/account-blocklist.ts
index 577b7dc19..fe9168ab8 100644
--- a/server/models/account/account-blocklist.ts
+++ b/server/models/account/account-blocklist.ts
@@ -1,4 +1,3 @@
1import * as Bluebird from 'bluebird'
2import { Op } from 'sequelize' 1import { Op } from 'sequelize'
3import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript' 2import { BelongsTo, Column, CreatedAt, ForeignKey, Model, Scopes, Table, UpdatedAt } from 'sequelize-typescript'
4import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models' 3import { MAccountBlocklist, MAccountBlocklistAccounts, MAccountBlocklistFormattable } from '@server/types/models'
@@ -41,7 +40,7 @@ enum ScopeNames {
41 } 40 }
42 ] 41 ]
43}) 42})
44export class AccountBlocklistModel extends Model<AccountBlocklistModel> { 43export class AccountBlocklistModel extends Model {
45 44
46 @CreatedAt 45 @CreatedAt
47 createdAt: Date 46 createdAt: Date
@@ -102,7 +101,7 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
102 }) 101 })
103 } 102 }
104 103
105 static loadByAccountAndTarget (accountId: number, targetAccountId: number): Bluebird<MAccountBlocklist> { 104 static loadByAccountAndTarget (accountId: number, targetAccountId: number): Promise<MAccountBlocklist> {
106 const query = { 105 const query = {
107 where: { 106 where: {
108 accountId, 107 accountId,
@@ -151,9 +150,9 @@ export class AccountBlocklistModel extends Model<AccountBlocklistModel> {
151 }) 150 })
152 } 151 }
153 152
154 static listHandlesBlockedBy (accountIds: number[]): Bluebird<string[]> { 153 static listHandlesBlockedBy (accountIds: number[]): Promise<string[]> {
155 const query = { 154 const query = {
156 attributes: [], 155 attributes: [ 'id' ],
157 where: { 156 where: {
158 accountId: { 157 accountId: {
159 [Op.in]: accountIds 158 [Op.in]: accountIds
diff --git a/server/models/account/account-video-rate.ts b/server/models/account/account-video-rate.ts
index 6955f45ee..d9c529491 100644
--- a/server/models/account/account-video-rate.ts
+++ b/server/models/account/account-video-rate.ts
@@ -1,22 +1,21 @@
1import { values } from 'lodash' 1import { values } from 'lodash'
2import { FindOptions, Op, Transaction } from 'sequelize' 2import { FindOptions, Op, QueryTypes, Transaction } from 'sequelize'
3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript' 3import { AllowNull, BelongsTo, Column, CreatedAt, DataType, ForeignKey, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
4import { VideoRateType } from '../../../shared/models/videos'
5import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
6import { VideoModel } from '../video/video'
7import { AccountModel } from './account'
8import { ActorModel } from '../activitypub/actor'
9import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
10import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
11import { AccountVideoRate } from '../../../shared'
12import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
13import * as Bluebird from 'bluebird'
14import { 4import {
15 MAccountVideoRate, 5 MAccountVideoRate,
16 MAccountVideoRateAccountUrl, 6 MAccountVideoRateAccountUrl,
17 MAccountVideoRateAccountVideo, 7 MAccountVideoRateAccountVideo,
18 MAccountVideoRateFormattable 8 MAccountVideoRateFormattable
19} from '@server/types/models/video/video-rate' 9} from '@server/types/models/video/video-rate'
10import { AccountVideoRate } from '../../../shared'
11import { VideoRateType } from '../../../shared/models/videos'
12import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
13import { CONSTRAINTS_FIELDS, VIDEO_RATE_TYPES } from '../../initializers/constants'
14import { ActorModel } from '../activitypub/actor'
15import { buildLocalAccountIdsIn, getSort, throwIfNotValid } from '../utils'
16import { VideoModel } from '../video/video'
17import { ScopeNames as VideoChannelScopeNames, SummaryOptions, VideoChannelModel } from '../video/video-channel'
18import { AccountModel } from './account'
20 19
21/* 20/*
22 Account rates per video. 21 Account rates per video.
@@ -43,7 +42,7 @@ import {
43 } 42 }
44 ] 43 ]
45}) 44})
46export class AccountVideoRateModel extends Model<AccountVideoRateModel> { 45export class AccountVideoRateModel extends Model {
47 46
48 @AllowNull(false) 47 @AllowNull(false)
49 @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES))) 48 @Column(DataType.ENUM(...values(VIDEO_RATE_TYPES)))
@@ -84,7 +83,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
84 }) 83 })
85 Account: AccountModel 84 Account: AccountModel
86 85
87 static load (accountId: number, videoId: number, transaction?: Transaction): Bluebird<MAccountVideoRate> { 86 static load (accountId: number, videoId: number, transaction?: Transaction): Promise<MAccountVideoRate> {
88 const options: FindOptions = { 87 const options: FindOptions = {
89 where: { 88 where: {
90 accountId, 89 accountId,
@@ -96,7 +95,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
96 return AccountVideoRateModel.findOne(options) 95 return AccountVideoRateModel.findOne(options)
97 } 96 }
98 97
99 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Bluebird<MAccountVideoRate> { 98 static loadByAccountAndVideoOrUrl (accountId: number, videoId: number, url: string, t?: Transaction): Promise<MAccountVideoRate> {
100 const options: FindOptions = { 99 const options: FindOptions = {
101 where: { 100 where: {
102 [Op.or]: [ 101 [Op.or]: [
@@ -152,7 +151,7 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
152 accountName: string, 151 accountName: string,
153 videoId: number | string, 152 videoId: number | string,
154 t?: Transaction 153 t?: Transaction
155 ): Bluebird<MAccountVideoRateAccountVideo> { 154 ): Promise<MAccountVideoRateAccountVideo> {
156 const options: FindOptions = { 155 const options: FindOptions = {
157 where: { 156 where: {
158 videoId, 157 videoId,
@@ -240,17 +239,23 @@ export class AccountVideoRateModel extends Model<AccountVideoRateModel> {
240 transaction: t 239 transaction: t
241 } 240 }
242 241
243 const deleted = await AccountVideoRateModel.destroy(query) 242 await AccountVideoRateModel.destroy(query)
244 243
245 const options = { 244 const field = type === 'like'
246 transaction: t, 245 ? 'likes'
247 where: { 246 : 'dislikes'
248 id: videoId 247
249 } 248 const rawQuery = `UPDATE "video" SET "${field}" = ` +
250 } 249 '(' +
250 'SELECT COUNT(id) FROM "accountVideoRate" WHERE "accountVideoRate"."videoId" = "video"."id" AND type = :rateType' +
251 ') ' +
252 'WHERE "video"."id" = :videoId'
251 253
252 if (type === 'like') await VideoModel.increment({ likes: -deleted }, options) 254 return AccountVideoRateModel.sequelize.query(rawQuery, {
253 else if (type === 'dislike') await VideoModel.increment({ dislikes: -deleted }, options) 255 transaction: t,
256 replacements: { videoId, rateType: type },
257 type: QueryTypes.UPDATE
258 })
254 }) 259 })
255 } 260 }
256 261
diff --git a/server/models/account/account.ts b/server/models/account/account.ts
index 8c244d432..c72f9c63d 100644
--- a/server/models/account/account.ts
+++ b/server/models/account/account.ts
@@ -1,5 +1,4 @@
1import * as Bluebird from 'bluebird' 1import { FindOptions, Includeable, IncludeOptions, Op, Transaction, WhereOptions } from 'sequelize'
2import { FindOptions, IncludeOptions, Op, Transaction, WhereOptions } from 'sequelize'
3import { 2import {
4 AllowNull, 3 AllowNull,
5 BeforeDestroy, 4 BeforeDestroy,
@@ -73,28 +72,29 @@ export type SummaryOptions = {
73 required: false 72 required: false
74 } 73 }
75 74
76 const query: FindOptions = { 75 const queryInclude: Includeable[] = [
77 attributes: [ 'id', 'name', 'actorId' ], 76 {
78 include: [ 77 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ],
79 { 78 model: ActorModel.unscoped(),
80 attributes: [ 'id', 'preferredUsername', 'url', 'serverId', 'avatarId' ], 79 required: options.actorRequired ?? true,
81 model: ActorModel.unscoped(), 80 where: whereActor,
82 required: options.actorRequired ?? true, 81 include: [
83 where: whereActor, 82 serverInclude,
84 include: [
85 serverInclude,
86 83
87 { 84 {
88 model: AvatarModel.unscoped(), 85 model: AvatarModel.unscoped(),
89 required: false 86 required: false
90 } 87 }
91 ] 88 ]
92 } 89 }
93 ] 90 ]
91
92 const query: FindOptions = {
93 attributes: [ 'id', 'name', 'actorId' ]
94 } 94 }
95 95
96 if (options.withAccountBlockerIds) { 96 if (options.withAccountBlockerIds) {
97 query.include.push({ 97 queryInclude.push({
98 attributes: [ 'id' ], 98 attributes: [ 'id' ],
99 model: AccountBlocklistModel.unscoped(), 99 model: AccountBlocklistModel.unscoped(),
100 as: 'BlockedAccounts', 100 as: 'BlockedAccounts',
@@ -120,6 +120,8 @@ export type SummaryOptions = {
120 ] 120 ]
121 } 121 }
122 122
123 query.include = queryInclude
124
123 return query 125 return query
124 } 126 }
125})) 127}))
@@ -138,7 +140,7 @@ export type SummaryOptions = {
138 } 140 }
139 ] 141 ]
140}) 142})
141export class AccountModel extends Model<AccountModel> { 143export class AccountModel extends Model {
142 144
143 @AllowNull(false) 145 @AllowNull(false)
144 @Column 146 @Column
@@ -244,11 +246,11 @@ export class AccountModel extends Model<AccountModel> {
244 return undefined 246 return undefined
245 } 247 }
246 248
247 static load (id: number, transaction?: Transaction): Bluebird<MAccountDefault> { 249 static load (id: number, transaction?: Transaction): Promise<MAccountDefault> {
248 return AccountModel.findByPk(id, { transaction }) 250 return AccountModel.findByPk(id, { transaction })
249 } 251 }
250 252
251 static loadByNameWithHost (nameWithHost: string): Bluebird<MAccountDefault> { 253 static loadByNameWithHost (nameWithHost: string): Promise<MAccountDefault> {
252 const [ accountName, host ] = nameWithHost.split('@') 254 const [ accountName, host ] = nameWithHost.split('@')
253 255
254 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName) 256 if (!host || host === WEBSERVER.HOST) return AccountModel.loadLocalByName(accountName)
@@ -256,7 +258,7 @@ export class AccountModel extends Model<AccountModel> {
256 return AccountModel.loadByNameAndHost(accountName, host) 258 return AccountModel.loadByNameAndHost(accountName, host)
257 } 259 }
258 260
259 static loadLocalByName (name: string): Bluebird<MAccountDefault> { 261 static loadLocalByName (name: string): Promise<MAccountDefault> {
260 const fun = () => { 262 const fun = () => {
261 const query = { 263 const query = {
262 where: { 264 where: {
@@ -296,7 +298,7 @@ export class AccountModel extends Model<AccountModel> {
296 }) 298 })
297 } 299 }
298 300
299 static loadByNameAndHost (name: string, host: string): Bluebird<MAccountDefault> { 301 static loadByNameAndHost (name: string, host: string): Promise<MAccountDefault> {
300 const query = { 302 const query = {
301 include: [ 303 include: [
302 { 304 {
@@ -321,7 +323,7 @@ export class AccountModel extends Model<AccountModel> {
321 return AccountModel.findOne(query) 323 return AccountModel.findOne(query)
322 } 324 }
323 325
324 static loadByUrl (url: string, transaction?: Transaction): Bluebird<MAccountDefault> { 326 static loadByUrl (url: string, transaction?: Transaction): Promise<MAccountDefault> {
325 const query = { 327 const query = {
326 include: [ 328 include: [
327 { 329 {
@@ -354,7 +356,7 @@ export class AccountModel extends Model<AccountModel> {
354 }) 356 })
355 } 357 }
356 358
357 static loadAccountIdFromVideo (videoId: number): Bluebird<MAccount> { 359 static loadAccountIdFromVideo (videoId: number): Promise<MAccount> {
358 const query = { 360 const query = {
359 include: [ 361 include: [
360 { 362 {
@@ -377,7 +379,7 @@ export class AccountModel extends Model<AccountModel> {
377 return AccountModel.findOne(query) 379 return AccountModel.findOne(query)
378 } 380 }
379 381
380 static listLocalsForSitemap (sort: string): Bluebird<MAccountActor[]> { 382 static listLocalsForSitemap (sort: string): Promise<MAccountActor[]> {
381 const query = { 383 const query = {
382 attributes: [ ], 384 attributes: [ ],
383 offset: 0, 385 offset: 0,
diff --git a/server/models/account/user-notification-setting.ts b/server/models/account/user-notification-setting.ts
index acc192d53..ebab8b6d2 100644
--- a/server/models/account/user-notification-setting.ts
+++ b/server/models/account/user-notification-setting.ts
@@ -28,7 +28,7 @@ import { UserModel } from './user'
28 } 28 }
29 ] 29 ]
30}) 30})
31export class UserNotificationSettingModel extends Model<UserNotificationSettingModel> { 31export class UserNotificationSettingModel extends Model {
32 32
33 @AllowNull(false) 33 @AllowNull(false)
34 @Default(null) 34 @Default(null)
diff --git a/server/models/account/user-notification.ts b/server/models/account/user-notification.ts
index 452574dc8..52b792a5b 100644
--- a/server/models/account/user-notification.ts
+++ b/server/models/account/user-notification.ts
@@ -254,7 +254,7 @@ function buildAccountInclude (required: boolean, withActor = false) {
254 } 254 }
255 ] as (ModelIndexesOptions & { where?: WhereOptions })[] 255 ] as (ModelIndexesOptions & { where?: WhereOptions })[]
256}) 256})
257export class UserNotificationModel extends Model<UserNotificationModel> { 257export class UserNotificationModel extends Model {
258 258
259 @AllowNull(false) 259 @AllowNull(false)
260 @Default(null) 260 @Default(null)
diff --git a/server/models/account/user-video-history.ts b/server/models/account/user-video-history.ts
index 76b469fbf..45171fc60 100644
--- a/server/models/account/user-video-history.ts
+++ b/server/models/account/user-video-history.ts
@@ -19,7 +19,7 @@ import { MUserAccountId, MUserId } from '@server/types/models'
19 } 19 }
20 ] 20 ]
21}) 21})
22export class UserVideoHistoryModel extends Model<UserVideoHistoryModel> { 22export class UserVideoHistoryModel extends Model {
23 @CreatedAt 23 @CreatedAt
24 createdAt: Date 24 createdAt: Date
25 25
diff --git a/server/models/account/user.ts b/server/models/account/user.ts
index 10117099b..8e437c3be 100644
--- a/server/models/account/user.ts
+++ b/server/models/account/user.ts
@@ -1,4 +1,3 @@
1import * as Bluebird from 'bluebird'
2import { values } from 'lodash' 1import { values } from 'lodash'
3import { col, FindOptions, fn, literal, Op, QueryTypes, where, WhereOptions } from 'sequelize' 2import { col, FindOptions, fn, literal, Op, QueryTypes, where, WhereOptions } from 'sequelize'
4import { 3import {
@@ -16,11 +15,11 @@ import {
16 HasOne, 15 HasOne,
17 Is, 16 Is,
18 IsEmail, 17 IsEmail,
18 IsUUID,
19 Model, 19 Model,
20 Scopes, 20 Scopes,
21 Table, 21 Table,
22 UpdatedAt, 22 UpdatedAt
23 IsUUID
24} from 'sequelize-typescript' 23} from 'sequelize-typescript'
25import { 24import {
26 MMyUserFormattable, 25 MMyUserFormattable,
@@ -220,7 +219,7 @@ enum ScopeNames {
220 } 219 }
221 ] 220 ]
222}) 221})
223export class UserModel extends Model<UserModel> { 222export class UserModel extends Model {
224 223
225 @AllowNull(true) 224 @AllowNull(true)
226 @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true)) 225 @Is('UserPassword', value => throwIfNotValid(value, isUserPasswordValid, 'user password', true))
@@ -483,7 +482,7 @@ export class UserModel extends Model<UserModel> {
483 }) 482 })
484 } 483 }
485 484
486 static listWithRight (right: UserRight): Bluebird<MUserDefault[]> { 485 static listWithRight (right: UserRight): Promise<MUserDefault[]> {
487 const roles = Object.keys(USER_ROLE_LABELS) 486 const roles = Object.keys(USER_ROLE_LABELS)
488 .map(k => parseInt(k, 10) as UserRole) 487 .map(k => parseInt(k, 10) as UserRole)
489 .filter(role => hasUserRight(role, right)) 488 .filter(role => hasUserRight(role, right))
@@ -499,7 +498,7 @@ export class UserModel extends Model<UserModel> {
499 return UserModel.findAll(query) 498 return UserModel.findAll(query)
500 } 499 }
501 500
502 static listUserSubscribersOf (actorId: number): Bluebird<MUserWithNotificationSetting[]> { 501 static listUserSubscribersOf (actorId: number): Promise<MUserWithNotificationSetting[]> {
503 const query = { 502 const query = {
504 include: [ 503 include: [
505 { 504 {
@@ -538,7 +537,7 @@ export class UserModel extends Model<UserModel> {
538 return UserModel.unscoped().findAll(query) 537 return UserModel.unscoped().findAll(query)
539 } 538 }
540 539
541 static listByUsernames (usernames: string[]): Bluebird<MUserDefault[]> { 540 static listByUsernames (usernames: string[]): Promise<MUserDefault[]> {
542 const query = { 541 const query = {
543 where: { 542 where: {
544 username: usernames 543 username: usernames
@@ -548,11 +547,11 @@ export class UserModel extends Model<UserModel> {
548 return UserModel.findAll(query) 547 return UserModel.findAll(query)
549 } 548 }
550 549
551 static loadById (id: number): Bluebird<MUser> { 550 static loadById (id: number): Promise<MUser> {
552 return UserModel.unscoped().findByPk(id) 551 return UserModel.unscoped().findByPk(id)
553 } 552 }
554 553
555 static loadByIdWithChannels (id: number, withStats = false): Bluebird<MUserDefault> { 554 static loadByIdWithChannels (id: number, withStats = false): Promise<MUserDefault> {
556 const scopes = [ 555 const scopes = [
557 ScopeNames.WITH_VIDEOCHANNELS 556 ScopeNames.WITH_VIDEOCHANNELS
558 ] 557 ]
@@ -562,7 +561,7 @@ export class UserModel extends Model<UserModel> {
562 return UserModel.scope(scopes).findByPk(id) 561 return UserModel.scope(scopes).findByPk(id)
563 } 562 }
564 563
565 static loadByUsername (username: string): Bluebird<MUserDefault> { 564 static loadByUsername (username: string): Promise<MUserDefault> {
566 const query = { 565 const query = {
567 where: { 566 where: {
568 username: { [Op.iLike]: username } 567 username: { [Op.iLike]: username }
@@ -572,7 +571,7 @@ export class UserModel extends Model<UserModel> {
572 return UserModel.findOne(query) 571 return UserModel.findOne(query)
573 } 572 }
574 573
575 static loadForMeAPI (username: string): Bluebird<MUserNotifSettingChannelDefault> { 574 static loadForMeAPI (username: string): Promise<MUserNotifSettingChannelDefault> {
576 const query = { 575 const query = {
577 where: { 576 where: {
578 username: { [Op.iLike]: username } 577 username: { [Op.iLike]: username }
@@ -582,7 +581,7 @@ export class UserModel extends Model<UserModel> {
582 return UserModel.scope(ScopeNames.FOR_ME_API).findOne(query) 581 return UserModel.scope(ScopeNames.FOR_ME_API).findOne(query)
583 } 582 }
584 583
585 static loadByEmail (email: string): Bluebird<MUserDefault> { 584 static loadByEmail (email: string): Promise<MUserDefault> {
586 const query = { 585 const query = {
587 where: { 586 where: {
588 email 587 email
@@ -592,7 +591,7 @@ export class UserModel extends Model<UserModel> {
592 return UserModel.findOne(query) 591 return UserModel.findOne(query)
593 } 592 }
594 593
595 static loadByUsernameOrEmail (username: string, email?: string): Bluebird<MUserDefault> { 594 static loadByUsernameOrEmail (username: string, email?: string): Promise<MUserDefault> {
596 if (!email) email = username 595 if (!email) email = username
597 596
598 const query = { 597 const query = {
@@ -608,7 +607,7 @@ export class UserModel extends Model<UserModel> {
608 return UserModel.findOne(query) 607 return UserModel.findOne(query)
609 } 608 }
610 609
611 static loadByVideoId (videoId: number): Bluebird<MUserDefault> { 610 static loadByVideoId (videoId: number): Promise<MUserDefault> {
612 const query = { 611 const query = {
613 include: [ 612 include: [
614 { 613 {
@@ -639,7 +638,7 @@ export class UserModel extends Model<UserModel> {
639 return UserModel.findOne(query) 638 return UserModel.findOne(query)
640 } 639 }
641 640
642 static loadByVideoImportId (videoImportId: number): Bluebird<MUserDefault> { 641 static loadByVideoImportId (videoImportId: number): Promise<MUserDefault> {
643 const query = { 642 const query = {
644 include: [ 643 include: [
645 { 644 {
@@ -656,7 +655,7 @@ export class UserModel extends Model<UserModel> {
656 return UserModel.findOne(query) 655 return UserModel.findOne(query)
657 } 656 }
658 657
659 static loadByChannelActorId (videoChannelActorId: number): Bluebird<MUserDefault> { 658 static loadByChannelActorId (videoChannelActorId: number): Promise<MUserDefault> {
660 const query = { 659 const query = {
661 include: [ 660 include: [
662 { 661 {
@@ -680,7 +679,7 @@ export class UserModel extends Model<UserModel> {
680 return UserModel.findOne(query) 679 return UserModel.findOne(query)
681 } 680 }
682 681
683 static loadByAccountActorId (accountActorId: number): Bluebird<MUserDefault> { 682 static loadByAccountActorId (accountActorId: number): Promise<MUserDefault> {
684 const query = { 683 const query = {
685 include: [ 684 include: [
686 { 685 {
@@ -697,7 +696,7 @@ export class UserModel extends Model<UserModel> {
697 return UserModel.findOne(query) 696 return UserModel.findOne(query)
698 } 697 }
699 698
700 static loadByLiveId (liveId: number): Bluebird<MUser> { 699 static loadByLiveId (liveId: number): Promise<MUser> {
701 const query = { 700 const query = {
702 include: [ 701 include: [
703 { 702 {