avatar.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. import 'multer'
  2. import { sendUpdateActor } from './activitypub/send'
  3. import { AVATARS_SIZE, LRU_CACHE, QUEUE_CONCURRENCY } from '../initializers/constants'
  4. import { updateActorAvatarInstance, deleteActorAvatarInstance } from './activitypub/actor'
  5. import { processImage } from '../helpers/image-utils'
  6. import { extname, join } from 'path'
  7. import { retryTransactionWrapper } from '../helpers/database-utils'
  8. import { v4 as uuidv4 } from 'uuid'
  9. import { CONFIG } from '../initializers/config'
  10. import { sequelizeTypescript } from '../initializers/database'
  11. import * as LRUCache from 'lru-cache'
  12. import { queue } from 'async'
  13. import { downloadImage } from '../helpers/requests'
  14. import { MAccountDefault, MChannelDefault } from '../types/models'
  15. async function updateActorAvatarFile (
  16. accountOrChannel: MAccountDefault | MChannelDefault,
  17. avatarPhysicalFile: Express.Multer.File
  18. ) {
  19. const extension = extname(avatarPhysicalFile.filename)
  20. const avatarName = uuidv4() + extension
  21. const destination = join(CONFIG.STORAGE.AVATARS_DIR, avatarName)
  22. await processImage(avatarPhysicalFile.path, destination, AVATARS_SIZE)
  23. return retryTransactionWrapper(() => {
  24. return sequelizeTypescript.transaction(async t => {
  25. const avatarInfo = {
  26. name: avatarName,
  27. fileUrl: null,
  28. onDisk: true
  29. }
  30. const updatedActor = await updateActorAvatarInstance(accountOrChannel.Actor, avatarInfo, t)
  31. await updatedActor.save({ transaction: t })
  32. await sendUpdateActor(accountOrChannel, t)
  33. return updatedActor.Avatar
  34. })
  35. })
  36. }
  37. async function deleteActorAvatarFile (
  38. accountOrChannel: MAccountDefault | MChannelDefault
  39. ) {
  40. return retryTransactionWrapper(() => {
  41. return sequelizeTypescript.transaction(async t => {
  42. const updatedActor = await deleteActorAvatarInstance(accountOrChannel.Actor, t)
  43. await updatedActor.save({ transaction: t })
  44. await sendUpdateActor(accountOrChannel, t)
  45. return updatedActor.Avatar
  46. })
  47. })
  48. }
  49. type DownloadImageQueueTask = { fileUrl: string, filename: string }
  50. const downloadImageQueue = queue<DownloadImageQueueTask, Error>((task, cb) => {
  51. downloadImage(task.fileUrl, CONFIG.STORAGE.AVATARS_DIR, task.filename, AVATARS_SIZE)
  52. .then(() => cb())
  53. .catch(err => cb(err))
  54. }, QUEUE_CONCURRENCY.AVATAR_PROCESS_IMAGE)
  55. function pushAvatarProcessInQueue (task: DownloadImageQueueTask) {
  56. return new Promise((res, rej) => {
  57. downloadImageQueue.push(task, err => {
  58. if (err) return rej(err)
  59. return res()
  60. })
  61. })
  62. }
  63. // Unsafe so could returns paths that does not exist anymore
  64. const avatarPathUnsafeCache = new LRUCache<string, string>({ max: LRU_CACHE.AVATAR_STATIC.MAX_SIZE })
  65. export {
  66. avatarPathUnsafeCache,
  67. updateActorAvatarFile,
  68. deleteActorAvatarFile,
  69. pushAvatarProcessInQueue
  70. }