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