pub-relay (#6341)
* pub-relay * relay actorをApplicationにする * Disable koa-compress * Homeはリレーに送らない * Disable debug * UI * cleanupなど
This commit is contained in:
parent
be183206e6
commit
145389768d
27 changed files with 510 additions and 12 deletions
59
src/services/create-system-user.ts
Normal file
59
src/services/create-system-user.ts
Normal 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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
})();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
96
src/services/relay.ts
Normal 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);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue