* pub-relay

* relay actorをApplicationにする

* Disable koa-compress

* Homeはリレーに送らない

* Disable debug

* UI

* cleanupなど
This commit is contained in:
MeiMei 2020-05-10 18:42:31 +09:00 committed by GitHub
parent be183206e6
commit 145389768d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
27 changed files with 510 additions and 12 deletions

View file

@ -0,0 +1,59 @@
import * as bcrypt from 'bcryptjs';
import { v4 as uuid } from 'uuid';
import generateNativeUserToken from '../server/api/common/generate-native-user-token';
import { genRsaKeyPair } from '../misc/gen-key-pair';
import { User } from '../models/entities/user';
import { UserProfile } from '../models/entities/user-profile';
import { getConnection } from 'typeorm';
import { genId } from '../misc/gen-id';
import { UserKeypair } from '../models/entities/user-keypair';
import { UsedUsername } from '../models/entities/used-username';
export async function createSystemUser(username: string) {
const password = uuid();
// Generate hash of password
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(password, salt);
// Generate secret
const secret = generateNativeUserToken();
const keyPair = await genRsaKeyPair(4096);
let account!: User;
// Start transaction
await getConnection().transaction(async transactionalEntityManager => {
account = await transactionalEntityManager.save(new User({
id: genId(),
createdAt: new Date(),
username: username,
usernameLower: username.toLowerCase(),
host: null,
token: secret,
isAdmin: false,
isLocked: true,
isBot: true,
}));
await transactionalEntityManager.save(new UserKeypair({
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
userId: account.id
}));
await transactionalEntityManager.save(new UserProfile({
userId: account.id,
autoAcceptFollowed: false,
password: hash,
}));
await transactionalEntityManager.save(new UsedUsername({
createdAt: new Date(),
username: username.toLowerCase(),
}));
});
return account;
}

View file

@ -9,6 +9,7 @@ import { Notes, UserNotePinings, Users } from '../../models';
import { UserNotePining } from '../../models/entities/user-note-pinings';
import { genId } from '../../misc/gen-id';
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
import { deliverToRelays } from '../relay';
/**
* 稿
@ -87,4 +88,5 @@ export async function deliverPinnedChange(userId: User['id'], noteId: Note['id']
const content = renderActivity(isAddition ? renderAdd(user, target, item) : renderRemove(user, target, item));
deliverToFollowers(user, content);
deliverToRelays(user, content);
}

View file

@ -4,6 +4,7 @@ import { Users } from '../../models';
import { User } from '../../models/entities/user';
import { renderPerson } from '../../remote/activitypub/renderer/person';
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
import { deliverToRelays } from '../relay';
export async function publishToFollowers(userId: User['id']) {
const user = await Users.findOne(userId);
@ -13,5 +14,6 @@ export async function publishToFollowers(userId: User['id']) {
if (Users.isLocalUser(user)) {
const content = renderActivity(renderUpdate(await renderPerson(user), user));
deliverToFollowers(user, content);
deliverToRelays(user, content);
}
}

View file

@ -31,6 +31,7 @@ import { ensure } from '../../prelude/ensure';
import { checkHitAntenna } from '../../misc/check-hit-antenna';
import { addNoteToAntenna } from '../add-note-to-antenna';
import { countSameRenotes } from '../../misc/count-same-renotes';
import { deliverToRelays } from '../relay';
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
@ -349,6 +350,10 @@ export default async (user: User, data: Option, silent = false) => new Promise<N
dm.addFollowersRecipe();
}
if (['public'].includes(note.visibility)) {
deliverToRelays(user, noteActivity);
}
dm.execute();
})();
}

View file

@ -12,6 +12,7 @@ import { Notes, Users, Instances } from '../../models';
import { notesChart, perUserNotesChart, instanceChart } from '../chart';
import { deliverToFollowers } from '../../remote/activitypub/deliver-manager';
import { countSameRenotes } from '../../misc/count-same-renotes';
import { deliverToRelays } from '../relay';
/**
* 稿
@ -48,6 +49,7 @@ export default async function(user: User, note: Note, quiet = false) {
: renderDelete(renderTombstone(`${config.url}/notes/${note.id}`), user));
deliverToFollowers(user, content);
deliverToRelays(user, content);
}
// also deliever delete activity to cascaded notes

View file

@ -4,6 +4,7 @@ import renderNote from '../../../remote/activitypub/renderer/note';
import { Users, Notes } from '../../../models';
import { Note } from '../../../models/entities/note';
import { deliverToFollowers } from '../../../remote/activitypub/deliver-manager';
import { deliverToRelays } from '../../relay';
export async function deliverQuestionUpdate(noteId: Note['id']) {
const note = await Notes.findOne(noteId);
@ -16,5 +17,6 @@ export async function deliverQuestionUpdate(noteId: Note['id']) {
const content = renderActivity(renderUpdate(await renderNote(note, false), user));
deliverToFollowers(user, content);
deliverToRelays(user, content);
}
}

96
src/services/relay.ts Normal file
View file

@ -0,0 +1,96 @@
import { createSystemUser } from './create-system-user';
import { renderFollowRelay } from '../remote/activitypub/renderer/follow-relay';
import { renderActivity, attachLdSignature } from '../remote/activitypub/renderer';
import renderUndo from '../remote/activitypub/renderer/undo';
import { deliver } from '../queue';
import { ILocalUser } from '../models/entities/user';
import { Users, Relays } from '../models';
import { genId } from '../misc/gen-id';
const ACTOR_USERNAME = 'relay.actor' as const;
export async function getRelayActor(): Promise<ILocalUser> {
const user = await Users.findOne({
host: null,
username: ACTOR_USERNAME
});
if (user) return user as ILocalUser;
const created = await createSystemUser(ACTOR_USERNAME);
return created as ILocalUser;
}
export async function addRelay(inbox: string) {
const relay = await Relays.save({
id: genId(),
inbox,
status: 'requesting'
});
const relayActor = await getRelayActor();
const follow = await renderFollowRelay(relay, relayActor);
const activity = renderActivity(follow);
deliver(relayActor, activity, relay.inbox);
return relay;
}
export async function removeRelay(inbox: string) {
const relay = await Relays.findOne({
inbox
});
if (relay == null) {
throw 'relay not found';
}
const relayActor = await getRelayActor();
const follow = renderFollowRelay(relay, relayActor);
const undo = renderUndo(follow, relayActor);
const activity = renderActivity(undo);
deliver(relayActor, activity, relay.inbox);
await Relays.delete(relay.id);
}
export async function listRelay() {
const relays = await Relays.find();
return relays;
}
export async function relayAccepted(id: string) {
const result = await Relays.update(id, {
status: 'accepted'
});
return JSON.stringify(result);
}
export async function relayRejected(id: string) {
const result = await Relays.update(id, {
status: 'rejected'
});
return JSON.stringify(result);
}
export async function deliverToRelays(user: ILocalUser, activity: any) {
if (activity == null) return;
const relays = await Relays.find({
status: 'accepted'
});
if (relays.length === 0) return;
const relayActor = await getRelayActor();
const copy = JSON.parse(JSON.stringify(activity));
if (!copy.to) copy.to = ['https://www.w3.org/ns/activitystreams#Public'];
const signed = await attachLdSignature(copy, user);
for (const relay of relays) {
deliver(relayActor, signed, relay.inbox);
}
}