diff --git a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts index 729cd296b..cb1110593 100644 --- a/client/src/app/shared/shared-custom-markup/custom-markup.service.ts +++ b/client/src/app/shared/shared-custom-markup/custom-markup.service.ts @@ -138,10 +138,10 @@ export class CustomMarkupService { const component = this.dynamicElementService.createElement(ButtonMarkupComponent) const model = { - theme: data.theme, + theme: data.theme ?? 'primary', href: data.href, label: data.label, - blankTarget: this.buildBoolean(data.blankTarget) + blankTarget: this.buildBoolean(data.blankTarget) ?? false } this.dynamicElementService.setModel(component, model) diff --git a/server/helpers/promise-cache.ts b/server/helpers/promise-cache.ts new file mode 100644 index 000000000..07e8a9962 --- /dev/null +++ b/server/helpers/promise-cache.ts @@ -0,0 +1,21 @@ +export class PromiseCache { + private readonly running = new Map>() + + constructor ( + private readonly fn: (arg: A) => Promise, + private readonly keyBuilder: (arg: A) => string + ) { + } + + run (arg: A) { + const key = this.keyBuilder(arg) + + if (this.running.has(key)) return this.running.get(key) + + const p = this.fn(arg) + + this.running.set(key, p) + + return p.finally(() => this.running.delete(key)) + } +} diff --git a/server/lib/activitypub/actors/get.ts b/server/lib/activitypub/actors/get.ts index de93aa964..501ff74e3 100644 --- a/server/lib/activitypub/actors/get.ts +++ b/server/lib/activitypub/actors/get.ts @@ -56,7 +56,7 @@ async function getOrCreateAPActor ( if (actor.Account) (actor as MActorAccountChannelIdActor).Account.Actor = actor if (actor.VideoChannel) (actor as MActorAccountChannelIdActor).VideoChannel.Actor = actor - const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded(actor, fetchType) + const { actor: actorRefreshed, refreshed } = await refreshActorIfNeeded({ actor, fetchedType: fetchType }) if (!actorRefreshed) throw new Error('Actor ' + actor.url + ' does not exist anymore.') await scheduleOutboxFetchIfNeeded(actor, created, refreshed, updateCollections) diff --git a/server/lib/activitypub/actors/refresh.ts b/server/lib/activitypub/actors/refresh.ts index 1f7cfcd8f..b2fe3932f 100644 --- a/server/lib/activitypub/actors/refresh.ts +++ b/server/lib/activitypub/actors/refresh.ts @@ -1,4 +1,5 @@ import { logger, loggerTagsFactory } from '@server/helpers/logger' +import { PromiseCache } from '@server/helpers/promise-cache' import { PeerTubeRequestError } from '@server/helpers/requests' import { ActorLoadByUrlType } from '@server/lib/model-loaders' import { ActorModel } from '@server/models/actor/actor' @@ -8,11 +9,30 @@ import { fetchRemoteActor } from './shared' import { APActorUpdater } from './updater' import { getUrlFromWebfinger } from './webfinger' -async function refreshActorIfNeeded ( - actorArg: T, +type RefreshResult = Promise<{ actor: T | MActorFull, refreshed: boolean }> + +type RefreshOptions = { + actor: T fetchedType: ActorLoadByUrlType -): Promise<{ actor: T | MActorFull, refreshed: boolean }> { - if (!actorArg.isOutdated()) return { actor: actorArg, refreshed: false } +} + +const promiseCache = new PromiseCache(doRefresh, (options: RefreshOptions) => options.actor.url) + +function refreshActorIfNeeded (options: RefreshOptions): RefreshResult { + const actorArg = options.actor + if (!actorArg.isOutdated()) return Promise.resolve({ actor: actorArg, refreshed: false }) + + return promiseCache.run(options) +} + +export { + refreshActorIfNeeded +} + +// --------------------------------------------------------------------------- + +async function doRefresh (options: RefreshOptions): RefreshResult { + const { actor: actorArg, fetchedType } = options // We need more attributes const actor = fetchedType === 'all' @@ -52,12 +72,6 @@ async function refreshActorIfNeeded { diff --git a/server/lib/job-queue/handlers/activitypub-refresher.ts b/server/lib/job-queue/handlers/activitypub-refresher.ts index f1c7d01b6..d97e50ebc 100644 --- a/server/lib/job-queue/handlers/activitypub-refresher.ts +++ b/server/lib/job-queue/handlers/activitypub-refresher.ts @@ -47,7 +47,7 @@ async function refreshActor (actorUrl: string) { const actor = await ActorModel.loadByUrlAndPopulateAccountAndChannel(actorUrl) if (actor) { - await refreshActorIfNeeded(actor, fetchType) + await refreshActorIfNeeded({ actor, fetchedType: fetchType }) } } diff --git a/shared/models/custom-markup/custom-markup-data.model.ts b/shared/models/custom-markup/custom-markup-data.model.ts index 06c9b544d..c56494485 100644 --- a/shared/models/custom-markup/custom-markup-data.model.ts +++ b/shared/models/custom-markup/custom-markup-data.model.ts @@ -34,7 +34,6 @@ export type VideosListMarkupData = { languageOneOf?: string // coma separated values onlyLocal?: string // boolean - } export type ButtonMarkupData = {