Skip to content

Commit

Permalink
Merge branch 'main' into mike-mom-322-sanitize-html-for-notes-content
Browse files Browse the repository at this point in the history
  • Loading branch information
mike182uk committed Jul 31, 2024
2 parents 3327a28 + 0154746 commit 854a0f8
Show file tree
Hide file tree
Showing 9 changed files with 417 additions and 94 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@
"@types/mocha": "10.0.7",
"@types/node": "20.12.12",
"@types/sanitize-html": "^2.11.0",
"@types/sinon": "17.0.3",
"@types/uuid": "10.0.0",
"c8": "10.1.2",
"mocha": "10.5.2",
"sinon": "18.0.0",
"tsx": "4.11.0",
"typescript": "5.4.5"
},
Expand Down
4 changes: 4 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
Organization,
Service,
Update,
Announce,
Context,
} from '@fedify/fedify';
import { federation } from '@fedify/fedify/x/hono';
Expand Down Expand Up @@ -45,6 +46,7 @@ import {
acceptDispatcher,
createDispatcher,
updateDispatcher,
handleAnnounce,
} from './dispatchers';

import { followAction, inboxHandler, postPublishedWebhook, siteChangedWebhook } from './handlers';
Expand Down Expand Up @@ -110,6 +112,8 @@ inboxListener
.on(Accept, ensureCorrectContext(handleAccept))
.onError(inboxErrorHandler)
.on(Create, ensureCorrectContext(handleCreate))
.onError(inboxErrorHandler)
.on(Announce, ensureCorrectContext(handleAnnounce))
.onError(inboxErrorHandler);

fedify
Expand Down
69 changes: 67 additions & 2 deletions src/dispatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ import {
Activity,
Update,
Context,
Announce,
} from '@fedify/fedify';
import { v4 as uuidv4 } from 'uuid';
import { addToList } from './kv-helpers';
import { ContextData } from './app';
import { ACTOR_DEFAULT_HANDLE } from './constants';
import { getUserData, getUserKeypair } from './user';

