Files
Synapsis/src/lib/activitypub/actor.ts
T

120 lines
2.9 KiB
TypeScript

/**
* ActivityPub Actor Utilities
*
* Handles the serialization of Synapsis users to ActivityPub Actor format.
* See: https://www.w3.org/TR/activitypub/#actor-objects
*/
import type { users } from '@/db/schema';
type User = typeof users.$inferSelect;
const ACTIVITY_STREAMS_CONTEXT = 'https://www.w3.org/ns/activitystreams';
const SECURITY_CONTEXT = 'https://w3id.org/security/v1';
export interface ActivityPubActor {
'@context': (string | object)[];
id: string;
type: 'Person';
preferredUsername: string;
name: string | null;
summary: string | null;
url: string;
inbox: string;
outbox: string;
followers: string;
following: string;
icon?: {
type: 'Image';
mediaType: string;
url: string;
};
image?: {
type: 'Image';
mediaType: string;
url: string;
};
publicKey: {
id: string;
owner: string;
publicKeyPem: string;
};
endpoints: {
sharedInbox: string;
};
}
/**
* Convert a Synapsis user to an ActivityPub Actor
*/
export function userToActor(user: User, nodeDomain: string): ActivityPubActor {
const actorUrl = `https://${nodeDomain}/users/${user.handle}`;
const actor: ActivityPubActor = {
'@context': [
ACTIVITY_STREAMS_CONTEXT,
SECURITY_CONTEXT,
{
'manuallyApprovesFollowers': 'as:manuallyApprovesFollowers',
'toot': 'http://joinmastodon.org/ns#',
'featured': {
'@id': 'toot:featured',
'@type': '@id',
},
},
],
id: actorUrl,
type: 'Person',
preferredUsername: user.handle,
name: user.displayName,
summary: user.bio,
url: actorUrl,
inbox: `${actorUrl}/inbox`,
outbox: `${actorUrl}/outbox`,
followers: `${actorUrl}/followers`,
following: `${actorUrl}/following`,
publicKey: {
id: `${actorUrl}#main-key`,
owner: actorUrl,
publicKeyPem: user.publicKey,
},
endpoints: {
sharedInbox: `https://${nodeDomain}/inbox`,
},
};
// Add avatar if present
if (user.avatarUrl) {
actor.icon = {
type: 'Image',
mediaType: 'image/png', // TODO: detect actual type
url: user.avatarUrl,
};
}
// Add header if present
if (user.headerUrl) {
actor.image = {
type: 'Image',
mediaType: 'image/png',
url: user.headerUrl,
};
}
return actor;
}
/**
* Get the actor URL for a user
*/
export function getActorUrl(handle: string, nodeDomain: string): string {
return `https://${nodeDomain}/users/${handle}`;
}
/**
* Get the inbox URL for a user
*/
export function getInboxUrl(handle: string, nodeDomain: string): string {
return `https://${nodeDomain}/users/${handle}/inbox`;
}