import { type UploadImageType_Type } from '@peertube/peertube-models' import { MActorId, MUploadImage } from '@server/types/models/index.js' import { remove } from 'fs-extra/esm' import { join } from 'path' import { Transaction } from 'sequelize' import { AfterDestroy, AllowNull, BelongsTo, Column, CreatedAt, Default, ForeignKey, Table, UpdatedAt } from 'sequelize-typescript' import { logger } from '../../helpers/logger.js' import { DIRECTORIES, STATIC_PATHS, WEBSERVER } from '../../initializers/constants.js' import { ActorModel } from '../actor/actor.js' import { SequelizeModel } from '../shared/index.js' // Image uploads that are not suitable for other tables actor images (avatars/banners) // Can be used to store instance images like logos, favicons, etc. @Table({ tableName: 'uploadImage', indexes: [ { fields: [ 'filename' ], unique: true }, { fields: [ 'actorId', 'type', 'width' ], unique: true } ] }) export class UploadImageModel extends SequelizeModel { @AllowNull(false) @Column declare filename: string @AllowNull(true) @Default(null) @Column declare height: number @AllowNull(true) @Default(null) @Column declare width: number @AllowNull(true) @Column declare fileUrl: string @AllowNull(false) @Column declare type: UploadImageType_Type @CreatedAt declare createdAt: Date @UpdatedAt declare updatedAt: Date @ForeignKey(() => ActorModel) @Column declare actorId: number @BelongsTo(() => ActorModel, { foreignKey: { allowNull: false }, onDelete: 'CASCADE' }) declare Actor: Awaited @AfterDestroy static removeFile (instance: UploadImageModel) { logger.info('Removing upload image file %s.', instance.filename) // Don't block the transaction instance.removeImage() .catch(err => logger.error('Cannot remove upload image file %s.', instance.filename, { err })) } static listByActor (actor: MActorId) { const query = { where: { actorId: actor.id } } return UploadImageModel.findAll(query) } static listByActorAndType (actor: MActorId, type: UploadImageType_Type, transaction: Transaction) { const query = { where: { actorId: actor.id, type }, transaction } return UploadImageModel.findAll(query) } static getImageUrl (image: MUploadImage) { if (!image) return undefined return WEBSERVER.URL + image.getStaticPath() } static getPathOf (filename: string) { return join(DIRECTORIES.UPLOAD_IMAGES, filename) } // --------------------------------------------------------------------------- getStaticPath (this: MUploadImage) { return join(STATIC_PATHS.UPLOAD_IMAGES, this.filename) } getPath () { return UploadImageModel.getPathOf(this.filename) } removeImage () { return remove(this.getPath()) } isOwned () { return !this.fileUrl } }