Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/account/account-updated.event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Account } from './account.entity';

export class AccountUpdatedEvent {
static getName(): string {
return 'account.updated.event';
return 'account.updated';
}

constructor(private readonly account: Account) {}
Expand Down
31 changes: 1 addition & 30 deletions src/account/account.service.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,7 @@ import { AP_BASE_PATH } from '../constants';
import { AccountFollowedEvent } from './account-followed.event';
import { KnexAccountRepository } from './account.repository.knex';
import { AccountService } from './account.service';
import type {
Account,
ExternalAccountData,
InternalAccountData,
Site,
} from './types';
import type { ExternalAccountData, InternalAccountData, Site } from './types';

vi.mock('@fedify/fedify', async () => {
// generateCryptoKeyPair is a slow operation so we generate a key pair
Expand Down Expand Up @@ -715,28 +710,4 @@ describe('AccountService', () => {
expect(isNotFollowing).toBe(false);
});
});

it('should update accounts and emit an account.updated event if they have changed', async () => {
const account = await service.createInternalAccount(site, {
...internalAccountData,
username: 'account',
});

let accountFromEvent: Account | undefined;

events.once('account.updated', (account) => {
accountFromEvent = account;
});

await service.updateAccount(account, {
name: 'A brand new name!',
});

expect(accountFromEvent).toBeDefined();

const newAccount = await service.getByInternalId(account.id);

expect(newAccount).toBeDefined();
expect(newAccount!.name).toBe('A brand new name!');
});
});
27 changes: 0 additions & 27 deletions src/activitypub/fediverse-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Delete, PUBLIC_COLLECTION, Update } from '@fedify/fedify';
import { PostDeletedEvent } from 'post/post-deleted.event';
import { v4 as uuidv4 } from 'uuid';
import { AccountUpdatedEvent } from '../account/account-updated.event';
import type { Account } from '../account/types';
import type { FedifyContextFactory } from './fedify-context.factory';

