Commit 2cb03dc1 authored by Chocobozzz's avatar Chocobozzz Committed by Chocobozzz

Add banners support

parent f4796856
......@@ -158,9 +158,9 @@ async function getConfig (req: express.Request, res: express.Response) {
avatar: {
file: {
size: {
max: CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max
max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max
},
extensions: CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME
extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
}
},
video: {
......
......@@ -2,7 +2,7 @@ import 'multer'
import * as express from 'express'
import { auditLoggerFactory, getAuditIdFromRes, UserAuditView } from '@server/helpers/audit-logger'
import { Hooks } from '@server/lib/plugins/hooks'
import { UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared'
import { ActorImageType, UserUpdateMe, UserVideoRate as FormattedUserVideoRate } from '../../../../shared'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { UserVideoQuota } from '../../../../shared/models/users/user-video-quota.model'
import { createReqFiles } from '../../../helpers/express-utils'
......@@ -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/actor-image'
import { deleteLocalActorImageFile, updateLocalActorImageFile } from '../../../lib/actor-image'
import { getOriginalVideoFileTotalDailyFromUser, getOriginalVideoFileTotalFromUser, sendVerifyUserEmail } from '../../../lib/user'
import {
asyncMiddleware,
......@@ -238,7 +238,7 @@ async function updateMyAvatar (req: express.Request, res: express.Response) {
const userAccount = await AccountModel.load(user.Account.id)
const avatar = await updateLocalActorAvatarFile(userAccount, avatarPhysicalFile)
const avatar = await updateLocalActorImageFile(userAccount, avatarPhysicalFile, ActorImageType.AVATAR)
return res.json({ avatar: avatar.toFormattedJSON() })
}
......@@ -247,7 +247,7 @@ async function deleteMyAvatar (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.user
const userAccount = await AccountModel.load(user.Account.id)
await deleteLocalActorAvatarFile(userAccount)
await deleteLocalActorImageFile(userAccount, ActorImageType.AVATAR)
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
}
import 'multer'
import * as express from 'express'
import { sendUndoFollow } from '@server/lib/activitypub/send'
import { VideoChannelModel } from '@server/models/video/video-channel'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
import { VideoFilter } from '../../../../shared/models/videos/video-query.type'
import { buildNSFWFilter, getCountVideos } from '../../../helpers/express-utils'
import { getFormattedObjects } from '../../../helpers/utils'
......@@ -26,8 +29,6 @@ import {
} from '../../../middlewares/validators'
import { ActorFollowModel } from '../../../models/activitypub/actor-follow'
import { VideoModel } from '../../../models/video/video'
import { sendUndoFollow } from '@server/lib/activitypub/send'
import { HttpStatusCode } from '../../../../shared/core-utils/miscs/http-error-codes'
const mySubscriptionsRouter = express.Router()
......@@ -66,7 +67,7 @@ mySubscriptionsRouter.post('/me/subscriptions',
mySubscriptionsRouter.get('/me/subscriptions/:uri',
authenticate,
userSubscriptionGetValidator,
getUserSubscription
asyncMiddleware(getUserSubscription)
)
mySubscriptionsRouter.delete('/me/subscriptions/:uri',
......@@ -130,10 +131,11 @@ function addUserSubscription (req: express.Request, res: express.Response) {
return res.status(HttpStatusCode.NO_CONTENT_204).end()
}
function getUserSubscription (req: express.Request, res: express.Response) {
async function getUserSubscription (req: express.Request, res: express.Response) {
const subscription = res.locals.subscription
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(subscription.ActorFollowing.VideoChannel.id)
return res.json(subscription.ActorFollowing.VideoChannel.toFormattedJSON())
return res.json(videoChannel.toFormattedJSON())
}
async function deleteUserSubscription (req: express.Request, res: express.Response) {
......
import * as express from 'express'
import { Hooks } from '@server/lib/plugins/hooks'
import { getServerActor } from '@server/models/application/application'
import { MChannelAccountDefault } from '@server/types/models'
import { VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
import { MChannelBannerAccountDefault } from '@server/types/models'
import { ActorImageType, VideoChannelCreate, VideoChannelUpdate } from '../../../shared'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { auditLoggerFactory, getAuditIdFromRes, VideoChannelAuditView } from '../../helpers/audit-logger'
import { resetSequelizeInstance } from '../../helpers/database-utils'
......@@ -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/actor-image'
import { deleteLocalActorImageFile, updateLocalActorImageFile } from '../../lib/actor-image'
import { JobQueue } from '../../lib/job-queue'
import { createLocalVideoChannel, federateAllVideosOfChannel } from '../../lib/video-channel'
import {
......@@ -33,7 +33,7 @@ import {
videoPlaylistsSortValidator
} from '../../middlewares'
import { videoChannelsNameWithHostValidator, videoChannelsOwnSearchValidator, videosSortValidator } from '../../middlewares/validators'
import { updateAvatarValidator } from '../../middlewares/validators/avatar'
import { updateAvatarValidator, updateBannerValidator } from '../../middlewares/validators/avatar'
import { commonVideoPlaylistFiltersValidator } from '../../middlewares/validators/videos/video-playlists'
import { AccountModel } from '../../models/account/account'
import { VideoModel } from '../../models/video/video'
......@@ -42,6 +42,7 @@ import { VideoPlaylistModel } from '../../models/video/video-playlist'
const auditLogger = auditLoggerFactory('channels')
const reqAvatarFile = createReqFiles([ 'avatarfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { avatarfile: CONFIG.STORAGE.TMP_DIR })
const reqBannerFile = createReqFiles([ 'bannerfile' ], MIMETYPES.IMAGE.MIMETYPE_EXT, { bannerfile: CONFIG.STORAGE.TMP_DIR })
const videoChannelRouter = express.Router()
......@@ -69,6 +70,15 @@ videoChannelRouter.post('/:nameWithHost/avatar/pick',
asyncMiddleware(updateVideoChannelAvatar)
)
videoChannelRouter.post('/:nameWithHost/banner/pick',
authenticate,
reqBannerFile,
// Check the rights
asyncMiddleware(videoChannelsUpdateValidator),
updateBannerValidator,
asyncMiddleware(updateVideoChannelBanner)
)
videoChannelRouter.delete('/:nameWithHost/avatar',
authenticate,
// Check the rights
......@@ -76,6 +86,13 @@ videoChannelRouter.delete('/:nameWithHost/avatar',
asyncMiddleware(deleteVideoChannelAvatar)
)
videoChannelRouter.delete('/:nameWithHost/banner',
authenticate,
// Check the rights
asyncMiddleware(videoChannelsUpdateValidator),
asyncMiddleware(deleteVideoChannelBanner)
)
videoChannelRouter.put('/:nameWithHost',
authenticate,
asyncMiddleware(videoChannelsUpdateValidator),
......@@ -134,26 +151,41 @@ async function listVideoChannels (req: express.Request, res: express.Response) {
return res.json(getFormattedObjects(resultList.data, resultList.total))
}
async function updateVideoChannelBanner (req: express.Request, res: express.Response) {
const bannerPhysicalFile = req.files['bannerfile'][0]
const videoChannel = res.locals.videoChannel
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
const banner = await updateLocalActorImageFile(videoChannel, bannerPhysicalFile, ActorImageType.BANNER)
auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys)
return res.json({ banner: banner.toFormattedJSON() })
}
async function updateVideoChannelAvatar (req: express.Request, res: express.Response) {
const avatarPhysicalFile = req.files['avatarfile'][0]
const videoChannel = res.locals.videoChannel
const oldVideoChannelAuditKeys = new VideoChannelAuditView(videoChannel.toFormattedJSON())
const avatar = await updateLocalActorAvatarFile(videoChannel, avatarPhysicalFile)
const avatar = await updateLocalActorImageFile(videoChannel, avatarPhysicalFile, ActorImageType.AVATAR)
auditLogger.update(getAuditIdFromRes(res), new VideoChannelAuditView(videoChannel.toFormattedJSON()), oldVideoChannelAuditKeys)
return res
.json({
avatar: avatar.toFormattedJSON()
})
.end()
return res.json({ avatar: avatar.toFormattedJSON() })
}
async function deleteVideoChannelAvatar (req: express.Request, res: express.Response) {
const videoChannel = res.locals.videoChannel
await deleteLocalActorAvatarFile(videoChannel)
await deleteLocalActorImageFile(videoChannel, ActorImageType.AVATAR)
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
}
async function deleteVideoChannelBanner (req: express.Request, res: express.Response) {
const videoChannel = res.locals.videoChannel
await deleteLocalActorImageFile(videoChannel, ActorImageType.BANNER)
return res.sendStatus(HttpStatusCode.NO_CONTENT_204)
}
......@@ -177,7 +209,7 @@ async function addVideoChannel (req: express.Request, res: express.Response) {
videoChannel: {
id: videoChannelCreated.id
}
}).end()
})
}
async function updateVideoChannel (req: express.Request, res: express.Response) {
......@@ -206,7 +238,7 @@ async function updateVideoChannel (req: express.Request, res: express.Response)
}
}
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelAccountDefault
const videoChannelInstanceUpdated = await videoChannelInstance.save(sequelizeOptions) as MChannelBannerAccountDefault
await sendUpdateActor(videoChannelInstanceUpdated, t)
auditLogger.update(
......@@ -252,13 +284,13 @@ async function removeVideoChannel (req: express.Request, res: express.Response)
}
async function getVideoChannel (req: express.Request, res: express.Response) {
const videoChannelWithVideos = await VideoChannelModel.loadAndPopulateAccountAndVideos(res.locals.videoChannel.id)
const videoChannel = res.locals.videoChannel
if (videoChannelWithVideos.isOutdated()) {
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannelWithVideos.Actor.url } })
if (videoChannel.isOutdated()) {
JobQueue.Instance.createJob({ type: 'activitypub-refresher', payload: { type: 'actor', url: videoChannel.Actor.url } })
}
return res.json(videoChannelWithVideos.toFormattedJSON())
return res.json(videoChannel.toFormattedJSON())
}
async function listVideoChannelPlaylists (req: express.Request, res: express.Response) {
......
......@@ -107,7 +107,7 @@ async function acceptOwnership (req: express.Request, res: express.Response) {
// We need more attributes for federation
const targetVideo = await VideoModel.loadAndPopulateAccountAndServerAndTags(videoChangeOwnership.Video.id)
const oldVideoChannel = await VideoChannelModel.loadByIdAndPopulateAccount(targetVideo.channelId)
const oldVideoChannel = await VideoChannelModel.loadAndPopulateAccount(targetVideo.channelId)
targetVideo.channelId = channel.id
......
......@@ -64,7 +64,7 @@ async function getActorImage (req: express.Request, res: express.Response) {
logger.info('Lazy serve remote actor image %s.', image.fileUrl)
try {
await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl })
await pushActorImageProcessInQueue({ filename: image.filename, fileUrl: image.fileUrl, type: image.type })
} catch (err) {
logger.warn('Cannot process remote actor image %s.', image.fileUrl, { err })
return res.sendStatus(HttpStatusCode.NOT_FOUND_404)
......
......@@ -252,9 +252,9 @@ async function generateNodeinfo (req: express.Request, res: express.Response) {
avatar: {
file: {
size: {
max: CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max
max: CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max
},
extensions: CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME
extensions: CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
}
},
video: {
......
import { values } from 'lodash'
import validator from 'validator'
import { UserRole } from '../../../shared'
import { isEmailEnabled } from '../../initializers/config'
import { CONSTRAINTS_FIELDS, NSFW_POLICY_TYPES } from '../../initializers/constants'
import { exists, isArray, isBooleanValid, isFileValid } from './misc'
import { values } from 'lodash'
import { isEmailEnabled } from '../../initializers/config'
const USERS_CONSTRAINTS_FIELDS = CONSTRAINTS_FIELDS.USERS
......@@ -97,12 +97,12 @@ function isUserRoleValid (value: any) {
return exists(value) && validator.isInt('' + value) && UserRole[value] !== undefined
}
const avatarMimeTypes = CONSTRAINTS_FIELDS.ACTORS.AVATAR.EXTNAME
const avatarMimeTypes = CONSTRAINTS_FIELDS.ACTORS.IMAGE.EXTNAME
.map(v => v.replace('.', ''))
.join('|')
const avatarMimeTypesRegex = `image/(${avatarMimeTypes})`
function isAvatarFile (files: { [ fieldname: string ]: Express.Multer.File[] } | Express.Multer.File[]) {
return isFileValid(files, avatarMimeTypesRegex, 'avatarfile', CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max)
return isFileValid(files, avatarMimeTypesRegex, 'avatarfile', CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max)
}
// ---------------------------------------------------------------------------
......
import * as express from 'express'
import { VideoChannelModel } from '../../models/video/video-channel'
import { MChannelAccountDefault } from '@server/types/models'
import { MChannelBannerAccountDefault } from '@server/types/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { VideoChannelModel } from '../../models/video/video-channel'
async function doesLocalVideoChannelNameExist (name: string, res: express.Response) {
const videoChannel = await VideoChannelModel.loadLocalByNameAndPopulateAccount(name)
......@@ -29,11 +29,10 @@ export {
doesVideoChannelNameWithHostExist
}
function processVideoChannelExist (videoChannel: MChannelAccountDefault, res: express.Response) {
function processVideoChannelExist (videoChannel: MChannelBannerAccountDefault, res: express.Response) {
if (!videoChannel) {
res.status(HttpStatusCode.NOT_FOUND_404)
.json({ error: 'Video channel not found' })
.end()
return false
}
......
......@@ -66,25 +66,24 @@ async function doesVideoFileOfVideoExist (id: number, videoIdOrUUID: number | st
}
async function doesVideoChannelOfAccountExist (channelId: number, user: MUserAccountId, res: Response) {
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
if (videoChannel === null) {
res.status(HttpStatusCode.BAD_REQUEST_400)
.json({ error: 'Unknown video `video channel` on this instance.' })
.end()
const videoChannel = await VideoChannelModel.loadAndPopulateAccount(channelId)
return false
}
if (videoChannel === null) {
res.status(HttpStatusCode.BAD_REQUEST_400)
.json({ error: 'Unknown video "video channel" for this instance.' })
return false
}
// Don't check account id if the user can update any video
if (user.hasRight(UserRight.UPDATE_ANY_VIDEO) === true) {
res.locals.videoChannel = videoChannel
return true
}
const videoChannel = await VideoChannelModel.loadByIdAndAccount(channelId, user.Account.id)
if (videoChannel === null) {
if (videoChannel.Account.id !== user.Account.id) {
res.status(HttpStatusCode.BAD_REQUEST_400)
.json({ error: 'Unknown video `video channel` for this account.' })
.end()
.json({ error: 'Unknown video "video channel" for this account.' })
return false
}
......
......@@ -305,7 +305,7 @@ const CONSTRAINTS_FIELDS = {
PUBLIC_KEY: { min: 10, max: 5000 }, // Length
PRIVATE_KEY: { min: 10, max: 5000 }, // Length
URL: { min: 3, max: 2000 }, // Length
AVATAR: {
IMAGE: {
EXTNAME: [ '.png', '.jpeg', '.jpg', '.gif', '.webp' ],
FILE_SIZE: {
max: 2 * 1024 * 1024 // 2MB
......@@ -466,6 +466,8 @@ const MIMETYPES = {
IMAGE: {
MIMETYPE_EXT: {
'image/png': '.png',
'image/gif': '.gif',
'image/webp': '.webp',
'image/jpg': '.jpg',
'image/jpeg': '.jpg'
},
......@@ -605,9 +607,15 @@ const PREVIEWS_SIZE = {
height: 480,
minWidth: 400
}
const AVATARS_SIZE = {
width: 120,
height: 120
const ACTOR_IMAGES_SIZE = {
AVATARS: {
width: 120,
height: 120
},
BANNERS: {
width: 1920,
height: 384
}
}
const EMBED_SIZE = {
......@@ -755,7 +763,7 @@ if (isTestInstance() === true) {
ACTIVITY_PUB.VIDEO_REFRESH_INTERVAL = 10 * 1000 // 10 seconds
ACTIVITY_PUB.VIDEO_PLAYLIST_REFRESH_INTERVAL = 10 * 1000 // 10 seconds
CONSTRAINTS_FIELDS.ACTORS.AVATAR.FILE_SIZE.max = 100 * 1024 // 100KB
CONSTRAINTS_FIELDS.ACTORS.IMAGE.FILE_SIZE.max = 100 * 1024 // 100KB
CONSTRAINTS_FIELDS.VIDEOS.IMAGE.FILE_SIZE.max = 400 * 1024 // 400KB
SCHEDULER_INTERVALS_MS.actorFollowScores = 1000
......@@ -816,7 +824,7 @@ export {
SEARCH_INDEX,
HLS_REDUNDANCY_DIRECTORY,
P2P_MEDIA_LOADER_PEER_VERSION,
AVATARS_SIZE,
ACTOR_IMAGES_SIZE,
ACCEPT_HEADERS,
BCRYPT_SALT_SIZE,
TRACKER_RATE_LIMITS,
......
......@@ -4,6 +4,7 @@ import { Op, Transaction } from 'sequelize'
import { URL } from 'url'
import { v4 as uuidv4 } from 'uuid'
import { getServerActor } from '@server/models/application/application'
import { ActorImageType } from '@shared/models'
import { HttpStatusCode } from '../../../shared/core-utils/miscs/http-error-codes'
import { ActivityPubActor, ActivityPubActorType, ActivityPubOrderedCollection } from '../../../shared/models/activitypub'
import { ActivityPubAttributedTo } from '../../../shared/models/activitypub/objects'
......@@ -30,10 +31,10 @@ import {
MActorAccountChannelId,
MActorAccountChannelIdActor,
MActorAccountId,
MActorDefault,
MActorFull,
MActorFullActor,
MActorId,
MActorImages,
MChannel
} from '../../types/models'
import { JobQueue } from '../job-queue'
......@@ -168,43 +169,60 @@ async function updateActorInstance (actorInstance: ActorModel, attributes: Activ
}
}
type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string }
async function updateActorAvatarInstance (actor: MActorDefault, info: AvatarInfo, t: Transaction) {
type AvatarInfo = { name: string, onDisk: boolean, fileUrl: string, type: ActorImageType }
async function updateActorImageInstance (actor: MActorImages, info: AvatarInfo, t: Transaction) {
if (!info.name) return actor
if (actor.Avatar) {
const oldImageModel = info.type === ActorImageType.AVATAR
? actor.Avatar
: actor.Banner
if (oldImageModel) {
// Don't update the avatar if the file URL did not change
if (info.fileUrl && actor.Avatar.fileUrl === info.fileUrl) return actor
if (info.fileUrl && oldImageModel.fileUrl === info.fileUrl) return actor
try {
await actor.Avatar.destroy({ transaction: t })
await oldImageModel.destroy({ transaction: t })
} catch (err) {
logger.error('Cannot remove old avatar of actor %s.', actor.url, { err })
logger.error('Cannot remove old actor image of actor %s.', actor.url, { err })
}
}
const avatar = await ActorImageModel.create({
const imageModel = await ActorImageModel.create({
filename: info.name,
onDisk: info.onDisk,
fileUrl: info.fileUrl
fileUrl: info.fileUrl,
type: info.type
}, { transaction: t })
actor.avatarId = avatar.id
actor.Avatar = avatar
if (info.type === ActorImageType.AVATAR) {
actor.avatarId = imageModel.id
actor.Avatar = imageModel
} else {
actor.bannerId = imageModel.id
actor.Banner = imageModel
}
return actor
}
async function deleteActorAvatarInstance (actor: MActorDefault, t: Transaction) {
async function deleteActorImageInstance (actor: MActorImages, type: ActorImageType, t: Transaction) {
try {
await actor.Avatar.destroy({ transaction: t })
if (type === ActorImageType.AVATAR) {
await actor.Avatar.destroy({ transaction: t })
actor.avatarId = null
actor.Avatar = null
} else {
await actor.Banner.destroy({ transaction: t })
actor.bannerId = null
actor.Banner = null
}
} catch (err) {
logger.error('Cannot remove old avatar of actor %s.', actor.url, { err })
logger.error('Cannot remove old image of actor %s.', actor.url, { err })
}
actor.avatarId = null
actor.Avatar = null
return actor
}
......@@ -219,9 +237,11 @@ async function fetchActorTotalItems (url: string) {
}
}
function getAvatarInfoIfExists (actorJSON: ActivityPubActor) {
function getImageInfoIfExists (actorJSON: ActivityPubActor, type: ActorImageType) {
const mimetypes = MIMETYPES.IMAGE
const icon = actorJSON.icon
const icon = type === ActorImageType.AVATAR
? actorJSON.icon
: actorJSON.image
if (!icon || icon.type !== 'Image' || !isActivityPubUrlValid(icon.url)) return undefined
......@@ -239,7 +259,8 @@ function getAvatarInfoIfExists (actorJSON: ActivityPubActor) {
return {
name: uuidv4() + extension,
fileUrl: icon.url
fileUrl: icon.url,
type
}
}
......@@ -293,10 +314,22 @@ async function refreshActorIfNeeded <T extends MActorFull | MActorAccountChannel
const avatarInfo = {
name: result.avatar.name,
fileUrl: result.avatar.fileUrl,
onDisk: false
onDisk: false,
type: ActorImageType.AVATAR
}
await updateActorImageInstance(actor, avatarInfo, t)
}
if (result.banner !== undefined) {
const bannerInfo = {
name: result.banner.name,
fileUrl: result.banner.fileUrl,
onDisk: false,
type: ActorImageType.BANNER
}
await updateActorAvatarInstance(actor, avatarInfo, t)
await updateActorImageInstance(actor, bannerInfo, t)
}
// Force update
......@@ -338,11 +371,11 @@ export {
buildActorInstance,
generateAndSaveActorKeys,
fetchActorTotalItems,
getAvatarInfoIfExists,
getImageInfoIfExists,
updateActorInstance,
deleteActorAvatarInstance,
deleteActorImageInstance,
refreshActorIfNeeded,
updateActorAvatarInstance,
updateActorImageInstance,
addFetchOutboxJob
}
......@@ -381,12 +414,25 @@ function saveActorAndServerAndModelIfNotExist (
const avatar = await ActorImageModel.create({
filename: result.avatar.name,
fileUrl: result.avatar.fileUrl,
onDisk: false
onDisk: false,
type: ActorImageType.AVATAR
}, { transaction: t })
actor.avatarId = avatar.id
}
// Banner?
if (result.banner) {
const banner = await ActorImageModel.create({
filename: result.banner.name,
fileUrl: result.banner.fileUrl,
onDisk: false,
type: ActorImageType.BANNER