Added actors admin panel with bulk merge. Fixed merge failing if source and target actor have conflicting network profiles.
This commit is contained in:
@@ -378,7 +378,7 @@ async function queryManticoreSql(filters, options, _reqUser) {
|
||||
if (filters.query.charAt(0) === '#') {
|
||||
builder.where('id', Number(escape(filters.query.slice(1))));
|
||||
} else {
|
||||
builder.whereRaw('match(\'@(name,aliases) :query:\', actors)', { query: escape(filters.query) });
|
||||
builder.whereRaw(`match('@(name,aliases) :query:${filters.query.charAt(0) === '=' ? '' : '*'}', actors)`, { query: escape(filters.query) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,8 +447,13 @@ async function queryManticoreSql(filters, options, _reqUser) {
|
||||
builder.where('has_avatar', 1);
|
||||
}
|
||||
|
||||
console.log('ACTOR OPTIONS', options);
|
||||
|
||||
if (options.order?.[0] === 'name') {
|
||||
builder.orderBy('actors.slug', options.order[1]);
|
||||
builder.orderBy([
|
||||
{ column: 'actors.slug', order: options.order[1] },
|
||||
{ column: 'actors.entity_id', order: 'asc' },
|
||||
]);
|
||||
} else if (options.order?.[0] === 'likes') {
|
||||
builder.orderBy([
|
||||
{ column: 'actors.stashed', order: options.order[1] },
|
||||
@@ -477,6 +482,10 @@ async function queryManticoreSql(filters, options, _reqUser) {
|
||||
]);
|
||||
} else {
|
||||
builder.orderBy('actors.slug', 'asc');
|
||||
builder.orderBy([
|
||||
{ column: 'actors.slug', order: 'asc' },
|
||||
{ column: 'actors.entity_id', order: 'asc' },
|
||||
]);
|
||||
}
|
||||
})
|
||||
.limit(options.limit)
|
||||
@@ -558,72 +567,70 @@ export async function createActor(newActor, context, reqUser) {
|
||||
return curateActor(actorEntry);
|
||||
}
|
||||
|
||||
export async function mergeActors(targetActorId, sourceActorId, reqUser) {
|
||||
export async function mergeActors(targetActorId, sourceActorIds, reqUser) {
|
||||
if (!verifyAbility(reqUser, 'actor', 'merge')) {
|
||||
throw new HttpError('You are not permitted to merge actors', 403);
|
||||
}
|
||||
|
||||
const [targetActor, sourceActor] = await Promise.all([
|
||||
if (sourceActorIds.includes(targetActorId)) {
|
||||
throw new HttpError('Cannot merge actor profile into itself', 400);
|
||||
}
|
||||
|
||||
const [targetActor, sourceActors] = await Promise.all([
|
||||
knex('actors')
|
||||
.where('id', targetActorId)
|
||||
.whereNull('entity_id')
|
||||
.whereNull('alias_for')
|
||||
.first(),
|
||||
knex('actors')
|
||||
.where('id', sourceActorId)
|
||||
.first(),
|
||||
.whereIn('id', sourceActorIds),
|
||||
]);
|
||||
|
||||
if (!targetActor) {
|
||||
throw new HttpError('Target actor not found', 404);
|
||||
}
|
||||
|
||||
if (!sourceActor) {
|
||||
if (sourceActors.length < sourceActorIds.length) {
|
||||
throw new HttpError('Source actor not found', 404);
|
||||
}
|
||||
|
||||
if (targetActor.entity_id) {
|
||||
throw new HttpError('Target actor is not global', 400);
|
||||
}
|
||||
|
||||
if (targetActor.alias_for) {
|
||||
throw new HttpError('Target actor is aliased', 400);
|
||||
}
|
||||
|
||||
const trx = await knex.transaction();
|
||||
|
||||
let mergedProfiles;
|
||||
let mergedScenes;
|
||||
|
||||
try {
|
||||
await trx('actors')
|
||||
.update('alias_for', targetActorId)
|
||||
.where('id', sourceActorId)
|
||||
.returning(['id', 'alias_for']);
|
||||
const [existingProfiles] = await Promise.all([
|
||||
trx('actors_profiles')
|
||||
.where('actor_id', targetActorId),
|
||||
trx('actors')
|
||||
.update('alias_for', targetActorId)
|
||||
.whereIn('id', sourceActorIds)
|
||||
.returning(['id', 'alias_for']),
|
||||
// some avatars are not matched to a profile, need to investigate why this happens and the avatar table needs a dedicated actor field
|
||||
trx('actors_avatars')
|
||||
.update('actor_id', targetActorId)
|
||||
.whereIn('actor_id', sourceActorIds),
|
||||
trx('stashes_actors')
|
||||
.update('actor_id', targetActorId)
|
||||
.whereIn('actor_id', sourceActorIds)
|
||||
.returning('id'),
|
||||
]);
|
||||
|
||||
mergedProfiles = await trx('actors_profiles')
|
||||
.update('actor_id', targetActorId)
|
||||
.where('actor_id', sourceActorId)
|
||||
.whereIn('actor_id', sourceActorIds)
|
||||
.whereNotIn('entity_id', existingProfiles.map((profile) => profile.entity_id))
|
||||
.returning('id');
|
||||
|
||||
// some avatars are not matched to a profile, need to investigate why this happens and the avatar table needs a dedicated actor field
|
||||
await trx('actors_avatars')
|
||||
.update('actor_id', targetActorId)
|
||||
.where('actor_id', sourceActorId);
|
||||
|
||||
mergedScenes = await trx('releases_actors')
|
||||
.update({
|
||||
actor_id: targetActorId,
|
||||
alias_id: sourceActorId,
|
||||
alias_id: knex.raw('actor_id'),
|
||||
})
|
||||
.where('actor_id', sourceActorId)
|
||||
.whereIn('actor_id', sourceActorIds)
|
||||
.returning('release_id');
|
||||
|
||||
await trx('stashes_actors')
|
||||
.update('actor_id', targetActorId)
|
||||
.where('actor_id', sourceActorId)
|
||||
.returning('id');
|
||||
|
||||
await trx.commit();
|
||||
} catch (error) {
|
||||
await trx.rollback();
|
||||
@@ -631,7 +638,7 @@ export async function mergeActors(targetActorId, sourceActorId, reqUser) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
await interpolateProfiles([targetActorId, sourceActorId], {
|
||||
await interpolateProfiles([targetActorId, ...sourceActorIds], {
|
||||
knex,
|
||||
logger,
|
||||
moment,
|
||||
@@ -641,8 +648,8 @@ export async function mergeActors(targetActorId, sourceActorId, reqUser) {
|
||||
|
||||
await Promise.all([
|
||||
syncScenes(mergedScenes.map((scene) => scene.release_id)),
|
||||
syncActors([targetActorId, sourceActorId]),
|
||||
syncStashes('actor', [targetActorId, sourceActorId]),
|
||||
syncActors([targetActorId, ...sourceActorIds]),
|
||||
syncStashes('actor', [targetActorId, ...sourceActorIds]),
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user