local-actor.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { remove } from 'fs-extra/esm'
  2. import { join } from 'path'
  3. import { Transaction } from 'sequelize'
  4. import { ActivityPubActorType, ActorImageType, ActorImageType_Type } from '@peertube/peertube-models'
  5. import { ActorModel } from '@server/models/actor/actor.js'
  6. import { buildUUID, getLowercaseExtension } from '@peertube/peertube-node-utils'
  7. import { retryTransactionWrapper } from '../helpers/database-utils.js'
  8. import { CONFIG } from '../initializers/config.js'
  9. import { ACTOR_IMAGES_SIZE, WEBSERVER } from '../initializers/constants.js'
  10. import { sequelizeTypescript } from '../initializers/database.js'
  11. import { MAccountDefault, MActor, MChannelDefault } from '../types/models/index.js'
  12. import { deleteActorImages, updateActorImages } from './activitypub/actors/index.js'
  13. import { sendUpdateActor } from './activitypub/send/index.js'
  14. import { processImageFromWorker } from './worker/parent-process.js'
  15. export function buildActorInstance (type: ActivityPubActorType, url: string, preferredUsername: string) {
  16. return new ActorModel({
  17. type,
  18. url,
  19. preferredUsername,
  20. publicKey: null,
  21. privateKey: null,
  22. followersCount: 0,
  23. followingCount: 0,
  24. inboxUrl: url + '/inbox',
  25. outboxUrl: url + '/outbox',
  26. sharedInboxUrl: WEBSERVER.URL + '/inbox',
  27. followersUrl: url + '/followers',
  28. followingUrl: url + '/following'
  29. }) as MActor
  30. }
  31. export async function updateLocalActorImageFiles (
  32. accountOrChannel: MAccountDefault | MChannelDefault,
  33. imagePhysicalFile: Express.Multer.File,
  34. type: ActorImageType_Type
  35. ) {
  36. const processImageSize = async (imageSize: { width: number, height: number }) => {
  37. const extension = getLowercaseExtension(imagePhysicalFile.filename)
  38. const imageName = buildUUID() + extension
  39. const destination = join(CONFIG.STORAGE.ACTOR_IMAGES_DIR, imageName)
  40. await processImageFromWorker({ path: imagePhysicalFile.path, destination, newSize: imageSize, keepOriginal: true })
  41. return {
  42. imageName,
  43. imageSize
  44. }
  45. }
  46. const processedImages = await Promise.all(ACTOR_IMAGES_SIZE[type].map(processImageSize))
  47. await remove(imagePhysicalFile.path)
  48. return retryTransactionWrapper(() => sequelizeTypescript.transaction(async t => {
  49. const actorImagesInfo = processedImages.map(({ imageName, imageSize }) => ({
  50. name: imageName,
  51. fileUrl: null,
  52. height: imageSize.height,
  53. width: imageSize.width,
  54. onDisk: true
  55. }))
  56. const updatedActor = await updateActorImages(accountOrChannel.Actor, type, actorImagesInfo, t)
  57. await updatedActor.save({ transaction: t })
  58. await sendUpdateActor(accountOrChannel, t)
  59. return type === ActorImageType.AVATAR
  60. ? updatedActor.Avatars
  61. : updatedActor.Banners
  62. }))
  63. }
  64. export async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType_Type) {
  65. return retryTransactionWrapper(() => {
  66. return sequelizeTypescript.transaction(async t => {
  67. const updatedActor = await deleteActorImages(accountOrChannel.Actor, type, t)
  68. await updatedActor.save({ transaction: t })
  69. await sendUpdateActor(accountOrChannel, t)
  70. return updatedActor.Avatars
  71. })
  72. })
  73. }
  74. // ---------------------------------------------------------------------------
  75. export async function findAvailableLocalActorName (baseActorName: string, transaction?: Transaction) {
  76. let actor = await ActorModel.loadLocalByName(baseActorName, transaction)
  77. if (!actor) return baseActorName
  78. for (let i = 1; i < 30; i++) {
  79. const name = `${baseActorName}-${i}`
  80. actor = await ActorModel.loadLocalByName(name, transaction)
  81. if (!actor) return name
  82. }
  83. throw new Error('Cannot find available actor local name (too much iterations).')
  84. }