diff --git a/src/server/announcements.ts b/src/server/announcements.ts new file mode 100644 index 0000000..03c0e7a --- /dev/null +++ b/src/server/announcements.ts @@ -0,0 +1,60 @@ +import { nanoid } from 'nanoid' +import { ActorInfo } from '../schemas' +import ActivityPubSystem, { DEFAULT_PUBLIC_KEY_FIELD } from './apsystem' +import { generateKeypair } from 'http-signed-fetch' + +export class Announcements { + apsystem: ActivityPubSystem + publicURL: string + + constructor (apsystem: ActivityPubSystem, publicURL: string) { + this.apsystem = apsystem + this.publicURL = publicURL + } + + async init (): Promise { + const actorUrl = `${this.publicURL}/v1/announcements` + + try { + const prev = await this.apsystem.store.announcements.getInfo() + if (prev.actorUrl !== actorUrl) { + await this.apsystem.store.announcements.setInfo({ + ...prev, + actorUrl + }) + } + } catch { + const { privateKeyPem, publicKeyPem } = generateKeypair() + await this.apsystem.store.announcements.setInfo({ + actorUrl, + publicKeyId: `${actorUrl}#${DEFAULT_PUBLIC_KEY_FIELD}`, + keypair: { + privateKeyPem, + publicKeyPem + }, + announce: false + }) + } + } + + async announce (actor: string, info: ActorInfo): Promise { + const existedAlready = (await this.apsystem.store.actorsDb.keys().all()).some(k => k === actor) + + if (!existedAlready && info.announce) { + const activity = { + '@context': 'https://www.w3.org/ns/activitystreams', + type: 'Note', + id: `${info.actorUrl}/outbox/${nanoid()}`, + actor: info.actorUrl, + attributedTo: info.actorUrl, + published: new Date().toUTCString(), + to: ['https://www.w3.org/ns/activitystreams#Public'], + cc: ['https://social.distributed.press/v1/announcements/followers'], + // TODO: add a template in config + content: `a wild site appears! ${actor}` + } + await this.apsystem.store.announcements.outbox.add(activity) + await this.apsystem.notifyFollowers('announcements', activity) + } + } +} diff --git a/src/server/api/creation.ts b/src/server/api/creation.ts index 4a495fa..5925454 100644 --- a/src/server/api/creation.ts +++ b/src/server/api/creation.ts @@ -29,8 +29,6 @@ export const creationRoutes = (cfg: APIConfig, store: Store, apsystem: ActivityP }, async (request, reply) => { const { actor } = request.params - const existedAlready = (await store.actorsDb.keys().all()).some(k => k === actor) - const allowed = await apsystem.hasPermissionActorRequest(actor, request, false) if (!allowed) { return await reply.code(403).send('Not Allowed') @@ -38,23 +36,7 @@ export const creationRoutes = (cfg: APIConfig, store: Store, apsystem: ActivityP const info = request.body await store.forActor(actor).setInfo(info) - - if (!existedAlready && info.announce) { - const activity = { - '@context': 'https://www.w3.org/ns/activitystreams', - type: 'Note', - id: `${info.actorUrl}/outbox/${nanoid()}`, - actor: info.actorUrl, - attributedTo: info.actorUrl, - published: new Date().toUTCString(), - to: ['https://www.w3.org/ns/activitystreams#Public'], - cc: ['https://social.distributed.press/v1/announcements/followers'], - // TODO: add a template in config - content: `a wild site appears! ${actor}` - } - await store.announcements.outbox.add(activity) - await apsystem.notifyFollowers(actor, activity) - } + await apsystem.announcements.announce(actor, info) return await reply.send(info) }) diff --git a/src/server/api/index.ts b/src/server/api/index.ts index fc4970a..68fb2e5 100644 --- a/src/server/api/index.ts +++ b/src/server/api/index.ts @@ -96,24 +96,7 @@ async function apiBuilder (cfg: APIConfig): Promise { return 'ok\n' }) - // Setup announcements actor - const actorUrl = `${cfg.publicURL}/v1/announcements` - let keys - try { - const prev = await store.announcements.getInfo() - keys = { ...prev.keypair, publicKeyId: prev.publicKeyId } - } catch { - keys = { ...generateKeypair(), publicKeyId: `${actorUrl}#${DEFAULT_PUBLIC_KEY_FIELD}` } - } - await store.announcements.setInfo({ - actorUrl, - publicKeyId: keys.publicKeyId, - keypair: { - privateKeyPem: keys.privateKeyPem, - publicKeyPem: keys.publicKeyPem - }, - announce: false - }) + await apsystem.announcements.init() await server.register(v1Routes(cfg, store, apsystem, hookSystem), { prefix: '/v1' }) diff --git a/src/server/apsystem.ts b/src/server/apsystem.ts index 69efe2d..3c0c06a 100644 --- a/src/server/apsystem.ts +++ b/src/server/apsystem.ts @@ -15,6 +15,7 @@ import { import type Store from './store/index.js' import { makeSigner } from '../keypair.js' +import { Announcements } from './announcements.js' export const DEFAULT_PUBLIC_KEY_FIELD = 'publicKey' @@ -45,6 +46,7 @@ export default class ActivityPubSystem { modCheck: ModerationChecker fetch: FetchLike hookSystem: HookSystem + announcements: Announcements constructor ( publicURL: string, @@ -58,6 +60,7 @@ export default class ActivityPubSystem { this.modCheck = modCheck this.fetch = fetch this.hookSystem = hookSystem + this.announcements = new Announcements(this, publicURL) } makeURL (path: string): string {