Skip to content

Commit

Permalink
Merge branch 'main' into initial-announce-handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mike182uk committed Jul 31, 2024
2 parents 8f9381b + ec29ebf commit e50bb2a
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 47 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@
"devDependencies": {
"@types/mocha": "10.0.7",
"@types/node": "20.12.12",
"@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
9 changes: 3 additions & 6 deletions src/dispatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ import {
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 @@ -31,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 @@ -270,10 +271,6 @@ export async function followingCounter(
return results.length;
}

type StoredThing = {
object: object | string;
}

export async function outboxDispatcher(
ctx: RequestContext<ContextData>,
handle: string,
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
}
});
});
});
41 changes: 7 additions & 34 deletions src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,18 @@ import {
PUBLIC_COLLECTION
} from '@fedify/fedify';
import { Context, Next } from 'hono';
import ky from 'ky';
import { v4 as uuidv4 } from 'uuid';
import { addToList } from './kv-helpers';
import { toURL } from './toURL';
import { ContextData, HonoContextVariables, fedify } from './app';
import { getSiteSettings } from './ghost';
import type { PersonData } from './user';
import {
ACTOR_DEFAULT_HANDLE,
ACTOR_DEFAULT_ICON,
ACTOR_DEFAULT_NAME,
ACTOR_DEFAULT_SUMMARY
} from './constants';

type GhostSiteSettings = {
site: {
description: string;
icon: string;
title: string;
}
}
import { ACTOR_DEFAULT_HANDLE } from './constants';

type StoredThing = {
object: object | string;
}

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

return {
site: {
description: settings?.site?.description || ACTOR_DEFAULT_SUMMARY,
title: settings?.site?.title || ACTOR_DEFAULT_NAME,
icon: settings?.site?.icon || ACTOR_DEFAULT_ICON
}
};
}

async function postToArticle(ctx: RequestContext<ContextData>, post: any) {
if (!post) {
return {
Expand Down Expand Up @@ -89,7 +62,7 @@ export async function followAction(
db: ctx.get('db'),
globaldb: ctx.get('globaldb'),
});
const actor = await apCtx.getActor('index'); // TODO This should be the actor making the request
const actor = await apCtx.getActor(ACTOR_DEFAULT_HANDLE); // TODO This should be the actor making the request
const followId = apCtx.getObjectUri(Follow, {
id: uuidv4(),
});
Expand All @@ -101,7 +74,7 @@ export async function followAction(
const followJson = await follow.toJsonLd();
ctx.get('globaldb').set([follow.id!.href], followJson);

apCtx.sendActivity({ handle: 'index' }, actorToFollow, follow);
apCtx.sendActivity({ handle: ACTOR_DEFAULT_HANDLE }, actorToFollow, follow);
return new Response(JSON.stringify(followJson), {
headers: {
'Content-Type': 'application/activity+json',
Expand All @@ -125,7 +98,7 @@ export async function postPublishedWebhook(
data?.post?.current,
);
if (article) {
const actor = await apCtx.getActor('index');
const actor = await apCtx.getActor(ACTOR_DEFAULT_HANDLE);
const create = new Create({
actor,
object: article,
Expand All @@ -145,7 +118,7 @@ export async function postPublishedWebhook(
.get('globaldb')
.set([article.id!.href], await article.toJsonLd());
await addToList(ctx.get('db'), ['outbox'], create.id!.href);
await apCtx.sendActivity({ handle: 'index' }, 'followers', create, {
await apCtx.sendActivity({ handle: ACTOR_DEFAULT_HANDLE }, 'followers', create, {
preferSharedInbox: true
});
} catch (err) {
Expand All @@ -168,7 +141,7 @@ export async function siteChangedWebhook(
// Retrieve site settings from Ghost
const host = ctx.req.header('host') || '';

const settings = await getGhostSiteSettings(host);
const settings = await getSiteSettings(host);

// Update the database
const handle = ACTOR_DEFAULT_HANDLE;
Expand Down
15 changes: 10 additions & 5 deletions src/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ import {
Context,
} from '@fedify/fedify';
import { ContextData } from './app';
import {
ACTOR_DEFAULT_ICON,
ACTOR_DEFAULT_NAME,
ACTOR_DEFAULT_SUMMARY
} from './constants';

export type PersonData = {
id: string;
Expand Down Expand Up @@ -49,10 +54,10 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri

const data = {
id: ctx.getActorUri(handle),
name: `Local Ghost site`,
summary: 'This is a summary',
name: ACTOR_DEFAULT_NAME,
summary: ACTOR_DEFAULT_SUMMARY,
preferredUsername: handle,
icon: new Image({ url: new URL('https://ghost.org/favicon.ico') }),
icon: new Image({ url: new URL(ACTOR_DEFAULT_ICON) }),
inbox: ctx.getInboxUri(handle),
outbox: ctx.getOutboxUri(handle),
following: ctx.getFollowingUri(handle),
Expand All @@ -67,14 +72,14 @@ export async function getUserData(ctx: RequestContext<ContextData>, handle: stri
name: data.name,
summary: data.summary,
preferredUsername: data.preferredUsername,
icon: 'https://ghost.org/favicon.ico',
icon: ACTOR_DEFAULT_ICON,
inbox: data.inbox.href,
outbox: data.outbox.href,
following: data.following.href,
followers: data.followers.href,
};

await ctx.data.db.set(['handle', handle], data);
await ctx.data.db.set(['handle', handle], dataToStore);

return data;
}
Expand Down
Loading

0 comments on commit e50bb2a

Please sign in to comment.