diff options
Diffstat (limited to 'server/models')
-rw-r--r-- | server/models/account/account-interface.ts | 3 | ||||
-rw-r--r-- | server/models/account/account.ts | 26 | ||||
-rw-r--r-- | server/models/account/user.ts | 5 | ||||
-rw-r--r-- | server/models/avatar/avatar-interface.ts | 16 | ||||
-rw-r--r-- | server/models/avatar/avatar.ts | 24 | ||||
-rw-r--r-- | server/models/avatar/index.ts | 1 | ||||
-rw-r--r-- | server/models/index.ts | 1 | ||||
-rw-r--r-- | server/models/video/video-interface.ts | 1 | ||||
-rw-r--r-- | server/models/video/video.ts | 92 |
9 files changed, 118 insertions, 51 deletions
diff --git a/server/models/account/account-interface.ts b/server/models/account/account-interface.ts index b369766dc..46fe068e3 100644 --- a/server/models/account/account-interface.ts +++ b/server/models/account/account-interface.ts | |||
@@ -1,6 +1,7 @@ | |||
1 | import * as Bluebird from 'bluebird' | 1 | import * as Bluebird from 'bluebird' |
2 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' | 3 | import { Account as FormattedAccount, ActivityPubActor } from '../../../shared' |
4 | import { AvatarInstance } from '../avatar' | ||
4 | import { ServerInstance } from '../server/server-interface' | 5 | import { ServerInstance } from '../server/server-interface' |
5 | import { VideoChannelInstance } from '../video/video-channel-interface' | 6 | import { VideoChannelInstance } from '../video/video-channel-interface' |
6 | 7 | ||
@@ -51,6 +52,7 @@ export interface AccountAttributes { | |||
51 | serverId?: number | 52 | serverId?: number |
52 | userId?: number | 53 | userId?: number |
53 | applicationId?: number | 54 | applicationId?: number |
55 | avatarId?: number | ||
54 | } | 56 | } |
55 | 57 | ||
56 | export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> { | 58 | export interface AccountInstance extends AccountClass, AccountAttributes, Sequelize.Instance<AccountAttributes> { |
@@ -68,6 +70,7 @@ export interface AccountInstance extends AccountClass, AccountAttributes, Sequel | |||
68 | 70 | ||
69 | Server: ServerInstance | 71 | Server: ServerInstance |
70 | VideoChannels: VideoChannelInstance[] | 72 | VideoChannels: VideoChannelInstance[] |
73 | Avatar: AvatarInstance | ||
71 | } | 74 | } |
72 | 75 | ||
73 | export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {} | 76 | export interface AccountModel extends AccountClass, Sequelize.Model<AccountInstance, AccountAttributes> {} |
diff --git a/server/models/account/account.ts b/server/models/account/account.ts index 61a88524c..8b0819f39 100644 --- a/server/models/account/account.ts +++ b/server/models/account/account.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | import { join } from 'path' | ||
1 | import * as Sequelize from 'sequelize' | 2 | import * as Sequelize from 'sequelize' |
3 | import { Avatar } from '../../../shared/models/avatars/avatar.model' | ||
2 | import { | 4 | import { |
3 | activityPubContextify, | 5 | activityPubContextify, |
4 | isAccountFollowersCountValid, | 6 | isAccountFollowersCountValid, |
@@ -8,6 +10,7 @@ import { | |||
8 | isUserUsernameValid | 10 | isUserUsernameValid |
9 | } from '../../helpers' | 11 | } from '../../helpers' |
10 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' | 12 | import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc' |
13 | import { AVATARS_DIR } from '../../initializers' | ||
11 | import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' | 14 | import { CONFIG, CONSTRAINTS_FIELDS } from '../../initializers/constants' |
12 | import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' | 15 | import { sendDeleteAccount } from '../../lib/activitypub/send/send-delete' |
13 | import { addMethodsToModel } from '../utils' | 16 | import { addMethodsToModel } from '../utils' |
@@ -252,6 +255,14 @@ function associate (models) { | |||
252 | as: 'followers', | 255 | as: 'followers', |
253 | onDelete: 'cascade' | 256 | onDelete: 'cascade' |
254 | }) | 257 | }) |
258 | |||
259 | Account.hasOne(models.Avatar, { | ||
260 | foreignKey: { | ||
261 | name: 'avatarId', | ||
262 | allowNull: true | ||
263 | }, | ||
264 | onDelete: 'cascade' | ||
265 | }) | ||
255 | } | 266 | } |
256 | 267 | ||
257 | function afterDestroy (account: AccountInstance) { | 268 | function afterDestroy (account: AccountInstance) { |
@@ -265,6 +276,15 @@ function afterDestroy (account: AccountInstance) { | |||
265 | toFormattedJSON = function (this: AccountInstance) { | 276 | toFormattedJSON = function (this: AccountInstance) { |
266 | let host = CONFIG.WEBSERVER.HOST | 277 | let host = CONFIG.WEBSERVER.HOST |
267 | let score: number | 278 | let score: number |
279 | let avatar: Avatar = null | ||
280 | |||
281 | if (this.Avatar) { | ||
282 | avatar = { | ||
283 | path: join(AVATARS_DIR.ACCOUNT, this.Avatar.filename), | ||
284 | createdAt: this.Avatar.createdAt, | ||
285 | updatedAt: this.Avatar.updatedAt | ||
286 | } | ||
287 | } | ||
268 | 288 | ||
269 | if (this.Server) { | 289 | if (this.Server) { |
270 | host = this.Server.host | 290 | host = this.Server.host |
@@ -273,11 +293,15 @@ toFormattedJSON = function (this: AccountInstance) { | |||
273 | 293 | ||
274 | const json = { | 294 | const json = { |
275 | id: this.id, | 295 | id: this.id, |
296 | uuid: this.uuid, | ||
276 | host, | 297 | host, |
277 | score, | 298 | score, |
278 | name: this.name, | 299 | name: this.name, |
300 | followingCount: this.followingCount, | ||
301 | followersCount: this.followersCount, | ||
279 | createdAt: this.createdAt, | 302 | createdAt: this.createdAt, |
280 | updatedAt: this.updatedAt | 303 | updatedAt: this.updatedAt, |
304 | avatar | ||
281 | } | 305 | } |
282 | 306 | ||
283 | return json | 307 | return json |
diff --git a/server/models/account/user.ts b/server/models/account/user.ts index 8f7c9b013..3705947c0 100644 --- a/server/models/account/user.ts +++ b/server/models/account/user.ts | |||
@@ -157,10 +157,7 @@ toFormattedJSON = function (this: UserInstance) { | |||
157 | roleLabel: USER_ROLE_LABELS[this.role], | 157 | roleLabel: USER_ROLE_LABELS[this.role], |
158 | videoQuota: this.videoQuota, | 158 | videoQuota: this.videoQuota, |
159 | createdAt: this.createdAt, | 159 | createdAt: this.createdAt, |
160 | account: { | 160 | account: this.Account.toFormattedJSON() |
161 | id: this.Account.id, | ||
162 | uuid: this.Account.uuid | ||
163 | } | ||
164 | } | 161 | } |
165 | 162 | ||
166 | if (Array.isArray(this.Account.VideoChannels) === true) { | 163 | if (Array.isArray(this.Account.VideoChannels) === true) { |
diff --git a/server/models/avatar/avatar-interface.ts b/server/models/avatar/avatar-interface.ts new file mode 100644 index 000000000..4af2b87b7 --- /dev/null +++ b/server/models/avatar/avatar-interface.ts | |||
@@ -0,0 +1,16 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | export namespace AvatarMethods {} | ||
4 | |||
5 | export interface AvatarClass {} | ||
6 | |||
7 | export interface AvatarAttributes { | ||
8 | filename: string | ||
9 | } | ||
10 | |||
11 | export interface AvatarInstance extends AvatarClass, AvatarAttributes, Sequelize.Instance<AvatarAttributes> { | ||
12 | createdAt: Date | ||
13 | updatedAt: Date | ||
14 | } | ||
15 | |||
16 | export interface AvatarModel extends AvatarClass, Sequelize.Model<AvatarInstance, AvatarAttributes> {} | ||
diff --git a/server/models/avatar/avatar.ts b/server/models/avatar/avatar.ts new file mode 100644 index 000000000..96308fd5f --- /dev/null +++ b/server/models/avatar/avatar.ts | |||
@@ -0,0 +1,24 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | import { addMethodsToModel } from '../utils' | ||
3 | import { AvatarAttributes, AvatarInstance } from './avatar-interface' | ||
4 | |||
5 | let Avatar: Sequelize.Model<AvatarInstance, AvatarAttributes> | ||
6 | |||
7 | export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes) { | ||
8 | Avatar = sequelize.define<AvatarInstance, AvatarAttributes>('Avatar', | ||
9 | { | ||
10 | filename: { | ||
11 | type: DataTypes.STRING, | ||
12 | allowNull: false | ||
13 | } | ||
14 | }, | ||
15 | {} | ||
16 | ) | ||
17 | |||
18 | const classMethods = [] | ||
19 | addMethodsToModel(Avatar, classMethods) | ||
20 | |||
21 | return Avatar | ||
22 | } | ||
23 | |||
24 | // ------------------------------ Statics ------------------------------ | ||
diff --git a/server/models/avatar/index.ts b/server/models/avatar/index.ts new file mode 100644 index 000000000..877aed1ce --- /dev/null +++ b/server/models/avatar/index.ts | |||
@@ -0,0 +1 @@ | |||
export * from './avatar-interface' | |||
diff --git a/server/models/index.ts b/server/models/index.ts index 65faa5294..fedd97dd1 100644 --- a/server/models/index.ts +++ b/server/models/index.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export * from './application' | 1 | export * from './application' |
2 | export * from './avatar' | ||
2 | export * from './job' | 3 | export * from './job' |
3 | export * from './oauth' | 4 | export * from './oauth' |
4 | export * from './server' | 5 | export * from './server' |
diff --git a/server/models/video/video-interface.ts b/server/models/video/video-interface.ts index be140de86..2a63350af 100644 --- a/server/models/video/video-interface.ts +++ b/server/models/video/video-interface.ts | |||
@@ -50,7 +50,6 @@ export namespace VideoMethods { | |||
50 | export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> > | 50 | export type ListUserVideosForApi = (userId: number, start: number, count: number, sort: string) => Bluebird< ResultList<VideoInstance> > |
51 | export type SearchAndPopulateAccountAndServerAndTags = ( | 51 | export type SearchAndPopulateAccountAndServerAndTags = ( |
52 | value: string, | 52 | value: string, |
53 | field: string, | ||
54 | start: number, | 53 | start: number, |
55 | count: number, | 54 | count: number, |
56 | sort: string | 55 | sort: string |
diff --git a/server/models/video/video.ts b/server/models/video/video.ts index f3469c1de..d46fdeebe 100644 --- a/server/models/video/video.ts +++ b/server/models/video/video.ts | |||
@@ -104,7 +104,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
104 | }, | 104 | }, |
105 | category: { | 105 | category: { |
106 | type: DataTypes.INTEGER, | 106 | type: DataTypes.INTEGER, |
107 | allowNull: false, | 107 | allowNull: true, |
108 | defaultValue: null, | ||
108 | validate: { | 109 | validate: { |
109 | categoryValid: value => { | 110 | categoryValid: value => { |
110 | const res = isVideoCategoryValid(value) | 111 | const res = isVideoCategoryValid(value) |
@@ -114,7 +115,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
114 | }, | 115 | }, |
115 | licence: { | 116 | licence: { |
116 | type: DataTypes.INTEGER, | 117 | type: DataTypes.INTEGER, |
117 | allowNull: false, | 118 | allowNull: true, |
118 | defaultValue: null, | 119 | defaultValue: null, |
119 | validate: { | 120 | validate: { |
120 | licenceValid: value => { | 121 | licenceValid: value => { |
@@ -126,6 +127,7 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
126 | language: { | 127 | language: { |
127 | type: DataTypes.INTEGER, | 128 | type: DataTypes.INTEGER, |
128 | allowNull: true, | 129 | allowNull: true, |
130 | defaultValue: null, | ||
129 | validate: { | 131 | validate: { |
130 | languageValid: value => { | 132 | languageValid: value => { |
131 | const res = isVideoLanguageValid(value) | 133 | const res = isVideoLanguageValid(value) |
@@ -155,7 +157,8 @@ export default function (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.Da | |||
155 | }, | 157 | }, |
156 | description: { | 158 | description: { |
157 | type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max), | 159 | type: DataTypes.STRING(CONSTRAINTS_FIELDS.VIDEOS.DESCRIPTION.max), |
158 | allowNull: false, | 160 | allowNull: true, |
161 | defaultValue: null, | ||
159 | validate: { | 162 | validate: { |
160 | descriptionValid: value => { | 163 | descriptionValid: value => { |
161 | const res = isVideoDescriptionValid(value) | 164 | const res = isVideoDescriptionValid(value) |
@@ -486,7 +489,7 @@ toFormattedJSON = function (this: VideoInstance) { | |||
486 | description: this.getTruncatedDescription(), | 489 | description: this.getTruncatedDescription(), |
487 | serverHost, | 490 | serverHost, |
488 | isLocal: this.isOwned(), | 491 | isLocal: this.isOwned(), |
489 | account: this.VideoChannel.Account.name, | 492 | accountName: this.VideoChannel.Account.name, |
490 | duration: this.duration, | 493 | duration: this.duration, |
491 | views: this.views, | 494 | views: this.views, |
492 | likes: this.likes, | 495 | likes: this.likes, |
@@ -514,6 +517,7 @@ toFormattedDetailsJSON = function (this: VideoInstance) { | |||
514 | privacy: this.privacy, | 517 | privacy: this.privacy, |
515 | descriptionPath: this.getDescriptionPath(), | 518 | descriptionPath: this.getDescriptionPath(), |
516 | channel: this.VideoChannel.toFormattedJSON(), | 519 | channel: this.VideoChannel.toFormattedJSON(), |
520 | account: this.VideoChannel.Account.toFormattedJSON(), | ||
517 | files: [] | 521 | files: [] |
518 | } | 522 | } |
519 | 523 | ||
@@ -560,6 +564,22 @@ toActivityPubObject = function (this: VideoInstance) { | |||
560 | } | 564 | } |
561 | } | 565 | } |
562 | 566 | ||
567 | let category | ||
568 | if (this.category) { | ||
569 | category = { | ||
570 | identifier: this.category + '', | ||
571 | name: this.getCategoryLabel() | ||
572 | } | ||
573 | } | ||
574 | |||
575 | let licence | ||
576 | if (this.licence) { | ||
577 | licence = { | ||
578 | identifier: this.licence + '', | ||
579 | name: this.getLicenceLabel() | ||
580 | } | ||
581 | } | ||
582 | |||
563 | let likesObject | 583 | let likesObject |
564 | let dislikesObject | 584 | let dislikesObject |
565 | 585 | ||
@@ -631,14 +651,8 @@ toActivityPubObject = function (this: VideoInstance) { | |||
631 | duration: 'PT' + this.duration + 'S', | 651 | duration: 'PT' + this.duration + 'S', |
632 | uuid: this.uuid, | 652 | uuid: this.uuid, |
633 | tag, | 653 | tag, |
634 | category: { | 654 | category, |
635 | identifier: this.category + '', | 655 | licence, |
636 | name: this.getCategoryLabel() | ||
637 | }, | ||
638 | licence: { | ||
639 | identifier: this.licence + '', | ||
640 | name: this.getLicenceLabel() | ||
641 | }, | ||
642 | language, | 656 | language, |
643 | views: this.views, | 657 | views: this.views, |
644 | nsfw: this.nsfw, | 658 | nsfw: this.nsfw, |
@@ -663,6 +677,8 @@ toActivityPubObject = function (this: VideoInstance) { | |||
663 | } | 677 | } |
664 | 678 | ||
665 | getTruncatedDescription = function (this: VideoInstance) { | 679 | getTruncatedDescription = function (this: VideoInstance) { |
680 | if (!this.description) return null | ||
681 | |||
666 | const options = { | 682 | const options = { |
667 | length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max | 683 | length: CONSTRAINTS_FIELDS.VIDEOS.TRUNCATED_DESCRIPTION.max |
668 | } | 684 | } |
@@ -753,8 +769,6 @@ getDescriptionPath = function (this: VideoInstance) { | |||
753 | 769 | ||
754 | getCategoryLabel = function (this: VideoInstance) { | 770 | getCategoryLabel = function (this: VideoInstance) { |
755 | let categoryLabel = VIDEO_CATEGORIES[this.category] | 771 | let categoryLabel = VIDEO_CATEGORIES[this.category] |
756 | |||
757 | // Maybe our server is not up to date and there are new categories since our version | ||
758 | if (!categoryLabel) categoryLabel = 'Misc' | 772 | if (!categoryLabel) categoryLabel = 'Misc' |
759 | 773 | ||
760 | return categoryLabel | 774 | return categoryLabel |
@@ -762,15 +776,12 @@ getCategoryLabel = function (this: VideoInstance) { | |||
762 | 776 | ||
763 | getLicenceLabel = function (this: VideoInstance) { | 777 | getLicenceLabel = function (this: VideoInstance) { |
764 | let licenceLabel = VIDEO_LICENCES[this.licence] | 778 | let licenceLabel = VIDEO_LICENCES[this.licence] |
765 | |||
766 | // Maybe our server is not up to date and there are new licences since our version | ||
767 | if (!licenceLabel) licenceLabel = 'Unknown' | 779 | if (!licenceLabel) licenceLabel = 'Unknown' |
768 | 780 | ||
769 | return licenceLabel | 781 | return licenceLabel |
770 | } | 782 | } |
771 | 783 | ||
772 | getLanguageLabel = function (this: VideoInstance) { | 784 | getLanguageLabel = function (this: VideoInstance) { |
773 | // Language is an optional attribute | ||
774 | let languageLabel = VIDEO_LANGUAGES[this.language] | 785 | let languageLabel = VIDEO_LANGUAGES[this.language] |
775 | if (!languageLabel) languageLabel = 'Unknown' | 786 | if (!languageLabel) languageLabel = 'Unknown' |
776 | 787 | ||
@@ -1070,7 +1081,7 @@ loadByUUIDAndPopulateAccountAndServerAndTags = function (uuid: string) { | |||
1070 | return Video.findOne(options) | 1081 | return Video.findOne(options) |
1071 | } | 1082 | } |
1072 | 1083 | ||
1073 | searchAndPopulateAccountAndServerAndTags = function (value: string, field: string, start: number, count: number, sort: string) { | 1084 | searchAndPopulateAccountAndServerAndTags = function (value: string, start: number, count: number, sort: string) { |
1074 | const serverInclude: Sequelize.IncludeOptions = { | 1085 | const serverInclude: Sequelize.IncludeOptions = { |
1075 | model: Video['sequelize'].models.Server, | 1086 | model: Video['sequelize'].models.Server, |
1076 | required: false | 1087 | required: false |
@@ -1099,33 +1110,24 @@ searchAndPopulateAccountAndServerAndTags = function (value: string, field: strin | |||
1099 | order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ] | 1110 | order: [ getSort(sort), [ Video['sequelize'].models.Tag, 'name', 'ASC' ] ] |
1100 | } | 1111 | } |
1101 | 1112 | ||
1102 | if (field === 'tags') { | 1113 | // TODO: search on tags too |
1103 | const escapedValue = Video['sequelize'].escape('%' + value + '%') | 1114 | // const escapedValue = Video['sequelize'].escape('%' + value + '%') |
1104 | query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( | 1115 | // query.where['id'][Sequelize.Op.in] = Video['sequelize'].literal( |
1105 | `(SELECT "VideoTags"."videoId" | 1116 | // `(SELECT "VideoTags"."videoId" |
1106 | FROM "Tags" | 1117 | // FROM "Tags" |
1107 | INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" | 1118 | // INNER JOIN "VideoTags" ON "Tags"."id" = "VideoTags"."tagId" |
1108 | WHERE name ILIKE ${escapedValue} | 1119 | // WHERE name ILIKE ${escapedValue} |
1109 | )` | 1120 | // )` |
1110 | ) | 1121 | // ) |
1111 | } else if (field === 'host') { | 1122 | |
1112 | // FIXME: Include our server? (not stored in the database) | 1123 | // TODO: search on account too |
1113 | serverInclude.where = { | 1124 | // accountInclude.where = { |
1114 | host: { | 1125 | // name: { |
1115 | [Sequelize.Op.iLike]: '%' + value + '%' | 1126 | // [Sequelize.Op.iLike]: '%' + value + '%' |
1116 | } | 1127 | // } |
1117 | } | 1128 | // } |
1118 | serverInclude.required = true | 1129 | query.where['name'] = { |
1119 | } else if (field === 'account') { | 1130 | [Sequelize.Op.iLike]: '%' + value + '%' |
1120 | accountInclude.where = { | ||
1121 | name: { | ||
1122 | [Sequelize.Op.iLike]: '%' + value + '%' | ||
1123 | } | ||
1124 | } | ||
1125 | } else { | ||
1126 | query.where[field] = { | ||
1127 | [Sequelize.Op.iLike]: '%' + value + '%' | ||
1128 | } | ||
1129 | } | 1131 | } |
1130 | 1132 | ||
1131 | query.include = [ | 1133 | query.include = [ |