]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blob - server/models/actor/actor-image.ts
Merge branch 'release/5.0.0' into develop
[github/Chocobozzz/PeerTube.git] / server / models / actor / actor-image.ts
1 import { remove } from 'fs-extra'
2 import { join } from 'path'
3 import {
4 AfterDestroy,
5 AllowNull,
6 BelongsTo,
7 Column,
8 CreatedAt,
9 Default,
10 ForeignKey,
11 Is,
12 Model,
13 Table,
14 UpdatedAt
15 } from 'sequelize-typescript'
16 import { MActorImage, MActorImageFormattable } from '@server/types/models'
17 import { getLowercaseExtension } from '@shared/core-utils'
18 import { ActivityIconObject, ActorImageType } from '@shared/models'
19 import { AttributesOnly } from '@shared/typescript-utils'
20 import { ActorImage } from '../../../shared/models/actors/actor-image.model'
21 import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
22 import { logger } from '../../helpers/logger'
23 import { CONFIG } from '../../initializers/config'
24 import { LAZY_STATIC_PATHS, MIMETYPES, WEBSERVER } from '../../initializers/constants'
25 import { buildSQLAttributes, throwIfNotValid } from '../shared'
26 import { ActorModel } from './actor'
27
28 @Table({
29 tableName: 'actorImage',
30 indexes: [
31 {
32 fields: [ 'filename' ],
33 unique: true
34 },
35 {
36 fields: [ 'actorId', 'type', 'width' ],
37 unique: true
38 }
39 ]
40 })
41 export class ActorImageModel extends Model<Partial<AttributesOnly<ActorImageModel>>> {
42
43 @AllowNull(false)
44 @Column
45 filename: string
46
47 @AllowNull(true)
48 @Default(null)
49 @Column
50 height: number
51
52 @AllowNull(true)
53 @Default(null)
54 @Column
55 width: number
56
57 @AllowNull(true)
58 @Is('ActorImageFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true))
59 @Column
60 fileUrl: string
61
62 @AllowNull(false)
63 @Column
64 onDisk: boolean
65
66 @AllowNull(false)
67 @Column
68 type: ActorImageType
69
70 @CreatedAt
71 createdAt: Date
72
73 @UpdatedAt
74 updatedAt: Date
75
76 @ForeignKey(() => ActorModel)
77 @Column
78 actorId: number
79
80 @BelongsTo(() => ActorModel, {
81 foreignKey: {
82 allowNull: false
83 },
84 onDelete: 'CASCADE'
85 })
86 Actor: ActorModel
87
88 @AfterDestroy
89 static removeFilesAndSendDelete (instance: ActorImageModel) {
90 logger.info('Removing actor image file %s.', instance.filename)
91
92 // Don't block the transaction
93 instance.removeImage()
94 .catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, { err }))
95 }
96
97 // ---------------------------------------------------------------------------
98
99 static getSQLAttributes (tableName: string, aliasPrefix = '') {
100 return buildSQLAttributes({
101 model: this,
102 tableName,
103 aliasPrefix
104 })
105 }
106
107 // ---------------------------------------------------------------------------
108
109 static loadByName (filename: string) {
110 const query = {
111 where: {
112 filename
113 }
114 }
115
116 return ActorImageModel.findOne(query)
117 }
118
119 static getImageUrl (image: MActorImage) {
120 if (!image) return undefined
121
122 return WEBSERVER.URL + image.getStaticPath()
123 }
124
125 toFormattedJSON (this: MActorImageFormattable): ActorImage {
126 return {
127 width: this.width,
128 path: this.getStaticPath(),
129 createdAt: this.createdAt,
130 updatedAt: this.updatedAt
131 }
132 }
133
134 toActivityPubObject (): ActivityIconObject {
135 const extension = getLowercaseExtension(this.filename)
136
137 return {
138 type: 'Image',
139 mediaType: MIMETYPES.IMAGE.EXT_MIMETYPE[extension],
140 height: this.height,
141 width: this.width,
142 url: ActorImageModel.getImageUrl(this)
143 }
144 }
145
146 getStaticPath () {
147 switch (this.type) {
148 case ActorImageType.AVATAR:
149 return join(LAZY_STATIC_PATHS.AVATARS, this.filename)
150
151 case ActorImageType.BANNER:
152 return join(LAZY_STATIC_PATHS.BANNERS, this.filename)
153
154 default:
155 throw new Error('Unknown actor image type: ' + this.type)
156 }
157 }
158
159 getPath () {
160 return join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
161 }
162
163 removeImage () {
164 const imagePath = join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
165 return remove(imagePath)
166 }
167
168 isOwned () {
169 return !this.fileUrl
170 }
171 }