Commit f4796856 authored by Chocobozzz's avatar Chocobozzz Committed by Chocobozzz

Agnostic actor image storage

parent 968aaed2
import { Account } from '@app/shared/shared-main/account/account.model'
import { hasUserRight } from '@shared/core-utils/users'
import {
Avatar,
ActorImage,
NSFWPolicyType,
User as UserServerModel,
UserAdminFlag,
......@@ -131,7 +131,7 @@ export class User implements UserServerModel {
}
}
updateAccountAvatar (newAccountAvatar?: Avatar) {
updateAccountAvatar (newAccountAvatar?: ActorImage) {
if (newAccountAvatar) this.account.updateAvatar(newAccountAvatar)
else this.account.resetAvatar()
}
......
......@@ -7,8 +7,7 @@ import { AuthService } from '@app/core/auth'
import { getBytes } from '@root-helpers/bytes'
import { UserLocalStorageKeys } from '@root-helpers/users'
import {
Avatar,
NSFWPolicyType,
ActorImage,
ResultList,
User as UserServerModel,
UserCreate,
......@@ -136,7 +135,7 @@ export class UserService {
changeAvatar (avatarForm: FormData) {
const url = UserService.BASE_USERS_URL + 'me/avatar/pick'
return this.authHttp.post<{ avatar: Avatar }>(url, avatarForm)
return this.authHttp.post<{ avatar: ActorImage }>(url, avatarForm)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
......
import { Account as ServerAccount, Avatar } from '@shared/models'
import { Account as ServerAccount, ActorImage } from '@shared/models'
import { Actor } from './actor.model'
export class Account extends Actor implements ServerAccount {
......@@ -38,7 +38,7 @@ export class Account extends Actor implements ServerAccount {
this.mutedServerByInstance = false
}
updateAvatar (newAvatar: Avatar) {
updateAvatar (newAvatar: ActorImage) {
this.avatar = newAvatar
this.updateComputedAttributes()
......
import { Actor as ActorServer, Avatar } from '@shared/models'
import { Actor as ActorServer, ActorImage } from '@shared/models'
import { getAbsoluteAPIUrl } from '@app/helpers'
export abstract class Actor implements ActorServer {
......@@ -10,7 +10,7 @@ export abstract class Actor implements ActorServer {
followersCount: number
createdAt: Date | string
updatedAt: Date | string
avatar: Avatar
avatar: ActorImage
avatarUrl: string
......
import { Account as ServerAccount, Avatar, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models'
import { Account as ServerAccount, ActorImage, VideoChannel as ServerVideoChannel, ViewsPerDate } from '@shared/models'
import { Account } from '../account/account.model'
import { Actor } from '../account/actor.model'
......@@ -51,7 +51,7 @@ export class VideoChannel extends Actor implements ServerVideoChannel {
}
}
updateAvatar (newAvatar: Avatar) {
updateAvatar (newAvatar: ActorImage) {
this.avatar = newAvatar
this.updateComputedAttributes()
......
......@@ -3,7 +3,7 @@ import { catchError, map, tap } from 'rxjs/operators'
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import { ComponentPaginationLight, RestExtractor, RestService } from '@app/core'
import { Avatar, ResultList, VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '@shared/models'
import { ActorImage, ResultList, VideoChannel as VideoChannelServer, VideoChannelCreate, VideoChannelUpdate } from '@shared/models'
import { environment } from '../../../../environments/environment'
import { Account } from '../account'
import { AccountService } from '../account/account.service'
......@@ -85,7 +85,7 @@ export class VideoChannelService {
changeVideoChannelAvatar (videoChannelName: string, avatarForm: FormData) {
const url = VideoChannelService.BASE_VIDEO_CHANNEL_URL + videoChannelName + '/avatar/pick'
return this.authHttp.post<{ avatar: Avatar }>(url, avatarForm)
return this.authHttp.post<{ avatar: ActorImage }>(url, avatarForm)
.pipe(catchError(err => this.restExtractor.handleError(err)))
}
......
......@@ -6,7 +6,7 @@ import { Actor } from '@app/shared/shared-main/account/actor.model'
import { VideoChannel } from '@app/shared/shared-main/video-channel/video-channel.model'
import { peertubeTranslate } from '@shared/core-utils/i18n'
import {
Avatar,
ActorImage,
ServerConfig,
UserRight,
Video as VideoServerModel,
......@@ -72,7 +72,7 @@ export class Video implements VideoServerModel {
displayName: string
url: string
host: string
avatar?: Avatar
avatar?: ActorImage
}
channel: {
......@@ -81,7 +81,7 @@ export class Video implements VideoServerModel {
displayName: string
url: string
host: string
avatar?: Avatar
avatar?: ActorImage
}
userHistory?: {
......
......@@ -11,7 +11,7 @@ import { VideoRedundancyModel } from '../server/models/redundancy/video-redundan
import * as Bluebird from 'bluebird'
import { getUUIDFromFilename } from '../server/helpers/utils'
import { ThumbnailModel } from '../server/models/video/thumbnail'
import { AvatarModel } from '../server/models/avatar/avatar'
import { ActorImageModel } from '../server/models/account/actor-image'
import { uniq, values } from 'lodash'
import { ThumbnailType } from '@shared/models'
......@@ -43,7 +43,7 @@ async function run () {
await pruneDirectory(CONFIG.STORAGE.PREVIEWS_DIR, doesThumbnailExist(true, ThumbnailType.PREVIEW)),
await pruneDirectory(CONFIG.STORAGE.THUMBNAILS_DIR, doesThumbnailExist(false, ThumbnailType.MINIATURE)),
await pruneDirectory(CONFIG.STORAGE.AVATARS_DIR, doesAvatarExist)
await pruneDirectory(CONFIG.STORAGE.ACTOR_IMAGES, doesActorImageExist)
)
const tmpFiles = await readdir(CONFIG.STORAGE.TMP_DIR)
......@@ -107,10 +107,10 @@ function doesThumbnailExist (keepOnlyOwned: boolean, type: ThumbnailType) {
}
}
async function doesAvatarExist (file: string) {
const avatar = await AvatarModel.loadByName(file)
async function doesActorImageExist (file: string) {
const image = await ActorImageModel.loadByName(file)
return !!avatar
return !!image
}
async function doesRedundancyExist (file: string) {
......
......@@ -11,7 +11,7 @@ import { CONFIG } from '../../../initializers/config'
import { MIMETYPES } from '../../../initializers/constants'
import { sequelizeTypescript } from '../../../initializers/database'
import { sendUpdateActor } from '../../../lib/activitypub/send'
import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../../lib/avatar'
import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../../lib/actor-image'
import { getOriginalVideoFileTotalDailyFromUser, getOriginalVideoFileTotalFromUser, sendVerifyUserEmail } from '../../../lib/user'
import {
asyncMiddleware,
......
......@@ -13,7 +13,7 @@ import { CONFIG } from '../../initializers/config'
import { MIMETYPES } from '../../initializers/constants'
import { sequelizeTypescript } from '../../initializers/database'
import { sendUpdateActor } from '../../lib/activitypub/send'
import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../lib/avatar'
import { deleteLocalActorAvatarFile, updateLocalActorAvatarFile } from '../../lib/actor-image'
import { JobQueue } from '../../lib/job-queue'
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
import {
......
......@@ -4,10 +4,10 @@ import { VideosTorrentCache } from '@server/lib/files-cache/videos-torrent-cache
import { HttpStatusCode } from '../../shared/core-utils/miscs/http-error-codes'
import { logger } from '../helpers/logger'
import { LAZY_STATIC_PATHS, STATIC_MAX_AGE } from '../initializers/constants'
import { avatarPathUnsafeCache, pushAvatarProcessInQueue } from '../lib/avatar'
import { actorImagePathUnsafeCache, pushActorImageProcessInQueue } from '../lib/actor-image'
import { VideosCaptionCache, VideosPreviewCache } from '../lib/files-cache'
import { asyncMiddleware } from '../middlewares'
import { AvatarModel } from '../models/avatar/avatar'
import { ActorImageModel } from '../models/account/actor-image'
const lazyStaticRouter = express.Router()
......@@ -15,7 +15,12 @@ lazyStaticRouter.use(cors())
lazyStaticRouter.use(
LAZY_STATIC_PATHS.AVATARS + ':filename',
asyncMiddleware(getAvatar)
asyncMiddleware(getActorImage)
)
lazyStaticRouter.use(
LAZY_STATIC_PATHS.BANNERS + ':filename',
asyncMiddleware(getActorImage)
)
lazyStaticRouter.use(
......@@ -43,36 +48,36 @@ export {
// ---------------------------------------------------------------------------
async function getAvatar (req: express.Request, res: express.Response) {
async function getActorImage (req: express.Request, res: express.Response) {
const filename = req.params.filename
if (avatarPathUnsafeCache.has(filename)) {
return res.sendFile(avatarPathUnsafeCache.get(filename), { maxAge: STATIC_MAX_AGE.SERVER })
if (actorImagePathUnsafeCache.has(filename)) {
return res.sendFile(actorImagePathUnsafeCache.get(filename), { maxAge: STATIC_MAX_AGE.SERVER })
}
const avatar = await AvatarModel.loadByName(filename)
if (!avatar) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
const image = await ActorImageModel.loadByName(filename)
if (!image) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
if (avatar.onDisk === false) {
if (!avatar.fileUrl) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
if (image.onDisk === false) {
if (!image.fileUrl) return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
logger.info('Lazy serve remote avatar image %s.', avatar.fileUrl)
logger.info('Lazy serve remote actor image %s.', image.fileUrl)
try {
await pushAvatarProcessInQueue({ filename: avatar.filename, fileUrl: avatar.fileUrl })
await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl })
} catch (err) {
logger.warn('Cannot process remote avatar %s.', avatar.fileUrl, { err })
logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err })
return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
}
avatar.onDisk = true
avatar.save()
.catch(err => logger.error('Cannot save new avatar disk state.', { err }))
image.onDisk = true
image.save()
.catch(err => logger.error('Cannot save new actor image disk state.', { err }))
}
const path = avatar.getPath()
const path = image.getPath()
avatarPathUnsafeCache.set(filename, path)
actorImagePathUnsafeCache.set(filename, path)
return res.sendFile(path, { maxAge: STATIC_MAX_AGE.LAZY_SERVER })
}
......
......@@ -59,7 +59,7 @@ const CONFIG = {
},
STORAGE: {
TMP_DIR: buildPath(config.get<string>('storage.tmp')),
AVATARS_DIR: buildPath(config.get<string>('storage.avatars')),
ACTOR_IMAGES: buildPath(config.get<string>('storage.avatars')),
LOG_DIR: buildPath(config.get<string>('storage.logs')),
VIDEOS_DIR: buildPath(config.get<string>('storage.videos')),
STREAMING_PLAYLISTS_DIR: buildPath(config.get<string>('storage.streaming_playlists')),
......
......@@ -580,6 +580,7 @@ const STATIC_DOWNLOAD_PATHS = {
HLS_VIDEOS: '/download/streaming-playlists/hls/videos/'
}
const LAZY_STATIC_PATHS = {
BANNERS: '/lazy-static/banners/',
AVATARS: '/lazy-static/avatars/',
PREVIEWS: '/lazy-static/previews/',
VIDEO_CAPTIONS: '/lazy-static/video-captions/',
......@@ -634,7 +635,7 @@ const LRU_CACHE = {
USER_TOKENS: {
MAX_SIZE: 1000
},
AVATAR_STATIC: {
ACTOR_IMAGE_STATIC: {
MAX_SIZE: 500
}
}
......@@ -671,7 +672,7 @@ const MEMOIZE_LENGTH = {
}
const QUEUE_CONCURRENCY = {
AVATAR_PROCESS_IMAGE: 3
ACTOR_PROCESS_IMAGE: 3
}
const REDUNDANCY = {
......
import { TrackerModel } from '@server/models/server/tracker'
import { VideoTrackerModel } from '@server/models/server/video-tracker'
import { QueryTypes, Transaction } from 'sequelize'
import { Sequelize as SequelizeTypescript } from 'sequelize-typescript'
import { TrackerModel } from '@server/models/server/tracker'
import { VideoTrackerModel } from '@server/models/server/video-tracker'
import { isTestInstance } from '../helpers/core-utils'
import { logger } from '../helpers/logger'
import { AbuseModel } from '../models/abuse/abuse'
......@@ -11,6 +11,7 @@ import { VideoCommentAbuseModel } from '../models/abuse/video-comment-abuse'
import { AccountModel } from '../models/account/account'
import { AccountBlocklistModel } from '../models/account/account-blocklist'
import { AccountVideoRateModel } from '../models/account/account-video-rate'
import { ActorImageModel } from '../models/account/actor-image'
import { UserModel } from '../models/account/user'
import { UserNotificationModel } from '../models/account/user-notification'
import { UserNotificationSettingModel } from '../models/account/user-notification-setting'
......@@ -18,7 +19,6 @@ import { UserVideoHistoryModel } from '../models/account/user-video-history'
import { ActorModel } from '../models/activitypub/actor'
import { ActorFollowModel } from '../models/activitypub/actor-follow'
import { ApplicationModel } from '../models/application/application'
import { AvatarModel } from '../models/avatar/avatar'
import { OAuthClientModel } from '../models/oauth/oauth-client'
import { OAuthTokenModel } from '../models/oauth/oauth-token'
import { VideoRedundancyModel } from '../models/redundancy/video-redundancy'
......@@ -95,7 +95,7 @@ async function initDatabaseModels (silent: boolean) {
ApplicationModel,
ActorModel,
ActorFollowModel,
AvatarModel,
ActorImageModel,
AccountModel,
OAuthClientModel,
OAuthTokenModel,
......
......@@ -19,8 +19,8 @@ import { getUrlFromWebfinger } from '../../helpers/webfinger'
import { MIMETYPES, WEBSERVER } from '../../initializers/constants'
import { sequelizeTypescript } from '../../initializers/database'
import { AccountModel } from '../../models/account/account'
import { ActorImageModel } from '../../models/account/actor-image'
import { ActorModel } from '../../models/activitypub/actor'
import { AvatarModel } from '../../models/avatar/avatar'
import { ServerModel } from '../../models/server/server'
import { VideoChannelModel } from '../../models/video/video-channel'
import {
......@@ -183,7 +183,7 @@ async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo
}
}
const avatar = await AvatarModel.create({
const avatar = await ActorImageModel.create({
filename: info.name,
onDisk: info.onDisk,
fileUrl: info.fileUrl
......@@ -378,7 +378,7 @@ function saveActorAndServerAndModelIfNotExist (
// Avatar?
if (result.avatar) {
const avatar = await AvatarModel.create({
const avatar = await ActorImageModel.create({
filename: result.avatar.name,
fileUrl: result.avatar.fileUrl,
onDisk: false
......
import 'multer'
import { sendUpdateActor } from './activitypub/send'
import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
import { updateActorAvatarInstance, deleteActorAvatarInstance } from './activitypub/actor'
import { processImage } from '../helpers/image-utils'
import { queue } from 'async'
import * as LRUCache from 'lru-cache'
import { extname, join } from 'path'
import { retryTransactionWrapper } from '../helpers/database-utils'
import { v4 as uuidv4 } from 'uuid'
import { retryTransactionWrapper } from '../helpers/database-utils'
import { processImage } from '../helpers/image-utils'
import { downloadImage } from '../helpers/requests'
import { CONFIG } from '../initializers/config'
import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
import { sequelizeTypescript } from '../initializers/database'
import * as LRUCache from 'lru-cache'
import { queue } from 'async'
import { downloadImage } from '../helpers/requests'
import { MAccountDefault, MChannelDefault } from '../types/models'
import { deleteActorAvatarInstance, updateActorAvatarInstance } from './activitypub/actor'
import { sendUpdateActor } from './activitypub/send'
async function updateLocalActorAvatarFile (
accountOrChannel: MAccountDefault | MChannelDefault,
......@@ -20,7 +20,7 @@ async function updateLocalActorAvatarFile (
const extension = extname(avatarPhysicalFile.filename)
const avatarName = uuidv4() + extension
const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, avatarName)
await processImage(avatarPhysicalFile.path, destination, AVATARS_SIZE)
return retryTransactionWrapper(() => {
......@@ -59,12 +59,12 @@ async function deleteLocalActorAvatarFile (
type DownloadImageQueueTask = { fileUrl: string, filename: string }
const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => {
downloadImage(task.fileUrl, CONFIG.STORAGE.AVATARS_DIR, task.filename, AVATARS_SIZE)
downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, AVATARS_SIZE)
.then(() => cb())
.catch(err => cb(err))
}, QUEUE_CONCURRENCY.AVATAR_PROCESS_IMAGE)
}, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE)
function pushAvatarProcessInQueue (task: DownloadImageQueueTask) {
function pushActorImageProcessInQueue (task: DownloadImageQueueTask) {
return new Promise<void>((res, rej) => {
downloadImageQueue.push(task, err => {
if (err) return rej(err)
......@@ -75,11 +75,11 @@ function pushAvatarProcessInQueue (task: DownloadImageQueueTask) {
}
// Unsafe so could returns paths that does not exist anymore
const avatarPathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE.AVATAR_STATIC.MAX_SIZE })
const actorImagePathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE.ACTOR_IMAGE_STATIC.MAX_SIZE })
export {
avatarPathUnsafeCache,
actorImagePathUnsafeCache,
updateLocalActorAvatarFile,
deleteLocalActorAvatarFile,
pushAvatarProcessInQueue
pushActorImageProcessInQueue
}
......@@ -33,7 +33,7 @@ import {
import { ActorModel } from '../activitypub/actor'
import { ActorFollowModel } from '../activitypub/actor-follow'
import { ApplicationModel } from '../application/application'
import { AvatarModel } from '../avatar/avatar'
import { ActorImageModel } from './actor-image'
import { ServerModel } from '../server/server'
import { ServerBlocklistModel } from '../server/server-blocklist'
import { getSort, throwIfNotValid } from '../utils'
......@@ -82,7 +82,8 @@ export type SummaryOptions = {
serverInclude,
{
model: AvatarModel.unscoped(),
model: ActorImageModel.unscoped(),
as: 'Avatar',
required: false
}
]
......
import { remove } from 'fs-extra'
import { join } from 'path'
import { AfterDestroy, AllowNull, Column, CreatedAt, Is, Model, Table, UpdatedAt } from 'sequelize-typescript'
import { Avatar } from '../../../shared/models/avatars/avatar.model'
import { LAZY_STATIC_PATHS } from '../../initializers/constants'
import { MActorImageFormattable } from '@server/types/models'
import { ActorImageType } from '@shared/models'
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { logger } from '../../helpers/logger'
import { remove } from 'fs-extra'
import { CONFIG } from '../../initializers/config'
import { LAZY_STATIC_PATHS } from '../../initializers/constants'
import { throwIfNotValid } from '../utils'
import { isActivityPubUrlValid } from '../../helpers/custom-validators/activitypub/misc'
import { MAvatarFormattable } from '@server/types/models'
@Table({
tableName: 'avatar',
tableName: 'actorImage',
indexes: [
{
fields: [ 'filename' ],
......@@ -18,14 +19,14 @@ import { MAvatarFormattable } from '@server/types/models'
}
]
})
export class AvatarModel extends Model {
export class ActorImageModel extends Model {
@AllowNull(false)
@Column
filename: string
@AllowNull(true)
@Is('AvatarFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true))
@Is('ActorImageFileUrl', value => throwIfNotValid(value, isActivityPubUrlValid, 'fileUrl', true))
@Column
fileUrl: string
......@@ -33,6 +34,10 @@ export class AvatarModel extends Model {
@Column
onDisk: boolean
@AllowNull(false)
@Column
type: ActorImageType
@CreatedAt
createdAt: Date
......@@ -40,12 +45,12 @@ export class AvatarModel extends Model {
updatedAt: Date
@AfterDestroy
static removeFilesAndSendDelete (instance: AvatarModel) {
logger.info('Removing avatar file %s.', instance.filename)
static removeFilesAndSendDelete (instance: ActorImageModel) {
logger.info('Removing actor image file %s.', instance.filename)
// Don't block the transaction
instance.removeAvatar()
.catch(err => logger.error('Cannot remove avatar file %s.', instance.filename, err))
instance.removeImage()
.catch(err => logger.error('Cannot remove actor image file %s.', instance.filename, err))
}
static loadByName (filename: string) {
......@@ -55,10 +60,10 @@ export class AvatarModel extends Model {
}
}
return AvatarModel.findOne(query)
return ActorImageModel.findOne(query)
}
toFormattedJSON (this: MAvatarFormattable): Avatar {
toFormattedJSON (this: MActorImageFormattable): ActorImage {
return {
path: this.getStaticPath(),
createdAt: this.createdAt,
......@@ -71,11 +76,11 @@ export class AvatarModel extends Model {
}
getPath () {
return join(CONFIG.STORAGE.AVATARS_DIR, this.filename)
return join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
}
removeAvatar () {
const avatarPath = join(CONFIG.STORAGE.AVATARS_DIR, this.filename)
return remove(avatarPath)
removeImage () {
const imagePath = join(CONFIG.STORAGE.ACTOR_IMAGES, this.filename)
return remove(imagePath)
}
}
......@@ -10,7 +10,6 @@ import { VideoCommentAbuseModel } from '../abuse/video-comment-abuse'
import { ActorModel } from '../activitypub/actor'
import { ActorFollowModel } from '../activitypub/actor-follow'
import { ApplicationModel } from '../application/application'
import { AvatarModel } from '../avatar/avatar'
import { PluginModel } from '../server/plugin'
import { ServerModel } from '../server/server'
import { getSort, throwIfNotValid } from '../utils'
......@@ -20,6 +19,7 @@ import { VideoChannelModel } from '../video/video-channel'
import { VideoCommentModel } from '../video/video-comment'
import { VideoImportModel } from '../video/video-import'
import { AccountModel } from './account'
import { ActorImageModel } from './actor-image'
import { UserModel } from './user'
enum ScopeNames {
......@@ -34,7 +34,8 @@ function buildActorWithAvatarInclude () {
include: [
{
attributes: [ 'filename' ],
model: AvatarModel.unscoped(),
as: 'Avatar',
model: ActorImageModel.unscoped(),
required: false
},
{
......@@ -172,7 +173,8 @@ function buildAccountInclude (required: boolean, withActor = false) {
},
{
attributes: [ 'filename' ],
model: AvatarModel.unscoped(),
as: 'Avatar',
model: ActorImageModel.unscoped(),
required: false
},
{
......
......@@ -19,7 +19,7 @@ import {
} from 'sequelize-typescript'
import { ModelCache } from '@server/models/model-cache'
import { ActivityIconObject, ActivityPubActorType } from '../../../shared/models/activitypub'
import { Avatar } from '../../../shared/models/avatars/avatar.model'
import { ActorImage } from '../../../shared/models/actors/actor-image.model'
import { activityPubContextify } from '../../helpers/activitypub'
import {
isActorFollowersCountValid,
......@@ -43,7 +43,7 @@ import {
MActorWithInboxes
} from '../../types/models'
import { AccountModel } from '../account/account'
import { AvatarModel } from '../avatar/avatar'
import { ActorImageModel } from '../account/actor-image'
import { ServerModel } from '../server/server'