export async function actorDispatcher(
ctx: RequestContext<ContextData>,
handle: string,
) {
if (handle !== 'index') return null;
if (handle !== ACTOR_DEFAULT_HANDLE) return null;

const data = await getUserData(ctx, handle);

Expand All @@ -30,7 +32,7 @@ export async function actorDispatcher(
}

export async function keypairDispatcher(ctx: Context<ContextData>, handle: string) {
if (handle !== 'index') return [];
if (handle !== ACTOR_DEFAULT_HANDLE) return [];

const data = await getUserKeypair(ctx, handle);

Expand Down Expand Up @@ -136,6 +138,69 @@ export async function handleCreate(
await addToList(ctx.data.db, ['inbox'], create.id.href);
}

export async function handleAnnounce(
ctx: Context<ContextData>,
announce: Announce,
) {
console.log('Handling Announce');

// Validate announce
if (!announce.id) {
console.log('Invalid Announce - no id');
return;
}

if (!announce.objectId) {
console.log('Invalid Announce - no object id');
return;
}

// Validate sender
const sender = await announce.getActor(ctx);

if (sender === null || sender.id === null) {
console.log('Sender missing, exit early');
return;
}

// Lookup announced object - If not found in globalDb, perform network lookup
let object = null;
let existing = await ctx.data.globaldb.get([announce.objectId.href]) ?? null;

if (!existing) {
console.log('Object not found in globalDb, performing network lookup');

object = await lookupObject(announce.objectId);
}

// Validate object
if (!existing && !object) {
console.log('Invalid Announce - could not find object');
return;
}

if (object && !object.id) {
console.log('Invalid Announce - could not find object id');
return;
}

// Persist announce
const announceJson = await announce.toJsonLd();
ctx.data.globaldb.set([announce.id.href], announceJson);

// Persist object if not already persisted
if (!existing && object && object.id) {
console.log('Storing object in globalDb');

const objectJson = await object.toJsonLd();

ctx.data.globaldb.set([object.id.href], objectJson);
}

// Add announce to inbox
await addToList(ctx.data.db, ['inbox'], announce.id.href);
}

export async function inboxErrorHandler(
ctx: Context<ContextData>,
error: unknown,
Expand Down
3 changes: 2 additions & 1 deletion src/dispatchers.unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ import {
createDispatcher,
} from './dispatchers';
import { RequestContext } from '@fedify/fedify';
import { ACTOR_DEFAULT_HANDLE } from './constants';

describe('dispatchers', function () {
describe('actorDispatcher', function () {
it('returns null if the handle is not "index"', async function () {
it(`returns null if the handle is not "${ACTOR_DEFAULT_HANDLE}"`, async function () {
const ctx = {} as RequestContext<any>;
const handle = 'anything';

Expand Down
28 changes: 28 additions & 0 deletions src/ghost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ky from 'ky';
import {
ACTOR_DEFAULT_ICON,
ACTOR_DEFAULT_NAME,
ACTOR_DEFAULT_SUMMARY
} from './constants';

type SiteSettings = {
site: {
description: string;
icon: string;
title: string;
}
}

export async function getSiteSettings(host: string): Promise<SiteSettings> {
const settings = await ky
.get(`https://${host}/ghost/api/admin/site/`)
.json<Partial<SiteSettings>>();

return {
site: {
description: settings?.site?.description || ACTOR_DEFAULT_SUMMARY,
title: settings?.site?.title || ACTOR_DEFAULT_NAME,
icon: settings?.site?.icon || ACTOR_DEFAULT_ICON
}
};
}
122 changes: 122 additions & 0 deletions src/ghost.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import assert from 'assert';
import sinon from 'sinon';
import ky from 'ky';
import { getSiteSettings } from './ghost';
import {
ACTOR_DEFAULT_ICON,
ACTOR_DEFAULT_NAME,
ACTOR_DEFAULT_SUMMARY
} from './constants';

describe('getSiteSettings', function () {
const host = 'example.com';

let kyGetStub: sinon.SinonStub;

beforeEach(function () {
kyGetStub = sinon.stub(ky, 'get');
});

afterEach(function () {
sinon.restore();
});

it('should retrieve settings from Ghost', async function () {
const settings = {
site: {
description: 'foo',
title: 'bar',
icon: 'https://example.com/baz.png'
}
};

kyGetStub.returns({
json: async () => settings
});

const result = await getSiteSettings(host);

assert.deepStrictEqual(result, settings);
assert.strictEqual(kyGetStub.callCount, 1);
assert.strictEqual(kyGetStub.firstCall.args[0], `https://${host}/ghost/api/admin/site/`);
});

it('should use defaults for missing settings', async function () {
let result;

// Missing description
kyGetStub.returns({
json: async () => ({
site: {
title: 'bar',
icon: 'https://example.com/baz.png'
}
})
});

result = await getSiteSettings(host);

assert.deepStrictEqual(result, {
site: {
description: ACTOR_DEFAULT_SUMMARY,
title: 'bar',
icon: 'https://example.com/baz.png'
}
});

// Missing title
kyGetStub.returns({
json: async () => ({
site: {
description: 'foo',
icon: 'https://example.com/baz.png'
}
})
});

result = await getSiteSettings(host);

assert.deepStrictEqual(result, {
site: {
description: 'foo',
title: ACTOR_DEFAULT_NAME,
icon: 'https://example.com/baz.png'
}
});

// Missing icon
kyGetStub.returns({
json: async () => ({
site: {
description: 'foo',
title: 'bar'
}
})
});

result = await getSiteSettings(host);

assert.deepStrictEqual(result, {
site: {
description: 'foo',
title: 'bar',
icon: ACTOR_DEFAULT_ICON
}
});

// Missing everything
kyGetStub.returns({
json: async () => ({})
});

result = await getSiteSettings(host);

assert.deepStrictEqual(result, {
site: {
description: ACTOR_DEFAULT_SUMMARY,
title: ACTOR_DEFAULT_NAME,
icon: ACTOR_DEFAULT_ICON
}
});
});
});
Loading

0 comments on commit 854a0f8

Please sign in to comment.