Added socials.
This commit is contained in:
170
src/actors.js
170
src/actors.js
@@ -55,6 +55,8 @@ const keyMap = {
|
||||
isCircumcised: 'circumcised',
|
||||
};
|
||||
|
||||
const socialsOrder = ['onlyfans', 'twitter'];
|
||||
|
||||
export function curateActor(actor, context = {}) {
|
||||
return {
|
||||
id: actor.id,
|
||||
@@ -115,9 +117,11 @@ export function curateActor(actor, context = {}) {
|
||||
agency: actor.agency,
|
||||
avatar: curateMedia(actor.avatar),
|
||||
socials: context.socials?.map((social) => ({
|
||||
id: social.id,
|
||||
url: social.url,
|
||||
platform: social.platform,
|
||||
})),
|
||||
handle: social.handle,
|
||||
})).toSorted((socialA, socialB) => socialsOrder.indexOf(socialB.platform) - socialsOrder.indexOf(socialA.platform)),
|
||||
profiles: context.profiles?.map((profile) => ({
|
||||
id: profile.id,
|
||||
description: profile.description,
|
||||
@@ -216,7 +220,7 @@ export async function fetchActorsById(actorIds, options = {}, reqUser) {
|
||||
.leftJoin('media', 'media.id', 'actors_avatars.media_id')
|
||||
.groupBy('media.id', 'actors_avatars.actor_id')
|
||||
.orderBy(knex.raw('max(actors_avatars.created_at)'), 'desc'),
|
||||
knex('actors_social')
|
||||
knex('actors_socials')
|
||||
.whereIn('actor_id', actorIds),
|
||||
reqUser
|
||||
? knex('stashes_actors')
|
||||
@@ -527,14 +531,14 @@ export async function fetchActorRevisions(revisionId, filters = {}, reqUser) {
|
||||
}
|
||||
|
||||
async function applyActorValueDelta(profileId, delta, trx) {
|
||||
return knex('actors_profiles')
|
||||
await knex('actors_profiles')
|
||||
.where('id', profileId)
|
||||
.update(keyMap[delta.key] || delta.key, delta.value)
|
||||
.transacting(trx);
|
||||
}
|
||||
|
||||
async function applyActorDirectDelta(actorId, delta, trx) {
|
||||
return knex('actors')
|
||||
await knex('actors')
|
||||
.where('id', actorId)
|
||||
.update(keyMap[delta.key] || delta.key, delta.value)
|
||||
.modify((builder) => {
|
||||
@@ -545,6 +549,22 @@ async function applyActorDirectDelta(actorId, delta, trx) {
|
||||
.transacting(trx);
|
||||
}
|
||||
|
||||
async function applyActorSocialsDelta(actorId, delta, trx) {
|
||||
await knex('actors_socials')
|
||||
.where('actor_id', actorId)
|
||||
.delete()
|
||||
.transacting(trx);
|
||||
|
||||
await knex('actors_socials')
|
||||
.insert(delta.value.map((social) => ({
|
||||
actor_id: actorId,
|
||||
platform: social.platform,
|
||||
handle: social.handle,
|
||||
url: social.url,
|
||||
})))
|
||||
.transacting(trx);
|
||||
}
|
||||
|
||||
async function fetchMainProfile(actorId, wasCreated = false) {
|
||||
const profileEntry = await knex('actors_profiles')
|
||||
.where('actor_id', actorId)
|
||||
@@ -623,6 +643,10 @@ async function applyActorRevision(revisionIds, reqUser) {
|
||||
return applyActorValueDelta(mainProfile.id, delta, trx);
|
||||
}
|
||||
|
||||
if (delta.key === 'socials') {
|
||||
return applyActorSocialsDelta(revision.actor_id, delta, trx);
|
||||
}
|
||||
|
||||
if (delta.key === 'name' && reqUser.role === 'admin') {
|
||||
return applyActorDirectDelta(revision.actor_id, delta, trx);
|
||||
}
|
||||
@@ -767,35 +791,59 @@ function convertWeight(weight, units) {
|
||||
return Number(weight) || null;
|
||||
}
|
||||
|
||||
export async function createActorRevision(actorId, {
|
||||
edits,
|
||||
comment,
|
||||
apply,
|
||||
...options
|
||||
}, reqUser) {
|
||||
const [
|
||||
[actor],
|
||||
openRevisions,
|
||||
] = await Promise.all([
|
||||
fetchActorsById([actorId], {
|
||||
reqUser,
|
||||
includeAssets: true,
|
||||
includePartOf: true,
|
||||
}),
|
||||
knex('actors_revisions')
|
||||
.where('user_id', reqUser.id)
|
||||
.whereNull('approved'),
|
||||
]);
|
||||
const platformsByHostname = Object.fromEntries(Object.entries(config.socials.urls).map(([platform, url]) => {
|
||||
const { hostname, pathname } = new URL(url);
|
||||
|
||||
if (!actor) {
|
||||
throw new HttpError(`No actor with ID ${actorId} found to update`, 404);
|
||||
}
|
||||
return [hostname, {
|
||||
platform,
|
||||
pathname: decodeURIComponent(pathname),
|
||||
url,
|
||||
}];
|
||||
}));
|
||||
|
||||
if (openRevisions.length >= config.revisions.unapprovedLimit && reqUser.role !== 'admin') {
|
||||
throw new HttpError(`You have ${config.revisions.unapprovedLimit} unapproved revisions, please wait for approval before submitting another revision.`, 429);
|
||||
}
|
||||
function curateSocials(socials) {
|
||||
return socials.map((social) => {
|
||||
if (!social.handle && !social.url) {
|
||||
throw new Error('No social handle or website URL specified');
|
||||
}
|
||||
|
||||
const baseActor = Object.fromEntries(Object.entries(actor).map(([key, values]) => {
|
||||
if (social.handle && !social.platform) {
|
||||
throw new Error('No platform specified for social handle');
|
||||
}
|
||||
|
||||
if (social.handle && social.platform && /[\w-]+/.test(social.handle) && /[a-z]+/i.test(social.platform)) {
|
||||
return {
|
||||
platform: social.platform.toLowerCase(),
|
||||
handle: social.handle,
|
||||
};
|
||||
}
|
||||
|
||||
if (social.url) {
|
||||
const { hostname, pathname } = new URL(social.url);
|
||||
const platform = platformsByHostname[hostname];
|
||||
|
||||
if (platform) {
|
||||
const handle = pathname.match(new RegExp(platform.pathname.replace('{handle}', '([\\w-]+)')))?.[1];
|
||||
|
||||
if (handle) {
|
||||
return {
|
||||
platform: platform.platform,
|
||||
handle,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
url: social.url,
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error('Invalid social');
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
function getBaseActor(actor) {
|
||||
return Object.fromEntries(Object.entries(actor).map(([key, values]) => {
|
||||
if ([
|
||||
'scenes',
|
||||
'likes',
|
||||
@@ -805,11 +853,11 @@ export async function createActorRevision(actorId, {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* avatar should return id
|
||||
if (values?.hash) {
|
||||
return [key, values.hash];
|
||||
if ([
|
||||
'socials',
|
||||
].includes(key)) {
|
||||
return [key, values];
|
||||
}
|
||||
*/
|
||||
|
||||
if (values?.id) {
|
||||
return [key, values.id];
|
||||
@@ -825,8 +873,10 @@ export async function createActorRevision(actorId, {
|
||||
|
||||
return [key, values];
|
||||
}).filter(Boolean));
|
||||
}
|
||||
|
||||
const deltas = await Promise.all(Object.entries(edits).map(async ([key, value]) => {
|
||||
function getDeltas(edits, baseActor, options) {
|
||||
return Promise.all(Object.entries(edits).map(async ([key, value]) => {
|
||||
if (baseActor[key] === value || typeof value === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
@@ -890,6 +940,24 @@ export async function createActorRevision(actorId, {
|
||||
];
|
||||
}
|
||||
|
||||
if (key === 'socials') {
|
||||
const convertedSocials = curateSocials(value);
|
||||
|
||||
const convertedUrls = value
|
||||
.filter((social) => social.url && !convertedSocials.some((convertedSocial) => convertedSocial.url === social.url))
|
||||
.map((social) => social.url);
|
||||
|
||||
const conversionComment = convertedUrls.length > 0
|
||||
? `curated URLs ${convertedUrls.join(', ')} as social handles`
|
||||
: null;
|
||||
|
||||
return {
|
||||
key,
|
||||
value: convertedSocials,
|
||||
comment: conversionComment,
|
||||
};
|
||||
}
|
||||
|
||||
if (['cup', 'bust', 'waist', 'hip'].includes(key)) {
|
||||
const convertedValue = convertFigure(key, value, options.figureUnits);
|
||||
|
||||
@@ -967,6 +1035,38 @@ export async function createActorRevision(actorId, {
|
||||
|
||||
return { key, value };
|
||||
})).then((rawDeltas) => rawDeltas.flat().filter(Boolean));
|
||||
}
|
||||
|
||||
export async function createActorRevision(actorId, {
|
||||
edits,
|
||||
comment,
|
||||
apply,
|
||||
...options
|
||||
}, reqUser) {
|
||||
const [
|
||||
[actor],
|
||||
openRevisions,
|
||||
] = await Promise.all([
|
||||
fetchActorsById([actorId], {
|
||||
reqUser,
|
||||
includeAssets: true,
|
||||
includePartOf: true,
|
||||
}),
|
||||
knex('actors_revisions')
|
||||
.where('user_id', reqUser.id)
|
||||
.whereNull('approved'),
|
||||
]);
|
||||
|
||||
if (!actor) {
|
||||
throw new HttpError(`No actor with ID ${actorId} found to update`, 404);
|
||||
}
|
||||
|
||||
if (openRevisions.length >= config.revisions.unapprovedLimit && reqUser.role !== 'admin') {
|
||||
throw new HttpError(`You have ${config.revisions.unapprovedLimit} unapproved revisions, please wait for approval before submitting another revision.`, 429);
|
||||
}
|
||||
|
||||
const baseActor = getBaseActor(actor);
|
||||
const deltas = await getDeltas(edits, baseActor, options);
|
||||
|
||||
const deltaComments = deltas.map((delta) => delta.comment);
|
||||
const curatedComment = [comment, ...deltaComments].filter(Boolean).join(' | ');
|
||||
|
||||
@@ -42,6 +42,7 @@ export default async function mainHandler(req, res, next) {
|
||||
media: config.media,
|
||||
psa: config.psa,
|
||||
links: config.links,
|
||||
socials: config.socials,
|
||||
},
|
||||
meta: {
|
||||
now: new Date().toISOString(),
|
||||
|
||||
Reference in New Issue
Block a user