]>
Commit | Line | Data |
---|---|---|
4bbfc6c6 | 1 | import 'multer' |
f4796856 | 2 | import { queue } from 'async' |
41fb13c3 | 3 | import LRUCache from 'lru-cache' |
ea54cd04 | 4 | import { join } from 'path' |
136d7efd | 5 | import { ActorModel } from '@server/models/actor/actor' |
c55e3d72 | 6 | import { buildUUID, getLowercaseExtension } from '@shared/core-utils' |
136d7efd | 7 | import { ActivityPubActorType, ActorImageType } from '@shared/models' |
f4796856 C |
8 | import { retryTransactionWrapper } from '../helpers/database-utils' |
9 | import { processImage } from '../helpers/image-utils' | |
10 | import { downloadImage } from '../helpers/requests' | |
6dd9de95 | 11 | import { CONFIG } from '../initializers/config' |
136d7efd | 12 | import { ACTOR_IMAGES_SIZE, LRU_CACHE, QUEUE_CONCURRENCY, WEBSERVER } from '../initializers/constants' |
74dc3bca | 13 | import { sequelizeTypescript } from '../initializers/database' |
136d7efd C |
14 | import { MAccountDefault, MActor, MChannelDefault } from '../types/models' |
15 | import { deleteActorImageInstance, updateActorImageInstance } from './activitypub/actors' | |
f4796856 | 16 | import { sendUpdateActor } from './activitypub/send' |
4bbfc6c6 | 17 | |
136d7efd C |
18 | function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) { |
19 | return new ActorModel({ | |
20 | type, | |
21 | url, | |
22 | preferredUsername, | |
23 | publicKey: null, | |
24 | privateKey: null, | |
25 | followersCount: 0, | |
26 | followingCount: 0, | |
27 | inboxUrl: url + '/inbox', | |
28 | outboxUrl: url + '/outbox', | |
29 | sharedInboxUrl: WEBSERVER.URL + '/inbox', | |
30 | followersUrl: url + '/followers', | |
31 | followingUrl: url + '/following' | |
32 | }) as MActor | |
33 | } | |
34 | ||
2cb03dc1 | 35 | async function updateLocalActorImageFile ( |
1ea7da81 | 36 | accountOrChannel: MAccountDefault | MChannelDefault, |
2cb03dc1 C |
37 | imagePhysicalFile: Express.Multer.File, |
38 | type: ActorImageType | |
453e83ea | 39 | ) { |
2cb03dc1 C |
40 | const imageSize = type === ActorImageType.AVATAR |
41 | ? ACTOR_IMAGES_SIZE.AVATARS | |
42 | : ACTOR_IMAGES_SIZE.BANNERS | |
e08ff02a | 43 | |
ea54cd04 | 44 | const extension = getLowercaseExtension(imagePhysicalFile.filename) |
2cb03dc1 | 45 | |
d4a8e7a6 | 46 | const imageName = buildUUID() + extension |
2cb03dc1 C |
47 | const destination = join(CONFIG.STORAGE.ACTOR_IMAGES, imageName) |
48 | await processImage(imagePhysicalFile.path, destination, imageSize) | |
4bbfc6c6 | 49 | |
4a534352 C |
50 | return retryTransactionWrapper(() => { |
51 | return sequelizeTypescript.transaction(async t => { | |
2cb03dc1 C |
52 | const actorImageInfo = { |
53 | name: imageName, | |
557b13ae | 54 | fileUrl: null, |
84531547 C |
55 | height: imageSize.height, |
56 | width: imageSize.width, | |
557b13ae C |
57 | onDisk: true |
58 | } | |
59 | ||
213e30ef | 60 | const updatedActor = await updateActorImageInstance(accountOrChannel.Actor, type, actorImageInfo, t) |
4a534352 | 61 | await updatedActor.save({ transaction: t }) |
4bbfc6c6 | 62 | |
4a534352 | 63 | await sendUpdateActor(accountOrChannel, t) |
4bbfc6c6 | 64 | |
2cb03dc1 C |
65 | return type === ActorImageType.AVATAR |
66 | ? updatedActor.Avatar | |
67 | : updatedActor.Banner | |
4a534352 | 68 | }) |
4bbfc6c6 C |
69 | }) |
70 | } | |
71 | ||
2cb03dc1 | 72 | async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType) { |
1ea7da81 RK |
73 | return retryTransactionWrapper(() => { |
74 | return sequelizeTypescript.transaction(async t => { | |
2cb03dc1 | 75 | const updatedActor = await deleteActorImageInstance(accountOrChannel.Actor, type, t) |
1ea7da81 RK |
76 | await updatedActor.save({ transaction: t }) |
77 | ||
78 | await sendUpdateActor(accountOrChannel, t) | |
79 | ||
80 | return updatedActor.Avatar | |
81 | }) | |
82 | }) | |
83 | } | |
84 | ||
2cb03dc1 | 85 | type DownloadImageQueueTask = { fileUrl: string, filename: string, type: ActorImageType } |
557b13ae C |
86 | |
87 | const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => { | |
2cb03dc1 C |
88 | const size = task.type === ActorImageType.AVATAR |
89 | ? ACTOR_IMAGES_SIZE.AVATARS | |
90 | : ACTOR_IMAGES_SIZE.BANNERS | |
91 | ||
92 | downloadImage(task.fileUrl, CONFIG.STORAGE.ACTOR_IMAGES, task.filename, size) | |
557b13ae C |
93 | .then(() => cb()) |
94 | .catch(err => cb(err)) | |
f4796856 | 95 | }, QUEUE_CONCURRENCY.ACTOR_PROCESS_IMAGE) |
557b13ae | 96 | |
f4796856 | 97 | function pushActorImageProcessInQueue (task: DownloadImageQueueTask) { |
ba5a8d89 | 98 | return new Promise<void>((res, rej) => { |
557b13ae C |
99 | downloadImageQueue.push(task, err => { |
100 | if (err) return rej(err) | |
101 | ||
102 | return res() | |
103 | }) | |
104 | }) | |
105 | } | |
106 | ||
107 | // Unsafe so could returns paths that does not exist anymore | |
f4796856 | 108 | const actorImagePathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE.ACTOR_IMAGE_STATIC.MAX_SIZE }) |
557b13ae | 109 | |
4bbfc6c6 | 110 | export { |
f4796856 | 111 | actorImagePathUnsafeCache, |
2cb03dc1 C |
112 | updateLocalActorImageFile, |
113 | deleteLocalActorImageFile, | |
136d7efd C |
114 | pushActorImageProcessInQueue, |
115 | buildActorInstance | |
4bbfc6c6 | 116 | } |