local-actor.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  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 (options: {
  32. accountOrChannel: MAccountDefault | MChannelDefault
  33. imagePhysicalFile: { path: string }
  34. type: ActorImageType_Type
  35. sendActorUpdate: boolean
  36. }) {
  37. const { accountOrChannel, imagePhysicalFile, type, sendActorUpdate } = options
  38. const processImageSize = async (imageSize: { width: number, height: number }) => {
  39. const extension = getLowercaseExtension(imagePhysicalFile.path)
  40. const imageName = buildUUID() + extension
  41. const destination = join(CONFIG.STORAGE.ACTOR_IMAGES_DIR, imageName)
  42. await processImageFromWorker({ path: imagePhysicalFile.path, destination, newSize: imageSize, keepOriginal: true })
  43. return {
  44. imageName,
  45. imageSize
  46. }
  47. }
  48. const processedImages = await Promise.all(ACTOR_IMAGES_SIZE[type].map(processImageSize))
  49. await remove(imagePhysicalFile.path)
  50. return retryTransactionWrapper(() => sequelizeTypescript.transaction(async t => {
  51. const actorImagesInfo = processedImages.map(({ imageName, imageSize }) => ({
  52. name: imageName,
  53. fileUrl: null,
  54. height: imageSize.height,
  55. width: imageSize.width,
  56. onDisk: true
  57. }))
  58. const updatedActor = await updateActorImages(accountOrChannel.Actor, type, actorImagesInfo, t)
  59. await updatedActor.save({ transaction: t })
  60. if (sendActorUpdate) {
  61. await sendUpdateActor(accountOrChannel, t)
  62. }
  63. return type === ActorImageType.AVATAR
  64. ? updatedActor.Avatars
  65. : updatedActor.Banners
  66. }))
  67. }
  68. export async function deleteLocalActorImageFile (accountOrChannel: MAccountDefault | MChannelDefault, type: ActorImageType_Type) {
  69. return retryTransactionWrapper(() => {
  70. return sequelizeTypescript.transaction(async t => {
  71. const updatedActor = await deleteActorImages(accountOrChannel.Actor, type, t)
  72. await updatedActor.save({ transaction: t })
  73. await sendUpdateActor(accountOrChannel, t)
  74. return updatedActor.Avatars
  75. })
  76. })
  77. }
  78. // ---------------------------------------------------------------------------
  79. export async function findAvailableLocalActorName (baseActorName: string, transaction?: Transaction) {
  80. let actor = await ActorModel.loadLocalByName(baseActorName, transaction)
  81. if (!actor) return baseActorName
  82. for (let i = 1; i < 30; i++) {
  83. const name = `${baseActorName}-${i}`
  84. actor = await ActorModel.loadLocalByName(name, transaction)
  85. if (!actor) return name
  86. }
  87. throw new Error('Cannot find available actor local name (too much iterations).')
  88. }