export class FediverseBridge {
Expand All @@ -13,7 +12,6 @@ export class FediverseBridge {
) {}

async init() {
this.events.on('account.updated', this.handleAccountUpdate.bind(this));
this.events.on(
AccountUpdatedEvent.getName(),
this.handleAccountUpdatedEvent.bind(this),
Expand Down Expand Up @@ -55,31 +53,6 @@ export class FediverseBridge {
);
}

private async handleAccountUpdate(account: Account) {
const ctx = this.fedifyContextFactory.getFedifyContext();

const update = new Update({
id: ctx.getObjectUri(Update, { id: uuidv4() }),
actor: new URL(account.ap_id),
to: PUBLIC_COLLECTION,
object: new URL(account.ap_id),
cc: new URL(account.ap_followers_url),
});

await ctx.data.globaldb.set([update.id!.href], await update.toJsonLd());

await ctx.sendActivity(
{
handle: account.username,
},
'followers',
update,
{
preferSharedInbox: true,
},
);
}

private async handleAccountUpdatedEvent(event: AccountUpdatedEvent) {
const account = event.getAccount();
if (!account.isInternal) {
Expand Down
55 changes: 0 additions & 55 deletions src/activitypub/fediverse-bridge.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import EventEmitter from 'node:events';
import { Account } from 'account/account.entity';
import type { Account as AccountType } from 'account/types';
import { PostDeletedEvent } from 'post/post-deleted.event';
import { Post } from 'post/post.entity';
import { describe, expect, it, vi } from 'vitest';
Expand All @@ -11,60 +10,6 @@ import { FediverseBridge } from './fediverse-bridge';
const nextTick = () => new Promise((resolve) => process.nextTick(resolve));

describe('FediverseBridge', () => {
it('Sends update activities on the account.updated event', async () => {
const events = new EventEmitter();
const context: FedifyContext = {
getObjectUri() {
return new URL('https://mockupdateurl.com');
},
async sendActivity() {},
data: {
globaldb: {
set: vi.fn(),
},
},
} as unknown as FedifyContext;
const fedifyContextFactory = {
getFedifyContext() {
return context;
},
} as FedifyContextFactory;

const bridge = new FediverseBridge(events, fedifyContextFactory);

await bridge.init();

const sendActivity = vi.spyOn(context, 'sendActivity');

const account: AccountType = {
id: 1,
username: 'username',
name: 'Name',
bio: 'Bio',
avatar_url: '',
banner_image_url: '',
url: 'https://account.com',
custom_fields: null,
ap_id: 'https://account.com',
ap_inbox_url: 'https://account.com/inbox',
ap_shared_inbox_url: 'https://account.com/inbox',
ap_outbox_url: 'https://account.com/outbox',
ap_following_url: 'https://account.com/following',
ap_followers_url: 'https://account.com/followers',
ap_liked_url: 'https://account.com/liked',
ap_public_key: '{}',
ap_private_key: '{}',
};

events.emit('account.updated', account);

await nextTick();

expect(sendActivity.mock.lastCall).toBeDefined();

expect(context.data.globaldb.set).toHaveBeenCalledOnce();
});

it('Sends delete activities on the PostDeletedEvent', async () => {
const events = new EventEmitter();
const context: FedifyContext = {
Expand Down
6 changes: 0 additions & 6 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ import {
createSearchHandler,
createUpdateAccountHandler,
handleCreateNote,
handleWebhookSiteChanged,
} from './http/api';
import { AccountFollowsView } from './http/api/views/account.follows.view';
import { createWebFingerHandler } from './http/handler/webfinger';
Expand Down Expand Up @@ -886,11 +885,6 @@ app.post(
createPostPublishedWebhookHandler(accountRepository, postRepository),
),
);
app.post(
'/.ghost/activitypub/webhooks/site/changed',
validateWebhook(),
spanWrapper(handleWebhookSiteChanged(siteService)),
);

function requireRole(...roles: GhostRole[]) {
return function roleMiddleware(ctx: HonoContext, next: Next) {
Expand Down
24 changes: 0 additions & 24 deletions src/http/api/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { Post } from '../../post/post.entity';
import type { KnexPostRepository } from '../../post/post.repository.knex';
import { publishPost } from '../../publishing/helpers';
import { PostVisibility } from '../../publishing/types';
import type { SiteService } from '../../site/site.service';

const PostInputSchema = z.object({
uuid: z.string().uuid(),
Expand Down Expand Up @@ -106,26 +105,3 @@ export function createPostPublishedWebhookHandler(
});
};
}

/**
* Handle a site.changed webhook
*
* @param ctx App context instance
*/
export const handleWebhookSiteChanged = (siteService: SiteService) =>
async function handleWebhookSiteChanged(ctx: AppContext) {
try {
await siteService.refreshSiteDataForHost(ctx.get('site').host);
} catch (err) {
ctx.get('logger').error('Site changed webhook failed: {error}', {
error: err,
});
}

return new Response(JSON.stringify({}), {
headers: {
'Content-Type': 'application/json',
},
status: 200,
});
};
19 changes: 0 additions & 19 deletions src/site/site.service.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { generateTestCryptoKeyPair } from 'test/crypto-key-pair';
import { createTestDb } from 'test/db';
import { KnexAccountRepository } from '../account/account.repository.knex';
import { AccountService } from '../account/account.service';
import type { Account } from '../account/types';
import { FedifyContextFactory } from '../activitypub/fedify-context.factory';
import { type IGhostService, type Site, SiteService } from './site.service';

Expand Down Expand Up @@ -108,22 +107,4 @@ describe('SiteService', () => {

expect(retrievedSite).toMatchObject(site);
});

it('Can update the default account for a host', async () => {
const updateAccount = vi
.spyOn(accountService, 'updateAccount')
.mockResolvedValue({} as unknown as Account);

const site = await service.initialiseSiteForHost('updating.tld');
const account = await accountService.getDefaultAccountForSite(site);

await service.refreshSiteDataForHost('updating.tld');

expect(updateAccount.mock.lastCall?.[0]).toMatchObject(account);
expect(updateAccount.mock.lastCall?.[1]).toMatchObject({
avatar_url: '',
name: 'Default Title',
bio: 'Default Description',
});
});
});
18 changes: 0 additions & 18 deletions src/site/site.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,22 +95,4 @@ export class SiteService {
return newSite;
});
}

public async refreshSiteDataForHost(host: string): Promise<void> {
const site = await this.getSiteByHost(host);
if (!site) {
throw new Error(`Could not find site for ${host}`);
}

const account =
await this.accountService.getDefaultAccountForSite(site);

const settings = await this.ghostService.getSiteSettings(site.host);

await this.accountService.updateAccount(account, {
avatar_url: settings.site.icon,
name: settings.site.title,
bio: settings.site.description,
});
}
}