summaryrefslogtreecommitdiff
path: root/src/remote
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2021-11-12 02:02:25 +0900
commit0e4a111f81cceed275d9bec2695f6e401fb654d8 (patch)
tree40874799472fa07416f17b50a398ac33b7771905 /src/remote
parentupdate deps (diff)
downloadsharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.gz
sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.tar.bz2
sharkey-0e4a111f81cceed275d9bec2695f6e401fb654d8.zip
refactoring
Resolve #7779
Diffstat (limited to 'src/remote')
-rw-r--r--src/remote/activitypub/ap-request.ts104
-rw-r--r--src/remote/activitypub/audience.ts92
-rw-r--r--src/remote/activitypub/db-resolver.ts140
-rw-r--r--src/remote/activitypub/deliver-manager.ts131
-rw-r--r--src/remote/activitypub/kernel/accept/follow.ts29
-rw-r--r--src/remote/activitypub/kernel/accept/index.ts24
-rw-r--r--src/remote/activitypub/kernel/add/index.ts23
-rw-r--r--src/remote/activitypub/kernel/announce/index.ts19
-rw-r--r--src/remote/activitypub/kernel/announce/note.ts67
-rw-r--r--src/remote/activitypub/kernel/block/index.ts22
-rw-r--r--src/remote/activitypub/kernel/create/index.ts43
-rw-r--r--src/remote/activitypub/kernel/create/note.ts44
-rw-r--r--src/remote/activitypub/kernel/delete/actor.ts26
-rw-r--r--src/remote/activitypub/kernel/delete/index.ts49
-rw-r--r--src/remote/activitypub/kernel/delete/note.ts41
-rw-r--r--src/remote/activitypub/kernel/flag/index.ts30
-rw-r--r--src/remote/activitypub/kernel/follow.ts20
-rw-r--r--src/remote/activitypub/kernel/index.ts71
-rw-r--r--src/remote/activitypub/kernel/like.ts21
-rw-r--r--src/remote/activitypub/kernel/move/index.ts0
-rw-r--r--src/remote/activitypub/kernel/read.ts27
-rw-r--r--src/remote/activitypub/kernel/reject/follow.ts29
-rw-r--r--src/remote/activitypub/kernel/reject/index.ts24
-rw-r--r--src/remote/activitypub/kernel/remove/index.ts23
-rw-r--r--src/remote/activitypub/kernel/undo/announce.ts17
-rw-r--r--src/remote/activitypub/kernel/undo/block.ts20
-rw-r--r--src/remote/activitypub/kernel/undo/follow.ts41
-rw-r--r--src/remote/activitypub/kernel/undo/index.ts34
-rw-r--r--src/remote/activitypub/kernel/undo/like.ts21
-rw-r--r--src/remote/activitypub/kernel/update/index.ts34
-rw-r--r--src/remote/activitypub/logger.ts3
-rw-r--r--src/remote/activitypub/misc/contexts.ts526
-rw-r--r--src/remote/activitypub/misc/get-note-html.ts11
-rw-r--r--src/remote/activitypub/misc/html-to-mfm.ts9
-rw-r--r--src/remote/activitypub/misc/ld-signature.ts134
-rw-r--r--src/remote/activitypub/models/icon.ts5
-rw-r--r--src/remote/activitypub/models/identifier.ts5
-rw-r--r--src/remote/activitypub/models/image.ts62
-rw-r--r--src/remote/activitypub/models/mention.ts24
-rw-r--r--src/remote/activitypub/models/note.ts356
-rw-r--r--src/remote/activitypub/models/person.ts494
-rw-r--r--src/remote/activitypub/models/question.ts83
-rw-r--r--src/remote/activitypub/models/tag.ts18
-rw-r--r--src/remote/activitypub/perform.ts7
-rw-r--r--src/remote/activitypub/renderer/accept.ts8
-rw-r--r--src/remote/activitypub/renderer/add.ts9
-rw-r--r--src/remote/activitypub/renderer/announce.ts29
-rw-r--r--src/remote/activitypub/renderer/block.ts8
-rw-r--r--src/remote/activitypub/renderer/create.ts17
-rw-r--r--src/remote/activitypub/renderer/delete.ts9
-rw-r--r--src/remote/activitypub/renderer/document.ts9
-rw-r--r--src/remote/activitypub/renderer/emoji.ts14
-rw-r--r--src/remote/activitypub/renderer/follow-relay.ts14
-rw-r--r--src/remote/activitypub/renderer/follow-user.ts12
-rw-r--r--src/remote/activitypub/renderer/follow.ts15
-rw-r--r--src/remote/activitypub/renderer/hashtag.ts7
-rw-r--r--src/remote/activitypub/renderer/image.ts9
-rw-r--r--src/remote/activitypub/renderer/index.ts59
-rw-r--r--src/remote/activitypub/renderer/key.ts14
-rw-r--r--src/remote/activitypub/renderer/like.ts30
-rw-r--r--src/remote/activitypub/renderer/mention.ts9
-rw-r--r--src/remote/activitypub/renderer/note.ts168
-rw-r--r--src/remote/activitypub/renderer/ordered-collection-page.ts23
-rw-r--r--src/remote/activitypub/renderer/ordered-collection.ts21
-rw-r--r--src/remote/activitypub/renderer/person.ts90
-rw-r--r--src/remote/activitypub/renderer/question.ts23
-rw-r--r--src/remote/activitypub/renderer/read.ts9
-rw-r--r--src/remote/activitypub/renderer/reject.ts8
-rw-r--r--src/remote/activitypub/renderer/remove.ts9
-rw-r--r--src/remote/activitypub/renderer/tombstone.ts4
-rw-r--r--src/remote/activitypub/renderer/undo.ts13
-rw-r--r--src/remote/activitypub/renderer/update.ts15
-rw-r--r--src/remote/activitypub/renderer/vote.ts23
-rw-r--r--src/remote/activitypub/request.ts58
-rw-r--r--src/remote/activitypub/resolver.ts73
-rw-r--r--src/remote/activitypub/type.ts289
-rw-r--r--src/remote/logger.ts3
-rw-r--r--src/remote/resolve-user.ts110
-rw-r--r--src/remote/webfinger.ts34
79 files changed, 0 insertions, 4248 deletions
diff --git a/src/remote/activitypub/ap-request.ts b/src/remote/activitypub/ap-request.ts
deleted file mode 100644
index 76a3857140..0000000000
--- a/src/remote/activitypub/ap-request.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-import * as crypto from 'crypto';
-import { URL } from 'url';
-
-type Request = {
- url: string;
- method: string;
- headers: Record<string, string>;
-};
-
-type PrivateKey = {
- privateKeyPem: string;
- keyId: string;
-};
-
-export function createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }) {
- const u = new URL(args.url);
- const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`;
-
- const request: Request = {
- url: u.href,
- method: 'POST',
- headers: objectAssignWithLcKey({
- 'Date': new Date().toUTCString(),
- 'Host': u.hostname,
- 'Content-Type': 'application/activity+json',
- 'Digest': digestHeader,
- }, args.additionalHeaders),
- };
-
- const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'digest']);
-
- return {
- request,
- signingString: result.signingString,
- signature: result.signature,
- signatureHeader: result.signatureHeader,
- };
-}
-
-export function createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }) {
- const u = new URL(args.url);
-
- const request: Request = {
- url: u.href,
- method: 'GET',
- headers: objectAssignWithLcKey({
- 'Accept': 'application/activity+json, application/ld+json',
- 'Date': new Date().toUTCString(),
- 'Host': new URL(args.url).hostname,
- }, args.additionalHeaders),
- };
-
- const result = signToRequest(request, args.key, ['(request-target)', 'date', 'host', 'accept']);
-
- return {
- request,
- signingString: result.signingString,
- signature: result.signature,
- signatureHeader: result.signatureHeader,
- };
-}
-
-function signToRequest(request: Request, key: PrivateKey, includeHeaders: string[]) {
- const signingString = genSigningString(request, includeHeaders);
- const signature = crypto.sign('sha256', Buffer.from(signingString), key.privateKeyPem).toString('base64');
- const signatureHeader = `keyId="${key.keyId}",algorithm="rsa-sha256",headers="${includeHeaders.join(' ')}",signature="${signature}"`;
-
- request.headers = objectAssignWithLcKey(request.headers, {
- Signature: signatureHeader
- });
-
- return {
- request,
- signingString,
- signature,
- signatureHeader,
- };
-}
-
-function genSigningString(request: Request, includeHeaders: string[]) {
- request.headers = lcObjectKey(request.headers);
-
- const results: string[] = [];
-
- for (const key of includeHeaders.map(x => x.toLowerCase())) {
- if (key === '(request-target)') {
- results.push(`(request-target): ${request.method.toLowerCase()} ${new URL(request.url).pathname}`);
- } else {
- results.push(`${key}: ${request.headers[key]}`);
- }
- }
-
- return results.join('\n');
-}
-
-function lcObjectKey(src: Record<string, string>) {
- const dst: Record<string, string> = {};
- for (const key of Object.keys(src).filter(x => x != '__proto__' && typeof src[x] === 'string')) dst[key.toLowerCase()] = src[key];
- return dst;
-}
-
-function objectAssignWithLcKey(a: Record<string, string>, b: Record<string, string>) {
- return Object.assign(lcObjectKey(a), lcObjectKey(b));
-}
diff --git a/src/remote/activitypub/audience.ts b/src/remote/activitypub/audience.ts
deleted file mode 100644
index 3d2dab1459..0000000000
--- a/src/remote/activitypub/audience.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import { ApObject, getApIds } from './type';
-import Resolver from './resolver';
-import { resolvePerson } from './models/person';
-import { unique, concat } from '@/prelude/array';
-import * as promiseLimit from 'promise-limit';
-import { User, IRemoteUser } from '@/models/entities/user';
-
-type Visibility = 'public' | 'home' | 'followers' | 'specified';
-
-type AudienceInfo = {
- visibility: Visibility,
- mentionedUsers: User[],
- visibleUsers: User[],
-};
-
-export async function parseAudience(actor: IRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
- const toGroups = groupingAudience(getApIds(to), actor);
- const ccGroups = groupingAudience(getApIds(cc), actor);
-
- const others = unique(concat([toGroups.other, ccGroups.other]));
-
- const limit = promiseLimit<User | null>(2);
- const mentionedUsers = (await Promise.all(
- others.map(id => limit(() => resolvePerson(id, resolver).catch(() => null)))
- )).filter((x): x is User => x != null);
-
- if (toGroups.public.length > 0) {
- return {
- visibility: 'public',
- mentionedUsers,
- visibleUsers: []
- };
- }
-
- if (ccGroups.public.length > 0) {
- return {
- visibility: 'home',
- mentionedUsers,
- visibleUsers: []
- };
- }
-
- if (toGroups.followers.length > 0) {
- return {
- visibility: 'followers',
- mentionedUsers,
- visibleUsers: []
- };
- }
-
- return {
- visibility: 'specified',
- mentionedUsers,
- visibleUsers: mentionedUsers
- };
-}
-
-function groupingAudience(ids: string[], actor: IRemoteUser) {
- const groups = {
- public: [] as string[],
- followers: [] as string[],
- other: [] as string[],
- };
-
- for (const id of ids) {
- if (isPublic(id)) {
- groups.public.push(id);
- } else if (isFollowers(id, actor)) {
- groups.followers.push(id);
- } else {
- groups.other.push(id);
- }
- }
-
- groups.other = unique(groups.other);
-
- return groups;
-}
-
-function isPublic(id: string) {
- return [
- 'https://www.w3.org/ns/activitystreams#Public',
- 'as#Public',
- 'Public',
- ].includes(id);
-}
-
-function isFollowers(id: string, actor: IRemoteUser) {
- return (
- id === (actor.followersUri || `${actor.uri}/followers`)
- );
-}
diff --git a/src/remote/activitypub/db-resolver.ts b/src/remote/activitypub/db-resolver.ts
deleted file mode 100644
index 289b6f0ee8..0000000000
--- a/src/remote/activitypub/db-resolver.ts
+++ /dev/null
@@ -1,140 +0,0 @@
-import config from '@/config/index';
-import { Note } from '@/models/entities/note';
-import { User, IRemoteUser } from '@/models/entities/user';
-import { UserPublickey } from '@/models/entities/user-publickey';
-import { MessagingMessage } from '@/models/entities/messaging-message';
-import { Notes, Users, UserPublickeys, MessagingMessages } from '@/models/index';
-import { IObject, getApId } from './type';
-import { resolvePerson } from './models/person';
-import escapeRegexp = require('escape-regexp');
-
-export default class DbResolver {
- constructor() {
- }
-
- /**
- * AP Note => Misskey Note in DB
- */
- public async getNoteFromApId(value: string | IObject): Promise<Note | null> {
- const parsed = this.parseUri(value);
-
- if (parsed.id) {
- return (await Notes.findOne({
- id: parsed.id
- })) || null;
- }
-
- if (parsed.uri) {
- return (await Notes.findOne({
- uri: parsed.uri
- })) || null;
- }
-
- return null;
- }
-
- public async getMessageFromApId(value: string | IObject): Promise<MessagingMessage | null> {
- const parsed = this.parseUri(value);
-
- if (parsed.id) {
- return (await MessagingMessages.findOne({
- id: parsed.id
- })) || null;
- }
-
- if (parsed.uri) {
- return (await MessagingMessages.findOne({
- uri: parsed.uri
- })) || null;
- }
-
- return null;
- }
-
- /**
- * AP Person => Misskey User in DB
- */
- public async getUserFromApId(value: string | IObject): Promise<User | null> {
- const parsed = this.parseUri(value);
-
- if (parsed.id) {
- return (await Users.findOne({
- id: parsed.id
- })) || null;
- }
-
- if (parsed.uri) {
- return (await Users.findOne({
- uri: parsed.uri
- })) || null;
- }
-
- return null;
- }
-
- /**
- * AP KeyId => Misskey User and Key
- */
- public async getAuthUserFromKeyId(keyId: string): Promise<AuthUser | null> {
- const key = await UserPublickeys.findOne({
- keyId
- });
-
- if (key == null) return null;
-
- const user = await Users.findOne(key.userId) as IRemoteUser;
-
- return {
- user,
- key
- };
- }
-
- /**
- * AP Actor id => Misskey User and Key
- */
- public async getAuthUserFromApId(uri: string): Promise<AuthUser | null> {
- const user = await resolvePerson(uri) as IRemoteUser;
-
- if (user == null) return null;
-
- const key = await UserPublickeys.findOne(user.id);
-
- return {
- user,
- key
- };
- }
-
- public parseUri(value: string | IObject): UriParseResult {
- const uri = getApId(value);
-
- const localRegex = new RegExp('^' + escapeRegexp(config.url) + '/' + '(\\w+)' + '/' + '(\\w+)');
- const matchLocal = uri.match(localRegex);
-
- if (matchLocal) {
- return {
- type: matchLocal[1],
- id: matchLocal[2]
- };
- } else {
- return {
- uri
- };
- }
- }
-}
-
-export type AuthUser = {
- user: IRemoteUser;
- key?: UserPublickey;
-};
-
-type UriParseResult = {
- /** id in DB (local object only) */
- id?: string;
- /** uri in DB (remote object only) */
- uri?: string;
- /** hint of type (local object only, ex: notes, users) */
- type?: string
-};
diff --git a/src/remote/activitypub/deliver-manager.ts b/src/remote/activitypub/deliver-manager.ts
deleted file mode 100644
index d37f97a447..0000000000
--- a/src/remote/activitypub/deliver-manager.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { Users, Followings } from '@/models/index';
-import { ILocalUser, IRemoteUser, User } from '@/models/entities/user';
-import { deliver } from '@/queue/index';
-
-//#region types
-interface IRecipe {
- type: string;
-}
-
-interface IFollowersRecipe extends IRecipe {
- type: 'Followers';
-}
-
-interface IDirectRecipe extends IRecipe {
- type: 'Direct';
- to: IRemoteUser;
-}
-
-const isFollowers = (recipe: any): recipe is IFollowersRecipe =>
- recipe.type === 'Followers';
-
-const isDirect = (recipe: any): recipe is IDirectRecipe =>
- recipe.type === 'Direct';
-//#endregion
-
-export default class DeliverManager {
- private actor: { id: User['id']; host: null; };
- private activity: any;
- private recipes: IRecipe[] = [];
-
- /**
- * Constructor
- * @param actor Actor
- * @param activity Activity to deliver
- */
- constructor(actor: { id: User['id']; host: null; }, activity: any) {
- this.actor = actor;
- this.activity = activity;
- }
-
- /**
- * Add recipe for followers deliver
- */
- public addFollowersRecipe() {
- const deliver = {
- type: 'Followers'
- } as IFollowersRecipe;
-
- this.addRecipe(deliver);
- }
-
- /**
- * Add recipe for direct deliver
- * @param to To
- */
- public addDirectRecipe(to: IRemoteUser) {
- const recipe = {
- type: 'Direct',
- to
- } as IDirectRecipe;
-
- this.addRecipe(recipe);
- }
-
- /**
- * Add recipe
- * @param recipe Recipe
- */
- public addRecipe(recipe: IRecipe) {
- this.recipes.push(recipe);
- }
-
- /**
- * Execute delivers
- */
- public async execute() {
- if (!Users.isLocalUser(this.actor)) return;
-
- const inboxes = new Set<string>();
-
- // build inbox list
- for (const recipe of this.recipes) {
- if (isFollowers(recipe)) {
- // followers deliver
- const followers = await Followings.find({
- followeeId: this.actor.id
- });
-
- for (const following of followers) {
- if (Followings.isRemoteFollower(following)) {
- const inbox = following.followerSharedInbox || following.followerInbox;
- inboxes.add(inbox);
- }
- }
- } else if (isDirect(recipe)) {
- // direct deliver
- const inbox = recipe.to.inbox;
- if (inbox) inboxes.add(inbox);
- }
- }
-
- // deliver
- for (const inbox of inboxes) {
- deliver(this.actor, this.activity, inbox);
- }
- }
-}
-
-//#region Utilities
-/**
- * Deliver activity to followers
- * @param activity Activity
- * @param from Followee
- */
-export async function deliverToFollowers(actor: ILocalUser, activity: any) {
- const manager = new DeliverManager(actor, activity);
- manager.addFollowersRecipe();
- await manager.execute();
-}
-
-/**
- * Deliver activity to user
- * @param activity Activity
- * @param to Target user
- */
-export async function deliverToUser(actor: ILocalUser, activity: any, to: IRemoteUser) {
- const manager = new DeliverManager(actor, activity);
- manager.addDirectRecipe(to);
- await manager.execute();
-}
-//#endregion
diff --git a/src/remote/activitypub/kernel/accept/follow.ts b/src/remote/activitypub/kernel/accept/follow.ts
deleted file mode 100644
index 1afb733ab5..0000000000
--- a/src/remote/activitypub/kernel/accept/follow.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import accept from '@/services/following/requests/accept';
-import { IFollow } from '../../type';
-import DbResolver from '../../db-resolver';
-import { relayAccepted } from '@/services/relay';
-
-export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
- // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
-
- const dbResolver = new DbResolver();
- const follower = await dbResolver.getUserFromApId(activity.actor);
-
- if (follower == null) {
- return `skip: follower not found`;
- }
-
- if (follower.host != null) {
- return `skip: follower is not a local user`;
- }
-
- // relay
- const match = activity.id?.match(/follow-relay\/(\w+)/);
- if (match) {
- return await relayAccepted(match[1]);
- }
-
- await accept(actor, follower);
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/accept/index.ts b/src/remote/activitypub/kernel/accept/index.ts
deleted file mode 100644
index 5c6f81b2e3..0000000000
--- a/src/remote/activitypub/kernel/accept/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import Resolver from '../../resolver';
-import { IRemoteUser } from '@/models/entities/user';
-import acceptFollow from './follow';
-import { IAccept, isFollow, getApType } from '../../type';
-import { apLogger } from '../../logger';
-
-const logger = apLogger;
-
-export default async (actor: IRemoteUser, activity: IAccept): Promise<string> => {
- const uri = activity.id || activity;
-
- logger.info(`Accept: ${uri}`);
-
- const resolver = new Resolver();
-
- const object = await resolver.resolve(activity.object).catch(e => {
- logger.error(`Resolution failed: ${e}`);
- throw e;
- });
-
- if (isFollow(object)) return await acceptFollow(actor, object);
-
- return `skip: Unknown Accept type: ${getApType(object)}`;
-};
diff --git a/src/remote/activitypub/kernel/add/index.ts b/src/remote/activitypub/kernel/add/index.ts
deleted file mode 100644
index b33be0cc85..0000000000
--- a/src/remote/activitypub/kernel/add/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { IAdd } from '../../type';
-import { resolveNote } from '../../models/note';
-import { addPinned } from '@/services/i/pin';
-
-export default async (actor: IRemoteUser, activity: IAdd): Promise<void> => {
- if ('actor' in activity && actor.uri !== activity.actor) {
- throw new Error('invalid actor');
- }
-
- if (activity.target == null) {
- throw new Error('target is null');
- }
-
- if (activity.target === actor.featured) {
- const note = await resolveNote(activity.object);
- if (note == null) throw new Error('note not found');
- await addPinned(actor, note.id);
- return;
- }
-
- throw new Error(`unknown target: ${activity.target}`);
-};
diff --git a/src/remote/activitypub/kernel/announce/index.ts b/src/remote/activitypub/kernel/announce/index.ts
deleted file mode 100644
index 581357e577..0000000000
--- a/src/remote/activitypub/kernel/announce/index.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import Resolver from '../../resolver';
-import { IRemoteUser } from '@/models/entities/user';
-import announceNote from './note';
-import { IAnnounce, getApId } from '../../type';
-import { apLogger } from '../../logger';
-
-const logger = apLogger;
-
-export default async (actor: IRemoteUser, activity: IAnnounce): Promise<void> => {
- const uri = getApId(activity);
-
- logger.info(`Announce: ${uri}`);
-
- const resolver = new Resolver();
-
- const targetUri = getApId(activity.object);
-
- announceNote(resolver, actor, activity, targetUri);
-};
diff --git a/src/remote/activitypub/kernel/announce/note.ts b/src/remote/activitypub/kernel/announce/note.ts
deleted file mode 100644
index 5230867f24..0000000000
--- a/src/remote/activitypub/kernel/announce/note.ts
+++ /dev/null
@@ -1,67 +0,0 @@
-import Resolver from '../../resolver';
-import post from '@/services/note/create';
-import { IRemoteUser } from '@/models/entities/user';
-import { IAnnounce, getApId } from '../../type';
-import { fetchNote, resolveNote } from '../../models/note';
-import { apLogger } from '../../logger';
-import { extractDbHost } from '@/misc/convert-host';
-import { fetchMeta } from '@/misc/fetch-meta';
-import { getApLock } from '@/misc/app-lock';
-import { parseAudience } from '../../audience';
-import { StatusError } from '@/misc/fetch';
-
-const logger = apLogger;
-
-/**
- * アナウンスアクティビティを捌きます
- */
-export default async function(resolver: Resolver, actor: IRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
- const uri = getApId(activity);
-
- // アナウンサーが凍結されていたらスキップ
- if (actor.isSuspended) {
- return;
- }
-
- // アナウンス先をブロックしてたら中断
- const meta = await fetchMeta();
- if (meta.blockedHosts.includes(extractDbHost(uri))) return;
-
- const unlock = await getApLock(uri);
-
- try {
- // 既に同じURIを持つものが登録されていないかチェック
- const exist = await fetchNote(uri);
- if (exist) {
- return;
- }
-
- // Announce対象をresolve
- let renote;
- try {
- renote = await resolveNote(targetUri);
- } catch (e) {
- // 対象が4xxならスキップ
- if (e instanceof StatusError && e.isClientError) {
- logger.warn(`Ignored announce target ${targetUri} - ${e.statusCode}`);
- return;
- }
- logger.warn(`Error in announce target ${targetUri} - ${e.statusCode || e}`);
- throw e;
- }
-
- logger.info(`Creating the (Re)Note: ${uri}`);
-
- const activityAudience = await parseAudience(actor, activity.to, activity.cc);
-
- await post(actor, {
- createdAt: activity.published ? new Date(activity.published) : null,
- renote,
- visibility: activityAudience.visibility,
- visibleUsers: activityAudience.visibleUsers,
- uri
- });
- } finally {
- unlock();
- }
-}
diff --git a/src/remote/activitypub/kernel/block/index.ts b/src/remote/activitypub/kernel/block/index.ts
deleted file mode 100644
index 4fd1e07b9b..0000000000
--- a/src/remote/activitypub/kernel/block/index.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { IBlock } from '../../type';
-import block from '@/services/blocking/create';
-import { IRemoteUser } from '@/models/entities/user';
-import DbResolver from '../../db-resolver';
-
-export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
- // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
-
- const dbResolver = new DbResolver();
- const blockee = await dbResolver.getUserFromApId(activity.object);
-
- if (blockee == null) {
- return `skip: blockee not found`;
- }
-
- if (blockee.host != null) {
- return `skip: ブロックしようとしているユーザーはローカルユーザーではありません`;
- }
-
- await block(actor, blockee);
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/create/index.ts b/src/remote/activitypub/kernel/create/index.ts
deleted file mode 100644
index ce039a363b..0000000000
--- a/src/remote/activitypub/kernel/create/index.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import Resolver from '../../resolver';
-import { IRemoteUser } from '@/models/entities/user';
-import createNote from './note';
-import { ICreate, getApId, isPost, getApType } from '../../type';
-import { apLogger } from '../../logger';
-import { toArray, concat, unique } from '@/prelude/array';
-
-const logger = apLogger;
-
-export default async (actor: IRemoteUser, activity: ICreate): Promise<void> => {
- const uri = getApId(activity);
-
- logger.info(`Create: ${uri}`);
-
- // copy audiences between activity <=> object.
- if (typeof activity.object === 'object') {
- const to = unique(concat([toArray(activity.to), toArray(activity.object.to)]));
- const cc = unique(concat([toArray(activity.cc), toArray(activity.object.cc)]));
-
- activity.to = to;
- activity.cc = cc;
- activity.object.to = to;
- activity.object.cc = cc;
- }
-
- // If there is no attributedTo, use Activity actor.
- if (typeof activity.object === 'object' && !activity.object.attributedTo) {
- activity.object.attributedTo = activity.actor;
- }
-
- const resolver = new Resolver();
-
- const object = await resolver.resolve(activity.object).catch(e => {
- logger.error(`Resolution failed: ${e}`);
- throw e;
- });
-
- if (isPost(object)) {
- createNote(resolver, actor, object, false, activity);
- } else {
- logger.warn(`Unknown type: ${getApType(object)}`);
- }
-};
diff --git a/src/remote/activitypub/kernel/create/note.ts b/src/remote/activitypub/kernel/create/note.ts
deleted file mode 100644
index 14e311e4cd..0000000000
--- a/src/remote/activitypub/kernel/create/note.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import Resolver from '../../resolver';
-import { IRemoteUser } from '@/models/entities/user';
-import { createNote, fetchNote } from '../../models/note';
-import { getApId, IObject, ICreate } from '../../type';
-import { getApLock } from '@/misc/app-lock';
-import { extractDbHost } from '@/misc/convert-host';
-import { StatusError } from '@/misc/fetch';
-
-/**
- * 投稿作成アクティビティを捌きます
- */
-export default async function(resolver: Resolver, actor: IRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
- const uri = getApId(note);
-
- if (typeof note === 'object') {
- if (actor.uri !== note.attributedTo) {
- return `skip: actor.uri !== note.attributedTo`;
- }
-
- if (typeof note.id === 'string') {
- if (extractDbHost(actor.uri) !== extractDbHost(note.id)) {
- return `skip: host in actor.uri !== note.id`;
- }
- }
- }
-
- const unlock = await getApLock(uri);
-
- try {
- const exist = await fetchNote(note);
- if (exist) return 'skip: note exists';
-
- await createNote(note, resolver, silent);
- return 'ok';
- } catch (e) {
- if (e instanceof StatusError && e.isClientError) {
- return `skip ${e.statusCode}`;
- } else {
- throw e;
- }
- } finally {
- unlock();
- }
-}
diff --git a/src/remote/activitypub/kernel/delete/actor.ts b/src/remote/activitypub/kernel/delete/actor.ts
deleted file mode 100644
index 502f8d5ab5..0000000000
--- a/src/remote/activitypub/kernel/delete/actor.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { apLogger } from '../../logger';
-import { createDeleteAccountJob } from '@/queue';
-import { IRemoteUser } from '@/models/entities/user';
-import { Users } from '@/models/index';
-
-const logger = apLogger;
-
-export async function deleteActor(actor: IRemoteUser, uri: string): Promise<string> {
- logger.info(`Deleting the Actor: ${uri}`);
-
- if (actor.uri !== uri) {
- return `skip: delete actor ${actor.uri} !== ${uri}`;
- }
-
- if (actor.isDeleted) {
- logger.info(`skip: already deleted`);
- }
-
- const job = await createDeleteAccountJob(actor);
-
- await Users.update(actor.id, {
- isDeleted: true,
- });
-
- return `ok: queued ${job.name} ${job.id}`;
-}
diff --git a/src/remote/activitypub/kernel/delete/index.ts b/src/remote/activitypub/kernel/delete/index.ts
deleted file mode 100644
index 86a452de76..0000000000
--- a/src/remote/activitypub/kernel/delete/index.ts
+++ /dev/null
@@ -1,49 +0,0 @@
-import deleteNote from './note';
-import { IRemoteUser } from '@/models/entities/user';
-import { IDelete, getApId, isTombstone, IObject, validPost, validActor } from '../../type';
-import { toSingle } from '@/prelude/array';
-import { deleteActor } from './actor';
-
-/**
- * 削除アクティビティを捌きます
- */
-export default async (actor: IRemoteUser, activity: IDelete): Promise<string> => {
- if ('actor' in activity && actor.uri !== activity.actor) {
- throw new Error('invalid actor');
- }
-
- // 削除対象objectのtype
- let formarType: string | undefined;
-
- if (typeof activity.object === 'string') {
- // typeが不明だけど、どうせ消えてるのでremote resolveしない
- formarType = undefined;
- } else {
- const object = activity.object as IObject;
- if (isTombstone(object)) {
- formarType = toSingle(object.formerType);
- } else {
- formarType = toSingle(object.type);
- }
- }
-
- const uri = getApId(activity.object);
-
- // type不明でもactorとobjectが同じならばそれはPersonに違いない
- if (!formarType && actor.uri === uri) {
- formarType = 'Person';
- }
-
- // それでもなかったらおそらくNote
- if (!formarType) {
- formarType = 'Note';
- }
-
- if (validPost.includes(formarType)) {
- return await deleteNote(actor, uri);
- } else if (validActor.includes(formarType)) {
- return await deleteActor(actor, uri);
- } else {
- return `Unknown type ${formarType}`;
- }
-};
diff --git a/src/remote/activitypub/kernel/delete/note.ts b/src/remote/activitypub/kernel/delete/note.ts
deleted file mode 100644
index 3875a33d13..0000000000
--- a/src/remote/activitypub/kernel/delete/note.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import deleteNode from '@/services/note/delete';
-import { apLogger } from '../../logger';
-import DbResolver from '../../db-resolver';
-import { getApLock } from '@/misc/app-lock';
-import { deleteMessage } from '@/services/messages/delete';
-
-const logger = apLogger;
-
-export default async function(actor: IRemoteUser, uri: string): Promise<string> {
- logger.info(`Deleting the Note: ${uri}`);
-
- const unlock = await getApLock(uri);
-
- try {
- const dbResolver = new DbResolver();
- const note = await dbResolver.getNoteFromApId(uri);
-
- if (note == null) {
- const message = await dbResolver.getMessageFromApId(uri);
- if (message == null) return 'message not found';
-
- if (message.userId !== actor.id) {
- return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
- }
-
- await deleteMessage(message);
-
- return 'ok: message deleted';
- }
-
- if (note.userId !== actor.id) {
- return '投稿を削除しようとしているユーザーは投稿の作成者ではありません';
- }
-
- await deleteNode(actor, note);
- return 'ok: note deleted';
- } finally {
- unlock();
- }
-}
diff --git a/src/remote/activitypub/kernel/flag/index.ts b/src/remote/activitypub/kernel/flag/index.ts
deleted file mode 100644
index 7abfd694cd..0000000000
--- a/src/remote/activitypub/kernel/flag/index.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import config from '@/config/index';
-import { IFlag, getApIds } from '../../type';
-import { AbuseUserReports, Users } from '@/models/index';
-import { In } from 'typeorm';
-import { genId } from '@/misc/gen-id';
-
-export default async (actor: IRemoteUser, activity: IFlag): Promise<string> => {
- // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
- // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
- const uris = getApIds(activity.object);
-
- const userIds = uris.filter(uri => uri.startsWith(config.url + '/users/')).map(uri => uri.split('/').pop());
- const users = await Users.find({
- id: In(userIds)
- });
- if (users.length < 1) return `skip`;
-
- await AbuseUserReports.insert({
- id: genId(),
- createdAt: new Date(),
- targetUserId: users[0].id,
- targetUserHost: users[0].host,
- reporterId: actor.id,
- reporterHost: actor.host,
- comment: `${activity.content}\n${JSON.stringify(uris, null, 2)}`
- });
-
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/follow.ts b/src/remote/activitypub/kernel/follow.ts
deleted file mode 100644
index 3183207afa..0000000000
--- a/src/remote/activitypub/kernel/follow.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import follow from '@/services/following/create';
-import { IFollow } from '../type';
-import DbResolver from '../db-resolver';
-
-export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
- const dbResolver = new DbResolver();
- const followee = await dbResolver.getUserFromApId(activity.object);
-
- if (followee == null) {
- return `skip: followee not found`;
- }
-
- if (followee.host != null) {
- return `skip: フォローしようとしているユーザーはローカルユーザーではありません`;
- }
-
- await follow(actor, followee, activity.id);
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/index.ts b/src/remote/activitypub/kernel/index.ts
deleted file mode 100644
index 20df28eec6..0000000000
--- a/src/remote/activitypub/kernel/index.ts
+++ /dev/null
@@ -1,71 +0,0 @@
-import { IObject, isCreate, isDelete, isUpdate, isRead, isFollow, isAccept, isReject, isAdd, isRemove, isAnnounce, isLike, isUndo, isBlock, isCollectionOrOrderedCollection, isCollection, isFlag } from '../type';
-import { IRemoteUser } from '@/models/entities/user';
-import create from './create/index';
-import performDeleteActivity from './delete/index';
-import performUpdateActivity from './update/index';
-import { performReadActivity } from './read';
-import follow from './follow';
-import undo from './undo/index';
-import like from './like';
-import announce from './announce/index';
-import accept from './accept/index';
-import reject from './reject/index';
-import add from './add/index';
-import remove from './remove/index';
-import block from './block/index';
-import flag from './flag/index';
-import { apLogger } from '../logger';
-import Resolver from '../resolver';
-import { toArray } from '@/prelude/array';
-
-export async function performActivity(actor: IRemoteUser, activity: IObject) {
- if (isCollectionOrOrderedCollection(activity)) {
- const resolver = new Resolver();
- for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
- const act = await resolver.resolve(item);
- try {
- await performOneActivity(actor, act);
- } catch (e) {
- apLogger.error(e);
- }
- }
- } else {
- await performOneActivity(actor, activity);
- }
-}
-
-async function performOneActivity(actor: IRemoteUser, activity: IObject): Promise<void> {
- if (actor.isSuspended) return;
-
- if (isCreate(activity)) {
- await create(actor, activity);
- } else if (isDelete(activity)) {
- await performDeleteActivity(actor, activity);
- } else if (isUpdate(activity)) {
- await performUpdateActivity(actor, activity);
- } else if (isRead(activity)) {
- await performReadActivity(actor, activity);
- } else if (isFollow(activity)) {
- await follow(actor, activity);
- } else if (isAccept(activity)) {
- await accept(actor, activity);
- } else if (isReject(activity)) {
- await reject(actor, activity);
- } else if (isAdd(activity)) {
- await add(actor, activity).catch(err => apLogger.error(err));
- } else if (isRemove(activity)) {
- await remove(actor, activity).catch(err => apLogger.error(err));
- } else if (isAnnounce(activity)) {
- await announce(actor, activity);
- } else if (isLike(activity)) {
- await like(actor, activity);
- } else if (isUndo(activity)) {
- await undo(actor, activity);
- } else if (isBlock(activity)) {
- await block(actor, activity);
- } else if (isFlag(activity)) {
- await flag(actor, activity);
- } else {
- apLogger.warn(`unrecognized activity type: ${(activity as any).type}`);
- }
-}
diff --git a/src/remote/activitypub/kernel/like.ts b/src/remote/activitypub/kernel/like.ts
deleted file mode 100644
index 58d5aefefc..0000000000
--- a/src/remote/activitypub/kernel/like.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { ILike, getApId } from '../type';
-import create from '@/services/note/reaction/create';
-import { fetchNote, extractEmojis } from '../models/note';
-
-export default async (actor: IRemoteUser, activity: ILike) => {
- const targetUri = getApId(activity.object);
-
- const note = await fetchNote(targetUri);
- if (!note) return `skip: target note not found ${targetUri}`;
-
- await extractEmojis(activity.tag || [], actor.host).catch(() => null);
-
- return await create(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => {
- if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') {
- return 'skip: already reacted';
- } else {
- throw e;
- }
- }).then(() => 'ok');
-};
diff --git a/src/remote/activitypub/kernel/move/index.ts b/src/remote/activitypub/kernel/move/index.ts
deleted file mode 100644
index e69de29bb2..0000000000
--- a/src/remote/activitypub/kernel/move/index.ts
+++ /dev/null
diff --git a/src/remote/activitypub/kernel/read.ts b/src/remote/activitypub/kernel/read.ts
deleted file mode 100644
index 11a1731869..0000000000
--- a/src/remote/activitypub/kernel/read.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { IRead, getApId } from '../type';
-import { isSelfHost, extractDbHost } from '@/misc/convert-host';
-import { MessagingMessages } from '@/models/index';
-import { readUserMessagingMessage } from '../../../server/api/common/read-messaging-message';
-
-export const performReadActivity = async (actor: IRemoteUser, activity: IRead): Promise<string> => {
- const id = await getApId(activity.object);
-
- if (!isSelfHost(extractDbHost(id))) {
- return `skip: Read to foreign host (${id})`;
- }
-
- const messageId = id.split('/').pop();
-
- const message = await MessagingMessages.findOne(messageId);
- if (message == null) {
- return `skip: message not found`;
- }
-
- if (actor.id != message.recipientId) {
- return `skip: actor is not a message recipient`;
- }
-
- await readUserMessagingMessage(message.recipientId!, message.userId, [message.id]);
- return `ok: mark as read (${message.userId} => ${message.recipientId} ${message.id})`;
-};
diff --git a/src/remote/activitypub/kernel/reject/follow.ts b/src/remote/activitypub/kernel/reject/follow.ts
deleted file mode 100644
index 356547440f..0000000000
--- a/src/remote/activitypub/kernel/reject/follow.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import reject from '@/services/following/requests/reject';
-import { IFollow } from '../../type';
-import DbResolver from '../../db-resolver';
-import { relayRejected } from '@/services/relay';
-
-export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
- // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
-
- const dbResolver = new DbResolver();
- const follower = await dbResolver.getUserFromApId(activity.actor);
-
- if (follower == null) {
- return `skip: follower not found`;
- }
-
- if (follower.host != null) {
- return `skip: follower is not a local user`;
- }
-
- // relay
- const match = activity.id?.match(/follow-relay\/(\w+)/);
- if (match) {
- return await relayRejected(match[1]);
- }
-
- await reject(actor, follower);
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/reject/index.ts b/src/remote/activitypub/kernel/reject/index.ts
deleted file mode 100644
index d0de9c329b..0000000000
--- a/src/remote/activitypub/kernel/reject/index.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import Resolver from '../../resolver';
-import { IRemoteUser } from '@/models/entities/user';
-import rejectFollow from './follow';
-import { IReject, isFollow, getApType } from '../../type';
-import { apLogger } from '../../logger';
-
-const logger = apLogger;
-
-export default async (actor: IRemoteUser, activity: IReject): Promise<string> => {
- const uri = activity.id || activity;
-
- logger.info(`Reject: ${uri}`);
-
- const resolver = new Resolver();
-
- const object = await resolver.resolve(activity.object).catch(e => {
- logger.error(`Resolution failed: ${e}`);
- throw e;
- });
-
- if (isFollow(object)) return await rejectFollow(actor, object);
-
- return `skip: Unknown Reject type: ${getApType(object)}`;
-};
diff --git a/src/remote/activitypub/kernel/remove/index.ts b/src/remote/activitypub/kernel/remove/index.ts
deleted file mode 100644
index d59953e653..0000000000
--- a/src/remote/activitypub/kernel/remove/index.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { IRemove } from '../../type';
-import { resolveNote } from '../../models/note';
-import { removePinned } from '@/services/i/pin';
-
-export default async (actor: IRemoteUser, activity: IRemove): Promise<void> => {
- if ('actor' in activity && actor.uri !== activity.actor) {
- throw new Error('invalid actor');
- }
-
- if (activity.target == null) {
- throw new Error('target is null');
- }
-
- if (activity.target === actor.featured) {
- const note = await resolveNote(activity.object);
- if (note == null) throw new Error('note not found');
- await removePinned(actor, note.id);
- return;
- }
-
- throw new Error(`unknown target: ${activity.target}`);
-};
diff --git a/src/remote/activitypub/kernel/undo/announce.ts b/src/remote/activitypub/kernel/undo/announce.ts
deleted file mode 100644
index 7bb9d7fcad..0000000000
--- a/src/remote/activitypub/kernel/undo/announce.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Notes } from '@/models/index';
-import { IRemoteUser } from '@/models/entities/user';
-import { IAnnounce, getApId } from '../../type';
-import deleteNote from '@/services/note/delete';
-
-export const undoAnnounce = async (actor: IRemoteUser, activity: IAnnounce): Promise<string> => {
- const uri = getApId(activity);
-
- const note = await Notes.findOne({
- uri
- });
-
- if (!note) return 'skip: no such Announce';
-
- await deleteNote(actor, note);
- return 'ok: deleted';
-};
diff --git a/src/remote/activitypub/kernel/undo/block.ts b/src/remote/activitypub/kernel/undo/block.ts
deleted file mode 100644
index 61940486be..0000000000
--- a/src/remote/activitypub/kernel/undo/block.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { IBlock } from '../../type';
-import unblock from '@/services/blocking/delete';
-import { IRemoteUser } from '@/models/entities/user';
-import DbResolver from '../../db-resolver';
-
-export default async (actor: IRemoteUser, activity: IBlock): Promise<string> => {
- const dbResolver = new DbResolver();
- const blockee = await dbResolver.getUserFromApId(activity.object);
-
- if (blockee == null) {
- return `skip: blockee not found`;
- }
-
- if (blockee.host != null) {
- return `skip: ブロック解除しようとしているユーザーはローカルユーザーではありません`;
- }
-
- await unblock(actor, blockee);
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/undo/follow.ts b/src/remote/activitypub/kernel/undo/follow.ts
deleted file mode 100644
index d85c7e4a71..0000000000
--- a/src/remote/activitypub/kernel/undo/follow.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import unfollow from '@/services/following/delete';
-import cancelRequest from '@/services/following/requests/cancel';
-import { IFollow } from '../../type';
-import { IRemoteUser } from '@/models/entities/user';
-import { FollowRequests, Followings } from '@/models/index';
-import DbResolver from '../../db-resolver';
-
-export default async (actor: IRemoteUser, activity: IFollow): Promise<string> => {
- const dbResolver = new DbResolver();
-
- const followee = await dbResolver.getUserFromApId(activity.object);
- if (followee == null) {
- return `skip: followee not found`;
- }
-
- if (followee.host != null) {
- return `skip: フォロー解除しようとしているユーザーはローカルユーザーではありません`;
- }
-
- const req = await FollowRequests.findOne({
- followerId: actor.id,
- followeeId: followee.id
- });
-
- const following = await Followings.findOne({
- followerId: actor.id,
- followeeId: followee.id
- });
-
- if (req) {
- await cancelRequest(followee, actor);
- return `ok: follow request canceled`;
- }
-
- if (following) {
- await unfollow(actor, followee);
- return `ok: unfollowed`;
- }
-
- return `skip: リクエストもフォローもされていない`;
-};
diff --git a/src/remote/activitypub/kernel/undo/index.ts b/src/remote/activitypub/kernel/undo/index.ts
deleted file mode 100644
index 14b1add152..0000000000
--- a/src/remote/activitypub/kernel/undo/index.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { IUndo, isFollow, isBlock, isLike, isAnnounce, getApType } from '../../type';
-import unfollow from './follow';
-import unblock from './block';
-import undoLike from './like';
-import { undoAnnounce } from './announce';
-import Resolver from '../../resolver';
-import { apLogger } from '../../logger';
-
-const logger = apLogger;
-
-export default async (actor: IRemoteUser, activity: IUndo): Promise<string> => {
- if ('actor' in activity && actor.uri !== activity.actor) {
- throw new Error('invalid actor');
- }
-
- const uri = activity.id || activity;
-
- logger.info(`Undo: ${uri}`);
-
- const resolver = new Resolver();
-
- const object = await resolver.resolve(activity.object).catch(e => {
- logger.error(`Resolution failed: ${e}`);
- throw e;
- });
-
- if (isFollow(object)) return await unfollow(actor, object);
- if (isBlock(object)) return await unblock(actor, object);
- if (isLike(object)) return await undoLike(actor, object);
- if (isAnnounce(object)) return await undoAnnounce(actor, object);
-
- return `skip: unknown object type ${getApType(object)}`;
-};
diff --git a/src/remote/activitypub/kernel/undo/like.ts b/src/remote/activitypub/kernel/undo/like.ts
deleted file mode 100644
index 107d3053e3..0000000000
--- a/src/remote/activitypub/kernel/undo/like.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { ILike, getApId } from '../../type';
-import deleteReaction from '@/services/note/reaction/delete';
-import { fetchNote } from '../../models/note';
-
-/**
- * Process Undo.Like activity
- */
-export default async (actor: IRemoteUser, activity: ILike) => {
- const targetUri = getApId(activity.object);
-
- const note = await fetchNote(targetUri);
- if (!note) return `skip: target note not found ${targetUri}`;
-
- await deleteReaction(actor, note).catch(e => {
- if (e.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') return;
- throw e;
- });
-
- return `ok`;
-};
diff --git a/src/remote/activitypub/kernel/update/index.ts b/src/remote/activitypub/kernel/update/index.ts
deleted file mode 100644
index 52bfc5002e..0000000000
--- a/src/remote/activitypub/kernel/update/index.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { IRemoteUser } from '@/models/entities/user';
-import { getApType, IUpdate, isActor } from '../../type';
-import { apLogger } from '../../logger';
-import { updateQuestion } from '../../models/question';
-import Resolver from '../../resolver';
-import { updatePerson } from '../../models/person';
-
-/**
- * Updateアクティビティを捌きます
- */
-export default async (actor: IRemoteUser, activity: IUpdate): Promise<string> => {
- if ('actor' in activity && actor.uri !== activity.actor) {
- return `skip: invalid actor`;
- }
-
- apLogger.debug('Update');
-
- const resolver = new Resolver();
-
- const object = await resolver.resolve(activity.object).catch(e => {
- apLogger.error(`Resolution failed: ${e}`);
- throw e;
- });
-
- if (isActor(object)) {
- await updatePerson(actor.uri!, resolver, object);
- return `ok: Person updated`;
- } else if (getApType(object) === 'Question') {
- await updateQuestion(object).catch(e => console.log(e));
- return `ok: Question updated`;
- } else {
- return `skip: Unknown type: ${getApType(object)}`;
- }
-};
diff --git a/src/remote/activitypub/logger.ts b/src/remote/activitypub/logger.ts
deleted file mode 100644
index e13add01db..0000000000
--- a/src/remote/activitypub/logger.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import { remoteLogger } from '../logger';
-
-export const apLogger = remoteLogger.createSubLogger('ap', 'magenta');
diff --git a/src/remote/activitypub/misc/contexts.ts b/src/remote/activitypub/misc/contexts.ts
deleted file mode 100644
index 1426ba15f5..0000000000
--- a/src/remote/activitypub/misc/contexts.ts
+++ /dev/null
@@ -1,526 +0,0 @@
-/* tslint:disable:quotemark indent */
-const id_v1 = {
- "@context": {
- "id": "@id",
- "type": "@type",
-
- "cred": "https://w3id.org/credentials#",
- "dc": "http://purl.org/dc/terms/",
- "identity": "https://w3id.org/identity#",
- "perm": "https://w3id.org/permissions#",
- "ps": "https://w3id.org/payswarm#",
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
- "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
- "sec": "https://w3id.org/security#",
- "schema": "http://schema.org/",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
-
- "Group": "https://www.w3.org/ns/activitystreams#Group",
-
- "claim": {"@id": "cred:claim", "@type": "@id"},
- "credential": {"@id": "cred:credential", "@type": "@id"},
- "issued": {"@id": "cred:issued", "@type": "xsd:dateTime"},
- "issuer": {"@id": "cred:issuer", "@type": "@id"},
- "recipient": {"@id": "cred:recipient", "@type": "@id"},
- "Credential": "cred:Credential",
- "CryptographicKeyCredential": "cred:CryptographicKeyCredential",
-
- "about": {"@id": "schema:about", "@type": "@id"},
- "address": {"@id": "schema:address", "@type": "@id"},
- "addressCountry": "schema:addressCountry",
- "addressLocality": "schema:addressLocality",
- "addressRegion": "schema:addressRegion",
- "comment": "rdfs:comment",
- "created": {"@id": "dc:created", "@type": "xsd:dateTime"},
- "creator": {"@id": "dc:creator", "@type": "@id"},
- "description": "schema:description",
- "email": "schema:email",
- "familyName": "schema:familyName",
- "givenName": "schema:givenName",
- "image": {"@id": "schema:image", "@type": "@id"},
- "label": "rdfs:label",
- "name": "schema:name",
- "postalCode": "schema:postalCode",
- "streetAddress": "schema:streetAddress",
- "title": "dc:title",
- "url": {"@id": "schema:url", "@type": "@id"},
- "Person": "schema:Person",
- "PostalAddress": "schema:PostalAddress",
- "Organization": "schema:Organization",
-
- "identityService": {"@id": "identity:identityService", "@type": "@id"},
- "idp": {"@id": "identity:idp", "@type": "@id"},
- "Identity": "identity:Identity",
-
- "paymentProcessor": "ps:processor",
- "preferences": {"@id": "ps:preferences", "@type": "@vocab"},
-
- "cipherAlgorithm": "sec:cipherAlgorithm",
- "cipherData": "sec:cipherData",
- "cipherKey": "sec:cipherKey",
- "digestAlgorithm": "sec:digestAlgorithm",
- "digestValue": "sec:digestValue",
- "domain": "sec:domain",
- "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
- "initializationVector": "sec:initializationVector",
- "member": {"@id": "schema:member", "@type": "@id"},
- "memberOf": {"@id": "schema:memberOf", "@type": "@id"},
- "nonce": "sec:nonce",
- "normalizationAlgorithm": "sec:normalizationAlgorithm",
- "owner": {"@id": "sec:owner", "@type": "@id"},
- "password": "sec:password",
- "privateKey": {"@id": "sec:privateKey", "@type": "@id"},
- "privateKeyPem": "sec:privateKeyPem",
- "publicKey": {"@id": "sec:publicKey", "@type": "@id"},
- "publicKeyPem": "sec:publicKeyPem",
- "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
- "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
- "signature": "sec:signature",
- "signatureAlgorithm": "sec:signatureAlgorithm",
- "signatureValue": "sec:signatureValue",
- "CryptographicKey": "sec:Key",
- "EncryptedMessage": "sec:EncryptedMessage",
- "GraphSignature2012": "sec:GraphSignature2012",
- "LinkedDataSignature2015": "sec:LinkedDataSignature2015",
-
- "accessControl": {"@id": "perm:accessControl", "@type": "@id"},
- "writePermission": {"@id": "perm:writePermission", "@type": "@id"}
- }
-};
-
-const security_v1 = {
- "@context": {
- "id": "@id",
- "type": "@type",
-
- "dc": "http://purl.org/dc/terms/",
- "sec": "https://w3id.org/security#",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
-
- "EcdsaKoblitzSignature2016": "sec:EcdsaKoblitzSignature2016",
- "Ed25519Signature2018": "sec:Ed25519Signature2018",
- "EncryptedMessage": "sec:EncryptedMessage",
- "GraphSignature2012": "sec:GraphSignature2012",
- "LinkedDataSignature2015": "sec:LinkedDataSignature2015",
- "LinkedDataSignature2016": "sec:LinkedDataSignature2016",
- "CryptographicKey": "sec:Key",
-
- "authenticationTag": "sec:authenticationTag",
- "canonicalizationAlgorithm": "sec:canonicalizationAlgorithm",
- "cipherAlgorithm": "sec:cipherAlgorithm",
- "cipherData": "sec:cipherData",
- "cipherKey": "sec:cipherKey",
- "created": {"@id": "dc:created", "@type": "xsd:dateTime"},
- "creator": {"@id": "dc:creator", "@type": "@id"},
- "digestAlgorithm": "sec:digestAlgorithm",
- "digestValue": "sec:digestValue",
- "domain": "sec:domain",
- "encryptionKey": "sec:encryptionKey",
- "expiration": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
- "expires": {"@id": "sec:expiration", "@type": "xsd:dateTime"},
- "initializationVector": "sec:initializationVector",
- "iterationCount": "sec:iterationCount",
- "nonce": "sec:nonce",
- "normalizationAlgorithm": "sec:normalizationAlgorithm",
- "owner": {"@id": "sec:owner", "@type": "@id"},
- "password": "sec:password",
- "privateKey": {"@id": "sec:privateKey", "@type": "@id"},
- "privateKeyPem": "sec:privateKeyPem",
- "publicKey": {"@id": "sec:publicKey", "@type": "@id"},
- "publicKeyBase58": "sec:publicKeyBase58",
- "publicKeyPem": "sec:publicKeyPem",
- "publicKeyWif": "sec:publicKeyWif",
- "publicKeyService": {"@id": "sec:publicKeyService", "@type": "@id"},
- "revoked": {"@id": "sec:revoked", "@type": "xsd:dateTime"},
- "salt": "sec:salt",
- "signature": "sec:signature",
- "signatureAlgorithm": "sec:signingAlgorithm",
- "signatureValue": "sec:signatureValue"
- }
-};
-
-const activitystreams = {
- "@context": {
- "@vocab": "_:",
- "xsd": "http://www.w3.org/2001/XMLSchema#",
- "as": "https://www.w3.org/ns/activitystreams#",
- "ldp": "http://www.w3.org/ns/ldp#",
- "vcard": "http://www.w3.org/2006/vcard/ns#",
- "id": "@id",
- "type": "@type",
- "Accept": "as:Accept",
- "Activity": "as:Activity",
- "IntransitiveActivity": "as:IntransitiveActivity",
- "Add": "as:Add",
- "Announce": "as:Announce",
- "Application": "as:Application",
- "Arrive": "as:Arrive",
- "Article": "as:Article",
- "Audio": "as:Audio",
- "Block": "as:Block",
- "Collection": "as:Collection",
- "CollectionPage": "as:CollectionPage",
- "Relationship": "as:Relationship",
- "Create": "as:Create",
- "Delete": "as:Delete",
- "Dislike": "as:Dislike",
- "Document": "as:Document",
- "Event": "as:Event",
- "Follow": "as:Follow",
- "Flag": "as:Flag",
- "Group": "as:Group",
- "Ignore": "as:Ignore",
- "Image": "as:Image",
- "Invite": "as:Invite",
- "Join": "as:Join",
- "Leave": "as:Leave",
- "Like": "as:Like",
- "Link": "as:Link",
- "Mention": "as:Mention",
- "Note": "as:Note",
- "Object": "as:Object",
- "Offer": "as:Offer",
- "OrderedCollection": "as:OrderedCollection",
- "OrderedCollectionPage": "as:OrderedCollectionPage",
- "Organization": "as:Organization",
- "Page": "as:Page",
- "Person": "as:Person",
- "Place": "as:Place",
- "Profile": "as:Profile",
- "Question": "as:Question",
- "Reject": "as:Reject",
- "Remove": "as:Remove",
- "Service": "as:Service",
- "TentativeAccept": "as:TentativeAccept",
- "TentativeReject": "as:TentativeReject",
- "Tombstone": "as:Tombstone",
- "Undo": "as:Undo",
- "Update": "as:Update",
- "Video": "as:Video",
- "View": "as:View",
- "Listen": "as:Listen",
- "Read": "as:Read",
- "Move": "as:Move",
- "Travel": "as:Travel",
- "IsFollowing": "as:IsFollowing",
- "IsFollowedBy": "as:IsFollowedBy",
- "IsContact": "as:IsContact",
- "IsMember": "as:IsMember",
- "subject": {
- "@id": "as:subject",
- "@type": "@id"
- },
- "relationship": {
- "@id": "as:relationship",
- "@type": "@id"
- },
- "actor": {
- "@id": "as:actor",
- "@type": "@id"
- },
- "attributedTo": {
- "@id": "as:attributedTo",
- "@type": "@id"
- },
- "attachment": {
- "@id": "as:attachment",
- "@type": "@id"
- },
- "bcc": {
- "@id": "as:bcc",
- "@type": "@id"
- },
- "bto": {
- "@id": "as:bto",
- "@type": "@id"
- },
- "cc": {
- "@id": "as:cc",
- "@type": "@id"
- },
- "context": {
- "@id": "as:context",
- "@type": "@id"
- },
- "current": {
- "@id": "as:current",
- "@type": "@id"
- },
- "first": {
- "@id": "as:first",
- "@type": "@id"
- },
- "generator": {
- "@id": "as:generator",
- "@type": "@id"
- },
- "icon": {
- "@id": "as:icon",
- "@type": "@id"
- },
- "image": {
- "@id": "as:image",
- "@type": "@id"
- },
- "inReplyTo": {
- "@id": "as:inReplyTo",
- "@type": "@id"
- },
- "items": {
- "@id": "as:items",
- "@type": "@id"
- },
- "instrument": {
- "@id": "as:instrument",
- "@type": "@id"
- },
- "orderedItems": {
- "@id": "as:items",
- "@type": "@id",
- "@container": "@list"
- },
- "last": {
- "@id": "as:last",
- "@type": "@id"
- },
- "location": {
- "@id": "as:location",
- "@type": "@id"
- },
- "next": {
- "@id": "as:next",
- "@type": "@id"
- },
- "object": {
- "@id": "as:object",
- "@type": "@id"
- },
- "oneOf": {
- "@id": "as:oneOf",
- "@type": "@id"
- },
- "anyOf": {
- "@id": "as:anyOf",
- "@type": "@id"
- },
- "closed": {
- "@id": "as:closed",
- "@type": "xsd:dateTime"
- },
- "origin": {
- "@id": "as:origin",
- "@type": "@id"
- },
- "accuracy": {
- "@id": "as:accuracy",
- "@type": "xsd:float"
- },
- "prev": {
- "@id": "as:prev",
- "@type": "@id"
- },
- "preview": {
- "@id": "as:preview",
- "@type": "@id"
- },
- "replies": {
- "@id": "as:replies",
- "@type": "@id"
- },
- "result": {
- "@id": "as:result",
- "@type": "@id"
- },
- "audience": {
- "@id": "as:audience",
- "@type": "@id"
- },
- "partOf": {
- "@id": "as:partOf",
- "@type": "@id"
- },
- "tag": {
- "@id": "as:tag",
- "@type": "@id"
- },
- "target": {
- "@id": "as:target",
- "@type": "@id"
- },
- "to": {
- "@id": "as:to",
- "@type": "@id"
- },
- "url": {
- "@id": "as:url",
- "@type": "@id"
- },
- "altitude": {
- "@id": "as:altitude",
- "@type": "xsd:float"
- },
- "content": "as:content",
- "contentMap": {
- "@id": "as:content",
- "@container": "@language"
- },
- "name": "as:name",
- "nameMap": {
- "@id": "as:name",
- "@container": "@language"
- },
- "duration": {
- "@id": "as:duration",
- "@type": "xsd:duration"
- },
- "endTime": {
- "@id": "as:endTime",
- "@type": "xsd:dateTime"
- },
- "height": {
- "@id": "as:height",
- "@type": "xsd:nonNegativeInteger"
- },
- "href": {
- "@id": "as:href",
- "@type": "@id"
- },
- "hreflang": "as:hreflang",
- "latitude": {
- "@id": "as:latitude",
- "@type": "xsd:float"
- },
- "longitude": {
- "@id": "as:longitude",
- "@type": "xsd:float"
- },
- "mediaType": "as:mediaType",
- "published": {
- "@id": "as:published",
- "@type": "xsd:dateTime"
- },
- "radius": {
- "@id": "as:radius",
- "@type": "xsd:float"
- },
- "rel": "as:rel",
- "startIndex": {
- "@id": "as:startIndex",
- "@type": "xsd:nonNegativeInteger"
- },
- "startTime": {
- "@id": "as:startTime",
- "@type": "xsd:dateTime"
- },
- "summary": "as:summary",
- "summaryMap": {
- "@id": "as:summary",
- "@container": "@language"
- },
- "totalItems": {
- "@id": "as:totalItems",
- "@type": "xsd:nonNegativeInteger"
- },
- "units": "as:units",
- "updated": {
- "@id": "as:updated",
- "@type": "xsd:dateTime"
- },
- "width": {
- "@id": "as:width",
- "@type": "xsd:nonNegativeInteger"
- },
- "describes": {
- "@id": "as:describes",
- "@type": "@id"
- },
- "formerType": {
- "@id": "as:formerType",
- "@type": "@id"
- },
- "deleted": {
- "@id": "as:deleted",
- "@type": "xsd:dateTime"
- },
- "inbox": {
- "@id": "ldp:inbox",
- "@type": "@id"
- },
- "outbox": {
- "@id": "as:outbox",
- "@type": "@id"
- },
- "following": {
- "@id": "as:following",
- "@type": "@id"
- },
- "followers": {
- "@id": "as:followers",
- "@type": "@id"
- },
- "streams": {
- "@id": "as:streams",
- "@type": "@id"
- },
- "preferredUsername": "as:preferredUsername",
- "endpoints": {
- "@id": "as:endpoints",
- "@type": "@id"
- },
- "uploadMedia": {
- "@id": "as:uploadMedia",
- "@type": "@id"
- },
- "proxyUrl": {
- "@id": "as:proxyUrl",
- "@type": "@id"
- },
- "liked": {
- "@id": "as:liked",
- "@type": "@id"
- },
- "oauthAuthorizationEndpoint": {
- "@id": "as:oauthAuthorizationEndpoint",
- "@type": "@id"
- },
- "oauthTokenEndpoint": {
- "@id": "as:oauthTokenEndpoint",
- "@type": "@id"
- },
- "provideClientKey": {
- "@id": "as:provideClientKey",
- "@type": "@id"
- },
- "signClientKey": {
- "@id": "as:signClientKey",
- "@type": "@id"
- },
- "sharedInbox": {
- "@id": "as:sharedInbox",
- "@type": "@id"
- },
- "Public": {
- "@id": "as:Public",
- "@type": "@id"
- },
- "source": "as:source",
- "likes": {
- "@id": "as:likes",
- "@type": "@id"
- },
- "shares": {
- "@id": "as:shares",
- "@type": "@id"
- },
- "alsoKnownAs": {
- "@id": "as:alsoKnownAs",
- "@type": "@id"
- }
- }
-};
-
-export const CONTEXTS: Record<string, any> = {
- "https://w3id.org/identity/v1": id_v1,
- "https://w3id.org/security/v1": security_v1,
- "https://www.w3.org/ns/activitystreams": activitystreams,
-};
diff --git a/src/remote/activitypub/misc/get-note-html.ts b/src/remote/activitypub/misc/get-note-html.ts
deleted file mode 100644
index a24ec43a69..0000000000
--- a/src/remote/activitypub/misc/get-note-html.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import * as mfm from 'mfm-js';
-import { fnNameList } from '@/mfm/fn-name-list';
-import { Note } from '@/models/entities/note';
-import { toHtml } from '../../../mfm/to-html';
-
-export default function(note: Note) {
- let html = note.text ? toHtml(mfm.parse(note.text, { fnNameList }), JSON.parse(note.mentionedRemoteUsers)) : null;
- if (html == null) html = '<p>.</p>';
-
- return html;
-}
diff --git a/src/remote/activitypub/misc/html-to-mfm.ts b/src/remote/activitypub/misc/html-to-mfm.ts
deleted file mode 100644
index 5cca04df21..0000000000
--- a/src/remote/activitypub/misc/html-to-mfm.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { IObject } from '../type';
-import { extractApHashtagObjects } from '../models/tag';
-import { fromHtml } from '../../../mfm/from-html';
-
-export function htmlToMfm(html: string, tag?: IObject | IObject[]) {
- const hashtagNames = extractApHashtagObjects(tag).map(x => x.name).filter((x): x is string => x != null);
-
- return fromHtml(html, hashtagNames);
-}
diff --git a/src/remote/activitypub/misc/ld-signature.ts b/src/remote/activitypub/misc/ld-signature.ts
deleted file mode 100644
index dec07ea81b..0000000000
--- a/src/remote/activitypub/misc/ld-signature.ts
+++ /dev/null
@@ -1,134 +0,0 @@
-import * as crypto from 'crypto';
-import * as jsonld from 'jsonld';
-import { CONTEXTS } from './contexts';
-import fetch from 'node-fetch';
-import { httpAgent, httpsAgent } from '@/misc/fetch';
-
-// RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017
-
-export class LdSignature {
- public debug = false;
- public preLoad = true;
- public loderTimeout = 10 * 1000;
-
- constructor() {
- }
-
- public async signRsaSignature2017(data: any, privateKey: string, creator: string, domain?: string, created?: Date): Promise<any> {
- const options = {
- type: 'RsaSignature2017',
- creator,
- domain,
- nonce: crypto.randomBytes(16).toString('hex'),
- created: (created || new Date()).toISOString()
- } as {
- type: string;
- creator: string;
- domain: string;
- nonce: string;
- created: string;
- };
-
- if (!domain) {
- delete options.domain;
- }
-
- const toBeSigned = await this.createVerifyData(data, options);
-
- const signer = crypto.createSign('sha256');
- signer.update(toBeSigned);
- signer.end();
-
- const signature = signer.sign(privateKey);
-
- return {
- ...data,
- signature: {
- ...options,
- signatureValue: signature.toString('base64')
- }
- };
- }
-
- public async verifyRsaSignature2017(data: any, publicKey: string): Promise<boolean> {
- const toBeSigned = await this.createVerifyData(data, data.signature);
- const verifier = crypto.createVerify('sha256');
- verifier.update(toBeSigned);
- return verifier.verify(publicKey, data.signature.signatureValue, 'base64');
- }
-
- public async createVerifyData(data: any, options: any) {
- const transformedOptions = {
- ...options,
- '@context': 'https://w3id.org/identity/v1'
- };
- delete transformedOptions['type'];
- delete transformedOptions['id'];
- delete transformedOptions['signatureValue'];
- const canonizedOptions = await this.normalize(transformedOptions);
- const optionsHash = this.sha256(canonizedOptions);
- const transformedData = { ...data };
- delete transformedData['signature'];
- const cannonidedData = await this.normalize(transformedData);
- if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
- const documentHash = this.sha256(cannonidedData);
- const verifyData = `${optionsHash}${documentHash}`;
- return verifyData;
- }
-
- public async normalize(data: any) {
- const customLoader = this.getLoader();
- return await jsonld.normalize(data, {
- documentLoader: customLoader
- });
- }
-
- private getLoader() {
- return async (url: string): Promise<any> => {
- if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`;
-
- if (this.preLoad) {
- if (url in CONTEXTS) {
- if (this.debug) console.debug(`HIT: ${url}`);
- return {
- contextUrl: null,
- document: CONTEXTS[url],
- documentUrl: url
- };
- }
- }
-
- if (this.debug) console.debug(`MISS: ${url}`);
- const document = await this.fetchDocument(url);
- return {
- contextUrl: null,
- document: document,
- documentUrl: url
- };
- };
- }
-
- private async fetchDocument(url: string) {
- const json = await fetch(url, {
- headers: {
- Accept: 'application/ld+json, application/json',
- },
- timeout: this.loderTimeout,
- agent: u => u.protocol == 'http:' ? httpAgent : httpsAgent,
- }).then(res => {
- if (!res.ok) {
- throw `${res.status} ${res.statusText}`;
- } else {
- return res.json();
- }
- });
-
- return json;
- }
-
- public sha256(data: string): string {
- const hash = crypto.createHash('sha256');
- hash.update(data);
- return hash.digest('hex');
- }
-}
diff --git a/src/remote/activitypub/models/icon.ts b/src/remote/activitypub/models/icon.ts
deleted file mode 100644
index 50794a937d..0000000000
--- a/src/remote/activitypub/models/icon.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type IIcon = {
- type: string;
- mediaType?: string;
- url?: string;
-};
diff --git a/src/remote/activitypub/models/identifier.ts b/src/remote/activitypub/models/identifier.ts
deleted file mode 100644
index f6c3bb8c88..0000000000
--- a/src/remote/activitypub/models/identifier.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export type IIdentifier = {
- type: string;
- name: string;
- value: string;
-};
diff --git a/src/remote/activitypub/models/image.ts b/src/remote/activitypub/models/image.ts
deleted file mode 100644
index d0a96e4313..0000000000
--- a/src/remote/activitypub/models/image.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import uploadFromUrl from '@/services/drive/upload-from-url';
-import { IRemoteUser } from '@/models/entities/user';
-import Resolver from '../resolver';
-import { fetchMeta } from '@/misc/fetch-meta';
-import { apLogger } from '../logger';
-import { DriveFile } from '@/models/entities/drive-file';
-import { DriveFiles } from '@/models/index';
-import { truncate } from '@/misc/truncate';
-import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/misc/hard-limits';
-
-const logger = apLogger;
-
-/**
- * Imageを作成します。
- */
-export async function createImage(actor: IRemoteUser, value: any): Promise<DriveFile> {
- // 投稿者が凍結されていたらスキップ
- if (actor.isSuspended) {
- throw new Error('actor has been suspended');
- }
-
- const image = await new Resolver().resolve(value) as any;
-
- if (image.url == null) {
- throw new Error('invalid image: url not privided');
- }
-
- logger.info(`Creating the Image: ${image.url}`);
-
- const instance = await fetchMeta();
- const cache = instance.cacheRemoteFiles;
-
- let file = await uploadFromUrl(image.url, actor, null, image.url, image.sensitive, false, !cache, truncate(image.name, DB_MAX_IMAGE_COMMENT_LENGTH));
-
- if (file.isLink) {
- // URLが異なっている場合、同じ画像が以前に異なるURLで登録されていたということなので、
- // URLを更新する
- if (file.url !== image.url) {
- await DriveFiles.update({ id: file.id }, {
- url: image.url,
- uri: image.url
- });
-
- file = await DriveFiles.findOneOrFail(file.id);
- }
- }
-
- return file;
-}
-
-/**
- * Imageを解決します。
- *
- * Misskeyに対象のImageが登録されていればそれを返し、そうでなければ
- * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
- */
-export async function resolveImage(actor: IRemoteUser, value: any): Promise<DriveFile> {
- // TODO
-
- // リモートサーバーからフェッチしてきて登録
- return await createImage(actor, value);
-}
diff --git a/src/remote/activitypub/models/mention.ts b/src/remote/activitypub/models/mention.ts
deleted file mode 100644
index ade9c90806..0000000000
--- a/src/remote/activitypub/models/mention.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { toArray, unique } from '@/prelude/array';
-import { IObject, isMention, IApMention } from '../type';
-import { resolvePerson } from './person';
-import * as promiseLimit from 'promise-limit';
-import Resolver from '../resolver';
-import { User } from '@/models/entities/user';
-
-export async function extractApMentions(tags: IObject | IObject[] | null | undefined) {
- const hrefs = unique(extractApMentionObjects(tags).map(x => x.href as string));
-
- const resolver = new Resolver();
-
- const limit = promiseLimit<User | null>(2);
- const mentionedUsers = (await Promise.all(
- hrefs.map(x => limit(() => resolvePerson(x, resolver).catch(() => null)))
- )).filter((x): x is User => x != null);
-
- return mentionedUsers;
-}
-
-export function extractApMentionObjects(tags: IObject | IObject[] | null | undefined): IApMention[] {
- if (tags == null) return [];
- return toArray(tags).filter(isMention);
-}
diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
deleted file mode 100644
index 492dc05248..0000000000
--- a/src/remote/activitypub/models/note.ts
+++ /dev/null
@@ -1,356 +0,0 @@
-import * as promiseLimit from 'promise-limit';
-
-import config from '@/config/index';
-import Resolver from '../resolver';
-import post from '@/services/note/create';
-import { resolvePerson, updatePerson } from './person';
-import { resolveImage } from './image';
-import { IRemoteUser } from '@/models/entities/user';
-import { htmlToMfm } from '../misc/html-to-mfm';
-import { extractApHashtags } from './tag';
-import { unique, toArray, toSingle } from '@/prelude/array';
-import { extractPollFromQuestion } from './question';
-import vote from '@/services/note/polls/vote';
-import { apLogger } from '../logger';
-import { DriveFile } from '@/models/entities/drive-file';
-import { deliverQuestionUpdate } from '@/services/note/polls/update';
-import { extractDbHost, toPuny } from '@/misc/convert-host';
-import { Emojis, Polls, MessagingMessages } from '@/models/index';
-import { Note } from '@/models/entities/note';
-import { IObject, getOneApId, getApId, getOneApHrefNullable, validPost, IPost, isEmoji, getApType } from '../type';
-import { Emoji } from '@/models/entities/emoji';
-import { genId } from '@/misc/gen-id';
-import { fetchMeta } from '@/misc/fetch-meta';
-import { getApLock } from '@/misc/app-lock';
-import { createMessage } from '@/services/messages/create';
-import { parseAudience } from '../audience';
-import { extractApMentions } from './mention';
-import DbResolver from '../db-resolver';
-import { StatusError } from '@/misc/fetch';
-
-const logger = apLogger;
-
-export function validateNote(object: any, uri: string) {
- const expectHost = extractDbHost(uri);
-
- if (object == null) {
- return new Error('invalid Note: object is null');
- }
-
- if (!validPost.includes(getApType(object))) {
- return new Error(`invalid Note: invalid object type ${getApType(object)}`);
- }
-
- if (object.id && extractDbHost(object.id) !== expectHost) {
- return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${extractDbHost(object.id)}`);
- }
-
- if (object.attributedTo && extractDbHost(getOneApId(object.attributedTo)) !== expectHost) {
- return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${extractDbHost(object.attributedTo)}`);
- }
-
- return null;
-}
-
-/**
- * Noteをフェッチします。
- *
- * Misskeyに対象のNoteが登録されていればそれを返します。
- */
-export async function fetchNote(object: string | IObject): Promise<Note | null> {
- const dbResolver = new DbResolver();
- return await dbResolver.getNoteFromApId(object);
-}
-
-/**
- * Noteを作成します。
- */
-export async function createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> {
- if (resolver == null) resolver = new Resolver();
-
- const object: any = await resolver.resolve(value);
-
- const entryUri = getApId(value);
- const err = validateNote(object, entryUri);
- if (err) {
- logger.error(`${err.message}`, {
- resolver: {
- history: resolver.getHistory()
- },
- value: value,
- object: object
- });
- throw new Error('invalid note');
- }
-
- const note: IPost = object;
-
- logger.debug(`Note fetched: ${JSON.stringify(note, null, 2)}`);
-
- logger.info(`Creating the Note: ${note.id}`);
-
- // 投稿者をフェッチ
- const actor = await resolvePerson(getOneApId(note.attributedTo), resolver) as IRemoteUser;
-
- // 投稿者が凍結されていたらスキップ
- if (actor.isSuspended) {
- throw new Error('actor has been suspended');
- }
-
- const noteAudience = await parseAudience(actor, note.to, note.cc);
- let visibility = noteAudience.visibility;
- const visibleUsers = noteAudience.visibleUsers;
-
- // Audience (to, cc) が指定されてなかった場合
- if (visibility === 'specified' && visibleUsers.length === 0) {
- if (typeof value === 'string') { // 入力がstringならばresolverでGETが発生している
- // こちらから匿名GET出来たものならばpublic
- visibility = 'public';
- }
- }
-
- let isTalk = note._misskey_talk && visibility === 'specified';
-
- const apMentions = await extractApMentions(note.tag);
- const apHashtags = await extractApHashtags(note.tag);
-
- // 添付ファイル
- // TODO: attachmentは必ずしもImageではない
- // TODO: attachmentは必ずしも配列ではない
- // Noteがsensitiveなら添付もsensitiveにする
- const limit = promiseLimit(2);
-
- note.attachment = Array.isArray(note.attachment) ? note.attachment : note.attachment ? [note.attachment] : [];
- const files = note.attachment
- .map(attach => attach.sensitive = note.sensitive)
- ? (await Promise.all(note.attachment.map(x => limit(() => resolveImage(actor, x)) as Promise<DriveFile>)))
- .filter(image => image != null)
- : [];
-
- // リプライ
- const reply: Note | null = note.inReplyTo
- ? await resolveNote(note.inReplyTo, resolver).then(x => {
- if (x == null) {
- logger.warn(`Specified inReplyTo, but nout found`);
- throw new Error('inReplyTo not found');
- } else {
- return x;
- }
- }).catch(async e => {
- // トークだったらinReplyToのエラーは無視
- const uri = getApId(note.inReplyTo);
- if (uri.startsWith(config.url + '/')) {
- const id = uri.split('/').pop();
- const talk = await MessagingMessages.findOne(id);
- if (talk) {
- isTalk = true;
- return null;
- }
- }
-
- logger.warn(`Error in inReplyTo ${note.inReplyTo} - ${e.statusCode || e}`);
- throw e;
- })
- : null;
-
- // 引用
- let quote: Note | undefined | null;
-
- if (note._misskey_quote || note.quoteUrl) {
- const tryResolveNote = async (uri: string): Promise<{
- status: 'ok';
- res: Note | null;
- } | {
- status: 'permerror' | 'temperror';
- }> => {
- if (typeof uri !== 'string' || !uri.match(/^https?:/)) return { status: 'permerror' };
- try {
- const res = await resolveNote(uri);
- if (res) {
- return {
- status: 'ok',
- res
- };
- } else {
- return {
- status: 'permerror'
- };
- }
- } catch (e) {
- return {
- status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror'
- };
- }
- };
-
- const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
- const results = await Promise.all(uris.map(uri => tryResolveNote(uri)));
-
- quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x);
- if (!quote) {
- if (results.some(x => x.status === 'temperror')) {
- throw 'quote resolve failed';
- }
- }
- }
-
- const cw = note.summary === '' ? null : note.summary;
-
- // テキストのパース
- const text = note._misskey_content || (note.content ? htmlToMfm(note.content, note.tag) : null);
-
- // vote
- if (reply && reply.hasPoll) {
- const poll = await Polls.findOneOrFail(reply.id);
-
- const tryCreateVote = async (name: string, index: number): Promise<null> => {
- if (poll.expiresAt && Date.now() > new Date(poll.expiresAt).getTime()) {
- logger.warn(`vote to expired poll from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
- } else if (index >= 0) {
- logger.info(`vote from AP: actor=${actor.username}@${actor.host}, note=${note.id}, choice=${name}`);
- await vote(actor, reply, index);
-
- // リモートフォロワーにUpdate配信
- deliverQuestionUpdate(reply.id);
- }
- return null;
- };
-
- if (note.name) {
- return await tryCreateVote(note.name, poll.choices.findIndex(x => x === note.name));
- }
- }
-
- const emojis = await extractEmojis(note.tag || [], actor.host).catch(e => {
- logger.info(`extractEmojis: ${e}`);
- return [] as Emoji[];
- });
-
- const apEmojis = emojis.map(emoji => emoji.name);
-
- const poll = await extractPollFromQuestion(note, resolver).catch(() => undefined);
-
- // ユーザーの情報が古かったらついでに更新しておく
- if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
- if (actor.uri) updatePerson(actor.uri);
- }
-
- if (isTalk) {
- for (const recipient of visibleUsers) {
- await createMessage(actor, recipient, undefined, text || undefined, (files && files.length > 0) ? files[0] : null, object.id);
- return null;
- }
- }
-
- return await post(actor, {
- createdAt: note.published ? new Date(note.published) : null,
- files,
- reply,
- renote: quote,
- name: note.name,
- cw,
- text,
- viaMobile: false,
- localOnly: false,
- visibility,
- visibleUsers,
- apMentions,
- apHashtags,
- apEmojis,
- poll,
- uri: note.id,
- url: getOneApHrefNullable(note.url),
- }, silent);
-}
-
-/**
- * Noteを解決します。
- *
- * Misskeyに対象のNoteが登録されていればそれを返し、そうでなければ
- * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
- */
-export async function resolveNote(value: string | IObject, resolver?: Resolver): Promise<Note | null> {
- const uri = typeof value === 'string' ? value : value.id;
- if (uri == null) throw new Error('missing uri');
-
- // ブロックしてたら中断
- const meta = await fetchMeta();
- if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 };
-
- const unlock = await getApLock(uri);
-
- try {
- //#region このサーバーに既に登録されていたらそれを返す
- const exist = await fetchNote(uri);
-
- if (exist) {
- return exist;
- }
- //#endregion
-
- if (uri.startsWith(config.url)) {
- throw new StatusError('cannot resolve local note', 400, 'cannot resolve local note');
- }
-
- // リモートサーバーからフェッチしてきて登録
- // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにノートが生成されるが
- // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
- return await createNote(uri, resolver, true);
- } finally {
- unlock();
- }
-}
-
-export async function extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> {
- host = toPuny(host);
-
- if (!tags) return [];
-
- const eomjiTags = toArray(tags).filter(isEmoji);
-
- return await Promise.all(eomjiTags.map(async tag => {
- const name = tag.name!.replace(/^:/, '').replace(/:$/, '');
- tag.icon = toSingle(tag.icon);
-
- const exists = await Emojis.findOne({
- host,
- name
- });
-
- if (exists) {
- if ((tag.updated != null && exists.updatedAt == null)
- || (tag.id != null && exists.uri == null)
- || (tag.updated != null && exists.updatedAt != null && new Date(tag.updated) > exists.updatedAt)
- || (tag.icon!.url !== exists.url)
- ) {
- await Emojis.update({
- host,
- name,
- }, {
- uri: tag.id,
- url: tag.icon!.url,
- updatedAt: new Date(),
- });
-
- return await Emojis.findOne({
- host,
- name
- }) as Emoji;
- }
-
- return exists;
- }
-
- logger.info(`register emoji host=${host}, name=${name}`);
-
- return await Emojis.save({
- id: genId(),
- host,
- name,
- uri: tag.id,
- url: tag.icon!.url,
- updatedAt: new Date(),
- aliases: []
- } as Partial<Emoji>);
- }));
-}
diff --git a/src/remote/activitypub/models/person.ts b/src/remote/activitypub/models/person.ts
deleted file mode 100644
index eb8c00a10b..0000000000
--- a/src/remote/activitypub/models/person.ts
+++ /dev/null
@@ -1,494 +0,0 @@
-import { URL } from 'url';
-import * as promiseLimit from 'promise-limit';
-
-import $, { Context } from 'cafy';
-import config from '@/config/index';
-import Resolver from '../resolver';
-import { resolveImage } from './image';
-import { isCollectionOrOrderedCollection, isCollection, IActor, getApId, getOneApHrefNullable, IObject, isPropertyValue, IApPropertyValue, getApType, isActor } from '../type';
-import { fromHtml } from '../../../mfm/from-html';
-import { htmlToMfm } from '../misc/html-to-mfm';
-import { resolveNote, extractEmojis } from './note';
-import { registerOrFetchInstanceDoc } from '@/services/register-or-fetch-instance-doc';
-import { extractApHashtags } from './tag';
-import { apLogger } from '../logger';
-import { Note } from '@/models/entities/note';
-import { updateUsertags } from '@/services/update-hashtag';
-import { Users, Instances, DriveFiles, Followings, UserProfiles, UserPublickeys } from '@/models/index';
-import { User, IRemoteUser } from '@/models/entities/user';
-import { Emoji } from '@/models/entities/emoji';
-import { UserNotePining } from '@/models/entities/user-note-pining';
-import { genId } from '@/misc/gen-id';
-import { instanceChart, usersChart } from '@/services/chart/index';
-import { UserPublickey } from '@/models/entities/user-publickey';
-import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error';
-import { toPuny } from '@/misc/convert-host';
-import { UserProfile } from '@/models/entities/user-profile';
-import { getConnection } from 'typeorm';
-import { toArray } from '@/prelude/array';
-import { fetchInstanceMetadata } from '@/services/fetch-instance-metadata';
-import { normalizeForSearch } from '@/misc/normalize-for-search';
-import { truncate } from '@/misc/truncate';
-import { StatusError } from '@/misc/fetch';
-
-const logger = apLogger;
-
-const nameLength = 128;
-const summaryLength = 2048;
-
-/**
- * Validate and convert to actor object
- * @param x Fetched object
- * @param uri Fetch target URI
- */
-function validateActor(x: IObject, uri: string): IActor {
- const expectHost = toPuny(new URL(uri).hostname);
-
- if (x == null) {
- throw new Error('invalid Actor: object is null');
- }
-
- if (!isActor(x)) {
- throw new Error(`invalid Actor type '${x.type}'`);
- }
-
- const validate = (name: string, value: any, validater: Context) => {
- const e = validater.test(value);
- if (e) throw new Error(`invalid Actor: ${name} ${e.message}`);
- };
-
- validate('id', x.id, $.str.min(1));
- validate('inbox', x.inbox, $.str.min(1));
- validate('preferredUsername', x.preferredUsername, $.str.min(1).max(128).match(/^\w([\w-.]*\w)?$/));
-
- // These fields are only informational, and some AP software allows these
- // fields to be very long. If they are too long, we cut them off. This way
- // we can at least see these users and their activities.
- validate('name', truncate(x.name, nameLength), $.optional.nullable.str);
- validate('summary', truncate(x.summary, summaryLength), $.optional.nullable.str);
-
- const idHost = toPuny(new URL(x.id!).hostname);
- if (idHost !== expectHost) {
- throw new Error('invalid Actor: id has different host');
- }
-
- if (x.publicKey) {
- if (typeof x.publicKey.id !== 'string') {
- throw new Error('invalid Actor: publicKey.id is not a string');
- }
-
- const publicKeyIdHost = toPuny(new URL(x.publicKey.id).hostname);
- if (publicKeyIdHost !== expectHost) {
- throw new Error('invalid Actor: publicKey.id has different host');
- }
- }
-
- return x;
-}
-
-/**
- * Personをフェッチします。
- *
- * Misskeyに対象のPersonが登録されていればそれを返します。
- */
-export async function fetchPerson(uri: string, resolver?: Resolver): Promise<User | null> {
- if (typeof uri !== 'string') throw new Error('uri is not string');
-
- // URIがこのサーバーを指しているならデータベースからフェッチ
- if (uri.startsWith(config.url + '/')) {
- const id = uri.split('/').pop();
- return await Users.findOne(id).then(x => x || null);
- }
-
- //#region このサーバーに既に登録されていたらそれを返す
- const exist = await Users.findOne({ uri });
-
- if (exist) {
- return exist;
- }
- //#endregion
-
- return null;
-}
-
-/**
- * Personを作成します。
- */
-export async function createPerson(uri: string, resolver?: Resolver): Promise<User> {
- if (typeof uri !== 'string') throw new Error('uri is not string');
-
- if (uri.startsWith(config.url)) {
- throw new StatusError('cannot resolve local user', 400, 'cannot resolve local user');
- }
-
- if (resolver == null) resolver = new Resolver();
-
- const object = await resolver.resolve(uri) as any;
-
- const person = validateActor(object, uri);
-
- logger.info(`Creating the Person: ${person.id}`);
-
- const host = toPuny(new URL(object.id).hostname);
-
- const { fields } = analyzeAttachments(person.attachment || []);
-
- const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
-
- const isBot = getApType(object) === 'Service';
-
- const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
-
- // Create user
- let user: IRemoteUser;
- try {
- // Start transaction
- await getConnection().transaction(async transactionalEntityManager => {
- user = await transactionalEntityManager.save(new User({
- id: genId(),
- avatarId: null,
- bannerId: null,
- createdAt: new Date(),
- lastFetchedAt: new Date(),
- name: truncate(person.name, nameLength),
- isLocked: !!person.manuallyApprovesFollowers,
- isExplorable: !!person.discoverable,
- username: person.preferredUsername,
- usernameLower: person.preferredUsername!.toLowerCase(),
- host,
- inbox: person.inbox,
- sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
- followersUri: person.followers ? getApId(person.followers) : undefined,
- featured: person.featured ? getApId(person.featured) : undefined,
- uri: person.id,
- tags,
- isBot,
- isCat: (person as any).isCat === true
- })) as IRemoteUser;
-
- await transactionalEntityManager.save(new UserProfile({
- userId: user.id,
- description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
- url: getOneApHrefNullable(person.url),
- fields,
- birthday: bday ? bday[0] : null,
- location: person['vcard:Address'] || null,
- userHost: host
- }));
-
- if (person.publicKey) {
- await transactionalEntityManager.save(new UserPublickey({
- userId: user.id,
- keyId: person.publicKey.id,
- keyPem: person.publicKey.publicKeyPem
- }));
- }
- });
- } catch (e) {
- // duplicate key error
- if (isDuplicateKeyValueError(e)) {
- // /users/@a => /users/:id のように入力がaliasなときにエラーになることがあるのを対応
- const u = await Users.findOne({
- uri: person.id
- });
-
- if (u) {
- user = u as IRemoteUser;
- } else {
- throw new Error('already registered');
- }
- } else {
- logger.error(e);
- throw e;
- }
- }
-
- // Register host
- registerOrFetchInstanceDoc(host).then(i => {
- Instances.increment({ id: i.id }, 'usersCount', 1);
- instanceChart.newUser(i.host);
- fetchInstanceMetadata(i);
- });
-
- usersChart.update(user!, true);
-
- // ハッシュタグ更新
- updateUsertags(user!, tags);
-
- //#region アバターとヘッダー画像をフェッチ
- const [avatar, banner] = await Promise.all([
- person.icon,
- person.image
- ].map(img =>
- img == null
- ? Promise.resolve(null)
- : resolveImage(user!, img).catch(() => null)
- ));
-
- const avatarId = avatar ? avatar.id : null;
- const bannerId = banner ? banner.id : null;
- const avatarUrl = avatar ? DriveFiles.getPublicUrl(avatar, true) : null;
- const bannerUrl = banner ? DriveFiles.getPublicUrl(banner) : null;
- const avatarBlurhash = avatar ? avatar.blurhash : null;
- const bannerBlurhash = banner ? banner.blurhash : null;
-
- await Users.update(user!.id, {
- avatarId,
- bannerId,
- avatarUrl,
- bannerUrl,
- avatarBlurhash,
- bannerBlurhash
- });
-
- user!.avatarId = avatarId;
- user!.bannerId = bannerId;
- user!.avatarUrl = avatarUrl;
- user!.bannerUrl = bannerUrl;
- user!.avatarBlurhash = avatarBlurhash;
- user!.bannerBlurhash = bannerBlurhash;
- //#endregion
-
- //#region カスタム絵文字取得
- const emojis = await extractEmojis(person.tag || [], host).catch(e => {
- logger.info(`extractEmojis: ${e}`);
- return [] as Emoji[];
- });
-
- const emojiNames = emojis.map(emoji => emoji.name);
-
- await Users.update(user!.id, {
- emojis: emojiNames
- });
- //#endregion
-
- await updateFeatured(user!.id).catch(err => logger.error(err));
-
- return user!;
-}
-
-/**
- * Personの情報を更新します。
- * Misskeyに対象のPersonが登録されていなければ無視します。
- * @param uri URI of Person
- * @param resolver Resolver
- * @param hint Hint of Person object (この値が正当なPersonの場合、Remote resolveをせずに更新に利用します)
- */
-export async function updatePerson(uri: string, resolver?: Resolver | null, hint?: object): Promise<void> {
- if (typeof uri !== 'string') throw new Error('uri is not string');
-
- // URIがこのサーバーを指しているならスキップ
- if (uri.startsWith(config.url + '/')) {
- return;
- }
-
- //#region このサーバーに既に登録されているか
- const exist = await Users.findOne({ uri }) as IRemoteUser;
-
- if (exist == null) {
- return;
- }
- //#endregion
-
- if (resolver == null) resolver = new Resolver();
-
- const object = hint || await resolver.resolve(uri) as any;
-
- const person = validateActor(object, uri);
-
- logger.info(`Updating the Person: ${person.id}`);
-
- // アバターとヘッダー画像をフェッチ
- const [avatar, banner] = await Promise.all([
- person.icon,
- person.image
- ].map(img =>
- img == null
- ? Promise.resolve(null)
- : resolveImage(exist, img).catch(() => null)
- ));
-
- // カスタム絵文字取得
- const emojis = await extractEmojis(person.tag || [], exist.host).catch(e => {
- logger.info(`extractEmojis: ${e}`);
- return [] as Emoji[];
- });
-
- const emojiNames = emojis.map(emoji => emoji.name);
-
- const { fields } = analyzeAttachments(person.attachment || []);
-
- const tags = extractApHashtags(person.tag).map(tag => normalizeForSearch(tag)).splice(0, 32);
-
- const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
-
- const updates = {
- lastFetchedAt: new Date(),
- inbox: person.inbox,
- sharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined),
- followersUri: person.followers ? getApId(person.followers) : undefined,
- featured: person.featured,
- emojis: emojiNames,
- name: truncate(person.name, nameLength),
- tags,
- isBot: getApType(object) === 'Service',
- isCat: (person as any).isCat === true,
- isLocked: !!person.manuallyApprovesFollowers,
- isExplorable: !!person.discoverable,
- } as Partial<User>;
-
- if (avatar) {
- updates.avatarId = avatar.id;
- updates.avatarUrl = DriveFiles.getPublicUrl(avatar, true);
- updates.avatarBlurhash = avatar.blurhash;
- }
-
- if (banner) {
- updates.bannerId = banner.id;
- updates.bannerUrl = DriveFiles.getPublicUrl(banner);
- updates.bannerBlurhash = banner.blurhash;
- }
-
- // Update user
- await Users.update(exist.id, updates);
-
- if (person.publicKey) {
- await UserPublickeys.update({ userId: exist.id }, {
- keyId: person.publicKey.id,
- keyPem: person.publicKey.publicKeyPem
- });
- }
-
- await UserProfiles.update({ userId: exist.id }, {
- url: getOneApHrefNullable(person.url),
- fields,
- description: person.summary ? htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
- birthday: bday ? bday[0] : null,
- location: person['vcard:Address'] || null,
- });
-
- // ハッシュタグ更新
- updateUsertags(exist, tags);
-
- // 該当ユーザーが既にフォロワーになっていた場合はFollowingもアップデートする
- await Followings.update({
- followerId: exist.id
- }, {
- followerSharedInbox: person.sharedInbox || (person.endpoints ? person.endpoints.sharedInbox : undefined)
- });
-
- await updateFeatured(exist.id).catch(err => logger.error(err));
-}
-
-/**
- * Personを解決します。
- *
- * Misskeyに対象のPersonが登録されていればそれを返し、そうでなければ
- * リモートサーバーからフェッチしてMisskeyに登録しそれを返します。
- */
-export async function resolvePerson(uri: string, resolver?: Resolver): Promise<User> {
- if (typeof uri !== 'string') throw new Error('uri is not string');
-
- //#region このサーバーに既に登録されていたらそれを返す
- const exist = await fetchPerson(uri);
-
- if (exist) {
- return exist;
- }
- //#endregion
-
- // リモートサーバーからフェッチしてきて登録
- if (resolver == null) resolver = new Resolver();
- return await createPerson(uri, resolver);
-}
-
-const services: {
- [x: string]: (id: string, username: string) => any
- } = {
- 'misskey:authentication:twitter': (userId, screenName) => ({ userId, screenName }),
- 'misskey:authentication:github': (id, login) => ({ id, login }),
- 'misskey:authentication:discord': (id, name) => $discord(id, name)
-};
-
-const $discord = (id: string, name: string) => {
- if (typeof name !== 'string')
- name = 'unknown#0000';
- const [username, discriminator] = name.split('#');
- return { id, username, discriminator };
-};
-
-function addService(target: { [x: string]: any }, source: IApPropertyValue) {
- const service = services[source.name];
-
- if (typeof source.value !== 'string')
- source.value = 'unknown';
-
- const [id, username] = source.value.split('@');
-
- if (service)
- target[source.name.split(':')[2]] = service(id, username);
-}
-
-export function analyzeAttachments(attachments: IObject | IObject[] | undefined) {
- const fields: {
- name: string,
- value: string
- }[] = [];
- const services: { [x: string]: any } = {};
-
- if (Array.isArray(attachments)) {
- for (const attachment of attachments.filter(isPropertyValue)) {
- if (isPropertyValue(attachment.identifier)) {
- addService(services, attachment.identifier);
- } else {
- fields.push({
- name: attachment.name,
- value: fromHtml(attachment.value)
- });
- }
- }
- }
-
- return { fields, services };
-}
-
-export async function updateFeatured(userId: User['id']) {
- const user = await Users.findOneOrFail(userId);
- if (!Users.isRemoteUser(user)) return;
- if (!user.featured) return;
-
- logger.info(`Updating the featured: ${user.uri}`);
-
- const resolver = new Resolver();
-
- // Resolve to (Ordered)Collection Object
- const collection = await resolver.resolveCollection(user.featured);
- if (!isCollectionOrOrderedCollection(collection)) throw new Error(`Object is not Collection or OrderedCollection`);
-
- // Resolve to Object(may be Note) arrays
- const unresolvedItems = isCollection(collection) ? collection.items : collection.orderedItems;
- const items = await Promise.all(toArray(unresolvedItems).map(x => resolver.resolve(x)));
-
- // Resolve and regist Notes
- const limit = promiseLimit<Note | null>(2);
- const featuredNotes = await Promise.all(items
- .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
- .slice(0, 5)
- .map(item => limit(() => resolveNote(item, resolver))));
-
- await getConnection().transaction(async transactionalEntityManager => {
- await transactionalEntityManager.delete(UserNotePining, { userId: user.id });
-
- // とりあえずidを別の時間で生成して順番を維持
- let td = 0;
- for (const note of featuredNotes.filter(note => note != null)) {
- td -= 1000;
- transactionalEntityManager.insert(UserNotePining, {
- id: genId(new Date(Date.now() + td)),
- createdAt: new Date(),
- userId: user.id,
- noteId: note!.id
- });
- }
- });
-}
diff --git a/src/remote/activitypub/models/question.ts b/src/remote/activitypub/models/question.ts
deleted file mode 100644
index 79f93c3a30..0000000000
--- a/src/remote/activitypub/models/question.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import config from '@/config/index';
-import Resolver from '../resolver';
-import { IObject, IQuestion, isQuestion, } from '../type';
-import { apLogger } from '../logger';
-import { Notes, Polls } from '@/models/index';
-import { IPoll } from '@/models/entities/poll';
-
-export async function extractPollFromQuestion(source: string | IObject, resolver?: Resolver): Promise<IPoll> {
- if (resolver == null) resolver = new Resolver();
-
- const question = await resolver.resolve(source);
-
- if (!isQuestion(question)) {
- throw new Error('invalid type');
- }
-
- const multiple = !question.oneOf;
- const expiresAt = question.endTime ? new Date(question.endTime) : question.closed ? new Date(question.closed) : null;
-
- if (multiple && !question.anyOf) {
- throw new Error('invalid question');
- }
-
- const choices = question[multiple ? 'anyOf' : 'oneOf']!
- .map((x, i) => x.name!);
-
- const votes = question[multiple ? 'anyOf' : 'oneOf']!
- .map((x, i) => x.replies && x.replies.totalItems || x._misskey_votes || 0);
-
- return {
- choices,
- votes,
- multiple,
- expiresAt
- };
-}
-
-/**
- * Update votes of Question
- * @param uri URI of AP Question object
- * @returns true if updated
- */
-export async function updateQuestion(value: any) {
- const uri = typeof value === 'string' ? value : value.id;
-
- // URIがこのサーバーを指しているならスキップ
- if (uri.startsWith(config.url + '/')) throw new Error('uri points local');
-
- //#region このサーバーに既に登録されているか
- const note = await Notes.findOne({ uri });
- if (note == null) throw new Error('Question is not registed');
-
- const poll = await Polls.findOne({ noteId: note.id });
- if (poll == null) throw new Error('Question is not registed');
- //#endregion
-
- // resolve new Question object
- const resolver = new Resolver();
- const question = await resolver.resolve(value) as IQuestion;
- apLogger.debug(`fetched question: ${JSON.stringify(question, null, 2)}`);
-
- if (question.type !== 'Question') throw new Error('object is not a Question');
-
- const apChoices = question.oneOf || question.anyOf;
-
- let changed = false;
-
- for (const choice of poll.choices) {
- const oldCount = poll.votes[poll.choices.indexOf(choice)];
- const newCount = apChoices!.filter(ap => ap.name === choice)[0].replies!.totalItems;
-
- if (oldCount != newCount) {
- changed = true;
- poll.votes[poll.choices.indexOf(choice)] = newCount;
- }
- }
-
- await Polls.update({ noteId: note.id }, {
- votes: poll.votes
- });
-
- return changed;
-}
diff --git a/src/remote/activitypub/models/tag.ts b/src/remote/activitypub/models/tag.ts
deleted file mode 100644
index fbc6b9b428..0000000000
--- a/src/remote/activitypub/models/tag.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { toArray } from '@/prelude/array';
-import { IObject, isHashtag, IApHashtag } from '../type';
-
-export function extractApHashtags(tags: IObject | IObject[] | null | undefined) {
- if (tags == null) return [];
-
- const hashtags = extractApHashtagObjects(tags);
-
- return hashtags.map(tag => {
- const m = tag.name.match(/^#(.+)/);
- return m ? m[1] : null;
- }).filter((x): x is string => x != null);
-}
-
-export function extractApHashtagObjects(tags: IObject | IObject[] | null | undefined): IApHashtag[] {
- if (tags == null) return [];
- return toArray(tags).filter(isHashtag);
-}
diff --git a/src/remote/activitypub/perform.ts b/src/remote/activitypub/perform.ts
deleted file mode 100644
index 01f0e3676e..0000000000
--- a/src/remote/activitypub/perform.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { IObject } from './type';
-import { IRemoteUser } from '@/models/entities/user';
-import { performActivity } from './kernel/index';
-
-export default async (actor: IRemoteUser, activity: IObject): Promise<void> => {
- await performActivity(actor, activity);
-};
diff --git a/src/remote/activitypub/renderer/accept.ts b/src/remote/activitypub/renderer/accept.ts
deleted file mode 100644
index f1e61f4c6a..0000000000
--- a/src/remote/activitypub/renderer/accept.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-
-export default (object: any, user: { id: User['id']; host: null }) => ({
- type: 'Accept',
- actor: `${config.url}/users/${user.id}`,
- object
-});
diff --git a/src/remote/activitypub/renderer/add.ts b/src/remote/activitypub/renderer/add.ts
deleted file mode 100644
index 21414a9380..0000000000
--- a/src/remote/activitypub/renderer/add.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import config from '@/config/index';
-import { ILocalUser } from '@/models/entities/user';
-
-export default (user: ILocalUser, target: any, object: any) => ({
- type: 'Add',
- actor: `${config.url}/users/${user.id}`,
- target,
- object
-});
diff --git a/src/remote/activitypub/renderer/announce.ts b/src/remote/activitypub/renderer/announce.ts
deleted file mode 100644
index 7bf90922be..0000000000
--- a/src/remote/activitypub/renderer/announce.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import config from '@/config/index';
-import { Note } from '@/models/entities/note';
-
-export default (object: any, note: Note) => {
- const attributedTo = `${config.url}/users/${note.userId}`;
-
- let to: string[] = [];
- let cc: string[] = [];
-
- if (note.visibility === 'public') {
- to = ['https://www.w3.org/ns/activitystreams#Public'];
- cc = [`${attributedTo}/followers`];
- } else if (note.visibility === 'home') {
- to = [`${attributedTo}/followers`];
- cc = ['https://www.w3.org/ns/activitystreams#Public'];
- } else {
- return null;
- }
-
- return {
- id: `${config.url}/notes/${note.id}/activity`,
- actor: `${config.url}/users/${note.userId}`,
- type: 'Announce',
- published: note.createdAt.toISOString(),
- to,
- cc,
- object
- };
-};
diff --git a/src/remote/activitypub/renderer/block.ts b/src/remote/activitypub/renderer/block.ts
deleted file mode 100644
index bb3d74295a..0000000000
--- a/src/remote/activitypub/renderer/block.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import config from '@/config/index';
-import { ILocalUser, IRemoteUser } from '@/models/entities/user';
-
-export default (blocker: ILocalUser, blockee: IRemoteUser) => ({
- type: 'Block',
- actor: `${config.url}/users/${blocker.id}`,
- object: blockee.uri
-});
diff --git a/src/remote/activitypub/renderer/create.ts b/src/remote/activitypub/renderer/create.ts
deleted file mode 100644
index 04aa993a91..0000000000
--- a/src/remote/activitypub/renderer/create.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import config from '@/config/index';
-import { Note } from '@/models/entities/note';
-
-export default (object: any, note: Note) => {
- const activity = {
- id: `${config.url}/notes/${note.id}/activity`,
- actor: `${config.url}/users/${note.userId}`,
- type: 'Create',
- published: note.createdAt.toISOString(),
- object
- } as any;
-
- if (object.to) activity.to = object.to;
- if (object.cc) activity.cc = object.cc;
-
- return activity;
-};
diff --git a/src/remote/activitypub/renderer/delete.ts b/src/remote/activitypub/renderer/delete.ts
deleted file mode 100644
index 176a6f7e27..0000000000
--- a/src/remote/activitypub/renderer/delete.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-
-export default (object: any, user: { id: User['id']; host: null }) => ({
- type: 'Delete',
- actor: `${config.url}/users/${user.id}`,
- object,
- published: new Date().toISOString(),
-});
diff --git a/src/remote/activitypub/renderer/document.ts b/src/remote/activitypub/renderer/document.ts
deleted file mode 100644
index a9d86dea15..0000000000
--- a/src/remote/activitypub/renderer/document.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { DriveFile } from '@/models/entities/drive-file';
-import { DriveFiles } from '@/models/index';
-
-export default (file: DriveFile) => ({
- type: 'Document',
- mediaType: file.type,
- url: DriveFiles.getPublicUrl(file),
- name: file.comment,
-});
diff --git a/src/remote/activitypub/renderer/emoji.ts b/src/remote/activitypub/renderer/emoji.ts
deleted file mode 100644
index ca514c56b5..0000000000
--- a/src/remote/activitypub/renderer/emoji.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import config from '@/config/index';
-import { Emoji } from '@/models/entities/emoji';
-
-export default (emoji: Emoji) => ({
- id: `${config.url}/emojis/${emoji.name}`,
- type: 'Emoji',
- name: `:${emoji.name}:`,
- updated: emoji.updatedAt != null ? emoji.updatedAt.toISOString() : new Date().toISOString,
- icon: {
- type: 'Image',
- mediaType: emoji.type || 'image/png',
- url: emoji.url
- }
-});
diff --git a/src/remote/activitypub/renderer/follow-relay.ts b/src/remote/activitypub/renderer/follow-relay.ts
deleted file mode 100644
index 984c3c7639..0000000000
--- a/src/remote/activitypub/renderer/follow-relay.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import config from '@/config/index';
-import { Relay } from '@/models/entities/relay';
-import { ILocalUser } from '@/models/entities/user';
-
-export function renderFollowRelay(relay: Relay, relayActor: ILocalUser) {
- const follow = {
- id: `${config.url}/activities/follow-relay/${relay.id}`,
- type: 'Follow',
- actor: `${config.url}/users/${relayActor.id}`,
- object: 'https://www.w3.org/ns/activitystreams#Public'
- };
-
- return follow;
-}
diff --git a/src/remote/activitypub/renderer/follow-user.ts b/src/remote/activitypub/renderer/follow-user.ts
deleted file mode 100644
index e3dde7f7fe..0000000000
--- a/src/remote/activitypub/renderer/follow-user.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import config from '@/config/index';
-import { Users } from '@/models/index';
-import { User } from '@/models/entities/user';
-
-/**
- * Convert (local|remote)(Follower|Followee)ID to URL
- * @param id Follower|Followee ID
- */
-export default async function renderFollowUser(id: User['id']): Promise<any> {
- const user = await Users.findOneOrFail(id);
- return Users.isLocalUser(user) ? `${config.url}/users/${user.id}` : user.uri;
-}
diff --git a/src/remote/activitypub/renderer/follow.ts b/src/remote/activitypub/renderer/follow.ts
deleted file mode 100644
index c8a7946799..0000000000
--- a/src/remote/activitypub/renderer/follow.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-import { Users } from '@/models/index';
-
-export default (follower: { id: User['id']; host: User['host']; uri: User['host'] }, followee: { id: User['id']; host: User['host']; uri: User['host'] }, requestId?: string) => {
- const follow = {
- type: 'Follow',
- actor: Users.isLocalUser(follower) ? `${config.url}/users/${follower.id}` : follower.uri,
- object: Users.isLocalUser(followee) ? `${config.url}/users/${followee.id}` : followee.uri
- } as any;
-
- if (requestId) follow.id = requestId;
-
- return follow;
-};
diff --git a/src/remote/activitypub/renderer/hashtag.ts b/src/remote/activitypub/renderer/hashtag.ts
deleted file mode 100644
index 290c74c7fe..0000000000
--- a/src/remote/activitypub/renderer/hashtag.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import config from '@/config/index';
-
-export default (tag: string) => ({
- type: 'Hashtag',
- href: `${config.url}/tags/${encodeURIComponent(tag)}`,
- name: `#${tag}`
-});
diff --git a/src/remote/activitypub/renderer/image.ts b/src/remote/activitypub/renderer/image.ts
deleted file mode 100644
index 0cb3d6ed65..0000000000
--- a/src/remote/activitypub/renderer/image.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { DriveFile } from '@/models/entities/drive-file';
-import { DriveFiles } from '@/models/index';
-
-export default (file: DriveFile) => ({
- type: 'Image',
- url: DriveFiles.getPublicUrl(file),
- sensitive: file.isSensitive,
- name: file.comment
-});
diff --git a/src/remote/activitypub/renderer/index.ts b/src/remote/activitypub/renderer/index.ts
deleted file mode 100644
index f6ec6583d0..0000000000
--- a/src/remote/activitypub/renderer/index.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import config from '@/config/index';
-import { v4 as uuid } from 'uuid';
-import { IActivity } from '../type';
-import { LdSignature } from '../misc/ld-signature';
-import { getUserKeypair } from '@/misc/keypair-store';
-import { User } from '@/models/entities/user';
-
-export const renderActivity = (x: any): IActivity | null => {
- if (x == null) return null;
-
- if (x !== null && typeof x === 'object' && x.id == null) {
- x.id = `${config.url}/${uuid()}`;
- }
-
- return Object.assign({
- '@context': [
- 'https://www.w3.org/ns/activitystreams',
- 'https://w3id.org/security/v1',
- {
- // as non-standards
- manuallyApprovesFollowers: 'as:manuallyApprovesFollowers',
- sensitive: 'as:sensitive',
- Hashtag: 'as:Hashtag',
- quoteUrl: 'as:quoteUrl',
- // Mastodon
- toot: 'http://joinmastodon.org/ns#',
- Emoji: 'toot:Emoji',
- featured: 'toot:featured',
- discoverable: 'toot:discoverable',
- // schema
- schema: 'http://schema.org#',
- PropertyValue: 'schema:PropertyValue',
- value: 'schema:value',
- // Misskey
- misskey: `${config.url}/ns#`,
- '_misskey_content': 'misskey:_misskey_content',
- '_misskey_quote': 'misskey:_misskey_quote',
- '_misskey_reaction': 'misskey:_misskey_reaction',
- '_misskey_votes': 'misskey:_misskey_votes',
- '_misskey_talk': 'misskey:_misskey_talk',
- 'isCat': 'misskey:isCat',
- // vcard
- vcard: 'http://www.w3.org/2006/vcard/ns#',
- }
- ]
- }, x);
-};
-
-export const attachLdSignature = async (activity: any, user: { id: User['id']; host: null; }): Promise<IActivity | null> => {
- if (activity == null) return null;
-
- const keypair = await getUserKeypair(user.id);
-
- const ldSignature = new LdSignature();
- ldSignature.debug = false;
- activity = await ldSignature.signRsaSignature2017(activity, keypair.privateKey, `${config.url}/users/${user.id}#main-key`);
-
- return activity;
-};
diff --git a/src/remote/activitypub/renderer/key.ts b/src/remote/activitypub/renderer/key.ts
deleted file mode 100644
index 992f98d79a..0000000000
--- a/src/remote/activitypub/renderer/key.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import config from '@/config/index';
-import { ILocalUser } from '@/models/entities/user';
-import { UserKeypair } from '@/models/entities/user-keypair';
-import { createPublicKey } from 'crypto';
-
-export default (user: ILocalUser, key: UserKeypair, postfix?: string) => ({
- id: `${config.url}/users/${user.id}${postfix || '/publickey'}`,
- type: 'Key',
- owner: `${config.url}/users/${user.id}`,
- publicKeyPem: createPublicKey(key.publicKey).export({
- type: 'spki',
- format: 'pem'
- })
-});
diff --git a/src/remote/activitypub/renderer/like.ts b/src/remote/activitypub/renderer/like.ts
deleted file mode 100644
index a7e79a176f..0000000000
--- a/src/remote/activitypub/renderer/like.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import config from '@/config/index';
-import { NoteReaction } from '@/models/entities/note-reaction';
-import { Note } from '@/models/entities/note';
-import { Emojis } from '@/models/index';
-import renderEmoji from './emoji';
-
-export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
- const reaction = noteReaction.reaction;
-
- const object = {
- type: 'Like',
- id: `${config.url}/likes/${noteReaction.id}`,
- actor: `${config.url}/users/${noteReaction.userId}`,
- object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
- content: reaction,
- _misskey_reaction: reaction
- } as any;
-
- if (reaction.startsWith(':')) {
- const name = reaction.replace(/:/g, '');
- const emoji = await Emojis.findOne({
- name,
- host: null
- });
-
- if (emoji) object.tag = [ renderEmoji(emoji) ];
- }
-
- return object;
-};
diff --git a/src/remote/activitypub/renderer/mention.ts b/src/remote/activitypub/renderer/mention.ts
deleted file mode 100644
index 06d2d33e59..0000000000
--- a/src/remote/activitypub/renderer/mention.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import config from '@/config/index';
-import { User, ILocalUser } from '@/models/entities/user';
-import { Users } from '@/models/index';
-
-export default (mention: User) => ({
- type: 'Mention',
- href: Users.isRemoteUser(mention) ? mention.uri : `${config.url}/users/${(mention as ILocalUser).id}`,
- name: Users.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as ILocalUser).username}`,
-});
diff --git a/src/remote/activitypub/renderer/note.ts b/src/remote/activitypub/renderer/note.ts
deleted file mode 100644
index 84a1786784..0000000000
--- a/src/remote/activitypub/renderer/note.ts
+++ /dev/null
@@ -1,168 +0,0 @@
-import renderDocument from './document';
-import renderHashtag from './hashtag';
-import renderMention from './mention';
-import renderEmoji from './emoji';
-import config from '@/config/index';
-import toHtml from '../misc/get-note-html';
-import { Note, IMentionedRemoteUsers } from '@/models/entities/note';
-import { DriveFile } from '@/models/entities/drive-file';
-import { DriveFiles, Notes, Users, Emojis, Polls } from '@/models/index';
-import { In } from 'typeorm';
-import { Emoji } from '@/models/entities/emoji';
-import { Poll } from '@/models/entities/poll';
-
-export default async function renderNote(note: Note, dive = true, isTalk = false): Promise<any> {
- const getPromisedFiles = async (ids: string[]) => {
- if (!ids || ids.length === 0) return [];
- const items = await DriveFiles.find({ id: In(ids) });
- return ids.map(id => items.find(item => item.id === id)).filter(item => item != null) as DriveFile[];
- };
-
- let inReplyTo;
- let inReplyToNote: Note | undefined;
-
- if (note.replyId) {
- inReplyToNote = await Notes.findOne(note.replyId);
-
- if (inReplyToNote != null) {
- const inReplyToUser = await Users.findOne(inReplyToNote.userId);
-
- if (inReplyToUser != null) {
- if (inReplyToNote.uri) {
- inReplyTo = inReplyToNote.uri;
- } else {
- if (dive) {
- inReplyTo = await renderNote(inReplyToNote, false);
- } else {
- inReplyTo = `${config.url}/notes/${inReplyToNote.id}`;
- }
- }
- }
- }
- } else {
- inReplyTo = null;
- }
-
- let quote;
-
- if (note.renoteId) {
- const renote = await Notes.findOne(note.renoteId);
-
- if (renote) {
- quote = renote.uri ? renote.uri : `${config.url}/notes/${renote.id}`;
- }
- }
-
- const user = await Users.findOneOrFail(note.userId);
-
- const attributedTo = `${config.url}/users/${user.id}`;
-
- const mentions = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri);
-
- let to: string[] = [];
- let cc: string[] = [];
-
- if (note.visibility === 'public') {
- to = ['https://www.w3.org/ns/activitystreams#Public'];
- cc = [`${attributedTo}/followers`].concat(mentions);
- } else if (note.visibility === 'home') {
- to = [`${attributedTo}/followers`];
- cc = ['https://www.w3.org/ns/activitystreams#Public'].concat(mentions);
- } else if (note.visibility === 'followers') {
- to = [`${attributedTo}/followers`];
- cc = mentions;
- } else {
- to = mentions;
- }
-
- const mentionedUsers = note.mentions.length > 0 ? await Users.find({
- id: In(note.mentions)
- }) : [];
-
- const hashtagTags = (note.tags || []).map(tag => renderHashtag(tag));
- const mentionTags = mentionedUsers.map(u => renderMention(u));
-
- const files = await getPromisedFiles(note.fileIds);
-
- const text = note.text;
- let poll: Poll | undefined;
-
- if (note.hasPoll) {
- poll = await Polls.findOne({ noteId: note.id });
- }
-
- let apText = text;
- if (apText == null) apText = '';
-
- if (quote) {
- apText += `\n\nRE: ${quote}`;
- }
-
- const summary = note.cw === '' ? String.fromCharCode(0x200B) : note.cw;
-
- const content = toHtml(Object.assign({}, note, {
- text: apText
- }));
-
- const emojis = await getEmojis(note.emojis);
- const apemojis = emojis.map(emoji => renderEmoji(emoji));
-
- const tag = [
- ...hashtagTags,
- ...mentionTags,
- ...apemojis,
- ];
-
- const asPoll = poll ? {
- type: 'Question',
- content: toHtml(Object.assign({}, note, {
- text: text
- })),
- [poll.expiresAt && poll.expiresAt < new Date() ? 'closed' : 'endTime']: poll.expiresAt,
- [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
- type: 'Note',
- name: text,
- replies: {
- type: 'Collection',
- totalItems: poll!.votes[i]
- }
- }))
- } : {};
-
- const asTalk = isTalk ? {
- _misskey_talk: true
- } : {};
-
- return {
- id: `${config.url}/notes/${note.id}`,
- type: 'Note',
- attributedTo,
- summary,
- content,
- _misskey_content: text,
- _misskey_quote: quote,
- quoteUrl: quote,
- published: note.createdAt.toISOString(),
- to,
- cc,
- inReplyTo,
- attachment: files.map(renderDocument),
- sensitive: note.cw != null || files.some(file => file.isSensitive),
- tag,
- ...asPoll,
- ...asTalk
- };
-}
-
-export async function getEmojis(names: string[]): Promise<Emoji[]> {
- if (names == null || names.length === 0) return [];
-
- const emojis = await Promise.all(
- names.map(name => Emojis.findOne({
- name,
- host: null
- }))
- );
-
- return emojis.filter(emoji => emoji != null) as Emoji[];
-}
diff --git a/src/remote/activitypub/renderer/ordered-collection-page.ts b/src/remote/activitypub/renderer/ordered-collection-page.ts
deleted file mode 100644
index 2433358646..0000000000
--- a/src/remote/activitypub/renderer/ordered-collection-page.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Render OrderedCollectionPage
- * @param id URL of self
- * @param totalItems Number of total items
- * @param orderedItems Items
- * @param partOf URL of base
- * @param prev URL of prev page (optional)
- * @param next URL of next page (optional)
- */
-export default function(id: string, totalItems: any, orderedItems: any, partOf: string, prev?: string, next?: string) {
- const page = {
- id,
- partOf,
- type: 'OrderedCollectionPage',
- totalItems,
- orderedItems
- } as any;
-
- if (prev) page.prev = prev;
- if (next) page.next = next;
-
- return page;
-}
diff --git a/src/remote/activitypub/renderer/ordered-collection.ts b/src/remote/activitypub/renderer/ordered-collection.ts
deleted file mode 100644
index 68870a0ecd..0000000000
--- a/src/remote/activitypub/renderer/ordered-collection.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Render OrderedCollection
- * @param id URL of self
- * @param totalItems Total number of items
- * @param first URL of first page (optional)
- * @param last URL of last page (optional)
- * @param orderedItems attached objects (optional)
- */
-export default function(id: string | null, totalItems: any, first?: string, last?: string, orderedItems?: object) {
- const page: any = {
- id,
- type: 'OrderedCollection',
- totalItems,
- };
-
- if (first) page.first = first;
- if (last) page.last = last;
- if (orderedItems) page.orderedItems = orderedItems;
-
- return page;
-}
diff --git a/src/remote/activitypub/renderer/person.ts b/src/remote/activitypub/renderer/person.ts
deleted file mode 100644
index 7e94abddfc..0000000000
--- a/src/remote/activitypub/renderer/person.ts
+++ /dev/null
@@ -1,90 +0,0 @@
-import { URL } from 'url';
-import * as mfm from 'mfm-js';
-import renderImage from './image';
-import renderKey from './key';
-import config from '@/config/index';
-import { ILocalUser } from '@/models/entities/user';
-import { toHtml } from '../../../mfm/to-html';
-import { getEmojis } from './note';
-import renderEmoji from './emoji';
-import { IIdentifier } from '../models/identifier';
-import renderHashtag from './hashtag';
-import { DriveFiles, UserProfiles } from '@/models/index';
-import { getUserKeypair } from '@/misc/keypair-store';
-import { fnNameList } from '@/mfm/fn-name-list';
-
-export async function renderPerson(user: ILocalUser) {
- const id = `${config.url}/users/${user.id}`;
- const isSystem = !!user.username.match(/\./);
-
- const [avatar, banner, profile] = await Promise.all([
- user.avatarId ? DriveFiles.findOne(user.avatarId) : Promise.resolve(undefined),
- user.bannerId ? DriveFiles.findOne(user.bannerId) : Promise.resolve(undefined),
- UserProfiles.findOneOrFail(user.id)
- ]);
-
- const attachment: {
- type: 'PropertyValue',
- name: string,
- value: string,
- identifier?: IIdentifier
- }[] = [];
-
- if (profile.fields) {
- for (const field of profile.fields) {
- attachment.push({
- type: 'PropertyValue',
- name: field.name,
- value: (field.value != null && field.value.match(/^https?:/))
- ? `<a href="${new URL(field.value).href}" rel="me nofollow noopener" target="_blank">${new URL(field.value).href}</a>`
- : field.value
- });
- }
- }
-
- const emojis = await getEmojis(user.emojis);
- const apemojis = emojis.map(emoji => renderEmoji(emoji));
-
- const hashtagTags = (user.tags || []).map(tag => renderHashtag(tag));
-
- const tag = [
- ...apemojis,
- ...hashtagTags,
- ];
-
- const keypair = await getUserKeypair(user.id);
-
- const person = {
- type: isSystem ? 'Application' : user.isBot ? 'Service' : 'Person',
- id,
- inbox: `${id}/inbox`,
- outbox: `${id}/outbox`,
- followers: `${id}/followers`,
- following: `${id}/following`,
- featured: `${id}/collections/featured`,
- sharedInbox: `${config.url}/inbox`,
- endpoints: { sharedInbox: `${config.url}/inbox` },
- url: `${config.url}/@${user.username}`,
- preferredUsername: user.username,
- name: user.name,
- summary: profile.description ? toHtml(mfm.parse(profile.description, { fnNameList })) : null,
- icon: avatar ? renderImage(avatar) : null,
- image: banner ? renderImage(banner) : null,
- tag,
- manuallyApprovesFollowers: user.isLocked,
- discoverable: !!user.isExplorable,
- publicKey: renderKey(user, keypair, `#main-key`),
- isCat: user.isCat,
- attachment: attachment.length ? attachment : undefined
- } as any;
-
- if (profile?.birthday) {
- person['vcard:bday'] = profile.birthday;
- }
-
- if (profile?.location) {
- person['vcard:Address'] = profile.location;
- }
-
- return person;
-}
diff --git a/src/remote/activitypub/renderer/question.ts b/src/remote/activitypub/renderer/question.ts
deleted file mode 100644
index 246d599bab..0000000000
--- a/src/remote/activitypub/renderer/question.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-import { Note } from '@/models/entities/note';
-import { Poll } from '@/models/entities/poll';
-
-export default async function renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll) {
- const question = {
- type: 'Question',
- id: `${config.url}/questions/${note.id}`,
- actor: `${config.url}/users/${user.id}`,
- content: note.text || '',
- [poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
- name: text,
- _misskey_votes: poll.votes[i],
- replies: {
- type: 'Collection',
- totalItems: poll.votes[i]
- }
- }))
- };
-
- return question;
-}
diff --git a/src/remote/activitypub/renderer/read.ts b/src/remote/activitypub/renderer/read.ts
deleted file mode 100644
index 95357f64d3..0000000000
--- a/src/remote/activitypub/renderer/read.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-import { MessagingMessage } from '@/models/entities/messaging-message';
-
-export const renderReadActivity = (user: { id: User['id'] }, message: MessagingMessage) => ({
- type: 'Read',
- actor: `${config.url}/users/${user.id}`,
- object: message.uri
-});
diff --git a/src/remote/activitypub/renderer/reject.ts b/src/remote/activitypub/renderer/reject.ts
deleted file mode 100644
index 42beffecf2..0000000000
--- a/src/remote/activitypub/renderer/reject.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-
-export default (object: any, user: { id: User['id'] }) => ({
- type: 'Reject',
- actor: `${config.url}/users/${user.id}`,
- object
-});
diff --git a/src/remote/activitypub/renderer/remove.ts b/src/remote/activitypub/renderer/remove.ts
deleted file mode 100644
index 79d60edbaa..0000000000
--- a/src/remote/activitypub/renderer/remove.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-
-export default (user: { id: User['id'] }, target: any, object: any) => ({
- type: 'Remove',
- actor: `${config.url}/users/${user.id}`,
- target,
- object
-});
diff --git a/src/remote/activitypub/renderer/tombstone.ts b/src/remote/activitypub/renderer/tombstone.ts
deleted file mode 100644
index 553406b93b..0000000000
--- a/src/remote/activitypub/renderer/tombstone.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default (id: string) => ({
- id,
- type: 'Tombstone'
-});
diff --git a/src/remote/activitypub/renderer/undo.ts b/src/remote/activitypub/renderer/undo.ts
deleted file mode 100644
index 14115b788d..0000000000
--- a/src/remote/activitypub/renderer/undo.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import config from '@/config/index';
-import { ILocalUser, User } from '@/models/entities/user';
-
-export default (object: any, user: { id: User['id'] }) => {
- if (object == null) return null;
-
- return {
- type: 'Undo',
- actor: `${config.url}/users/${user.id}`,
- object,
- published: new Date().toISOString(),
- };
-};
diff --git a/src/remote/activitypub/renderer/update.ts b/src/remote/activitypub/renderer/update.ts
deleted file mode 100644
index 8bb415d117..0000000000
--- a/src/remote/activitypub/renderer/update.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import config from '@/config/index';
-import { User } from '@/models/entities/user';
-
-export default (object: any, user: { id: User['id'] }) => {
- const activity = {
- id: `${config.url}/users/${user.id}#updates/${new Date().getTime()}`,
- actor: `${config.url}/users/${user.id}`,
- type: 'Update',
- to: [ 'https://www.w3.org/ns/activitystreams#Public' ],
- object,
- published: new Date().toISOString(),
- } as any;
-
- return activity;
-};
diff --git a/src/remote/activitypub/renderer/vote.ts b/src/remote/activitypub/renderer/vote.ts
deleted file mode 100644
index ff038070f7..0000000000
--- a/src/remote/activitypub/renderer/vote.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import config from '@/config/index';
-import { Note } from '@/models/entities/note';
-import { IRemoteUser, User } from '@/models/entities/user';
-import { PollVote } from '@/models/entities/poll-vote';
-import { Poll } from '@/models/entities/poll';
-
-export default async function renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: IRemoteUser): Promise<any> {
- return {
- id: `${config.url}/users/${user.id}#votes/${vote.id}/activity`,
- actor: `${config.url}/users/${user.id}`,
- type: 'Create',
- to: [pollOwner.uri],
- published: new Date().toISOString(),
- object: {
- id: `${config.url}/users/${user.id}#votes/${vote.id}`,
- type: 'Note',
- attributedTo: `${config.url}/users/${user.id}`,
- to: [pollOwner.uri],
- inReplyTo: note.uri,
- name: poll.choices[vote.choice]
- }
- };
-}
diff --git a/src/remote/activitypub/request.ts b/src/remote/activitypub/request.ts
deleted file mode 100644
index d6ced630c1..0000000000
--- a/src/remote/activitypub/request.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import config from '@/config/index';
-import { getUserKeypair } from '@/misc/keypair-store';
-import { User } from '@/models/entities/user';
-import { getResponse } from '../../misc/fetch';
-import { createSignedPost, createSignedGet } from './ap-request';
-
-export default async (user: { id: User['id'] }, url: string, object: any) => {
- const body = JSON.stringify(object);
-
- const keypair = await getUserKeypair(user.id);
-
- const req = createSignedPost({
- key: {
- privateKeyPem: keypair.privateKey,
- keyId: `${config.url}/users/${user.id}#main-key`
- },
- url,
- body,
- additionalHeaders: {
- 'User-Agent': config.userAgent,
- }
- });
-
- await getResponse({
- url,
- method: req.request.method,
- headers: req.request.headers,
- body,
- });
-};
-
-/**
- * Get AP object with http-signature
- * @param user http-signature user
- * @param url URL to fetch
- */
-export async function signedGet(url: string, user: { id: User['id'] }) {
- const keypair = await getUserKeypair(user.id);
-
- const req = createSignedGet({
- key: {
- privateKeyPem: keypair.privateKey,
- keyId: `${config.url}/users/${user.id}#main-key`
- },
- url,
- additionalHeaders: {
- 'User-Agent': config.userAgent,
- }
- });
-
- const res = await getResponse({
- url,
- method: req.request.method,
- headers: req.request.headers
- });
-
- return await res.json();
-}
diff --git a/src/remote/activitypub/resolver.ts b/src/remote/activitypub/resolver.ts
deleted file mode 100644
index f392a65e3a..0000000000
--- a/src/remote/activitypub/resolver.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import config from '@/config/index';
-import { getJson } from '@/misc/fetch';
-import { ILocalUser } from '@/models/entities/user';
-import { getInstanceActor } from '@/services/instance-actor';
-import { signedGet } from './request';
-import { IObject, isCollectionOrOrderedCollection, ICollection, IOrderedCollection } from './type';
-import { fetchMeta } from '@/misc/fetch-meta';
-import { extractDbHost } from '@/misc/convert-host';
-
-export default class Resolver {
- private history: Set<string>;
- private user?: ILocalUser;
-
- constructor() {
- this.history = new Set();
- }
-
- public getHistory(): string[] {
- return Array.from(this.history);
- }
-
- public async resolveCollection(value: string | IObject): Promise<ICollection | IOrderedCollection> {
- const collection = typeof value === 'string'
- ? await this.resolve(value)
- : value;
-
- if (isCollectionOrOrderedCollection(collection)) {
- return collection;
- } else {
- throw new Error(`unrecognized collection type: ${collection.type}`);
- }
- }
-
- public async resolve(value: string | IObject): Promise<IObject> {
- if (value == null) {
- throw new Error('resolvee is null (or undefined)');
- }
-
- if (typeof value !== 'string') {
- return value;
- }
-
- if (this.history.has(value)) {
- throw new Error('cannot resolve already resolved one');
- }
-
- this.history.add(value);
-
- const meta = await fetchMeta();
- const host = extractDbHost(value);
- if (meta.blockedHosts.includes(host)) {
- throw new Error('Instance is blocked');
- }
-
- if (config.signToActivityPubGet && !this.user) {
- this.user = await getInstanceActor();
- }
-
- const object = this.user
- ? await signedGet(value, this.user)
- : await getJson(value, 'application/activity+json, application/ld+json');
-
- if (object == null || (
- Array.isArray(object['@context']) ?
- !object['@context'].includes('https://www.w3.org/ns/activitystreams') :
- object['@context'] !== 'https://www.w3.org/ns/activitystreams'
- )) {
- throw new Error('invalid response');
- }
-
- return object;
- }
-}
diff --git a/src/remote/activitypub/type.ts b/src/remote/activitypub/type.ts
deleted file mode 100644
index 2051d2624d..0000000000
--- a/src/remote/activitypub/type.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-export type obj = { [x: string]: any };
-export type ApObject = IObject | string | (IObject | string)[];
-
-export interface IObject {
- '@context': string | obj | obj[];
- type: string | string[];
- id?: string;
- summary?: string;
- published?: string;
- cc?: ApObject;
- to?: ApObject;
- attributedTo: ApObject;
- attachment?: any[];
- inReplyTo?: any;
- replies?: ICollection;
- content?: string;
- name?: string;
- startTime?: Date;
- endTime?: Date;
- icon?: any;
- image?: any;
- url?: ApObject;
- href?: string;
- tag?: IObject | IObject[];
- sensitive?: boolean;
-}
-
-/**
- * Get array of ActivityStreams Objects id
- */
-export function getApIds(value: ApObject | undefined): string[] {
- if (value == null) return [];
- const array = Array.isArray(value) ? value : [value];
- return array.map(x => getApId(x));
-}
-
-/**
- * Get first ActivityStreams Object id
- */
-export function getOneApId(value: ApObject): string {
- const firstOne = Array.isArray(value) ? value[0] : value;
- return getApId(firstOne);
-}
-
-/**
- * Get ActivityStreams Object id
- */
-export function getApId(value: string | IObject): string {
- if (typeof value === 'string') return value;
- if (typeof value.id === 'string') return value.id;
- throw new Error(`cannot detemine id`);
-}
-
-/**
- * Get ActivityStreams Object type
- */
-export function getApType(value: IObject): string {
- if (typeof value.type === 'string') return value.type;
- if (Array.isArray(value.type) && typeof value.type[0] === 'string') return value.type[0];
- throw new Error(`cannot detect type`);
-}
-
-export function getOneApHrefNullable(value: ApObject | undefined): string | undefined {
- const firstOne = Array.isArray(value) ? value[0] : value;
- return getApHrefNullable(firstOne);
-}
-
-export function getApHrefNullable(value: string | IObject | undefined): string | undefined {
- if (typeof value === 'string') return value;
- if (typeof value?.href === 'string') return value.href;
- return undefined;
-}
-
-export interface IActivity extends IObject {
- //type: 'Activity';
- actor: IObject | string;
- object: IObject | string;
- target?: IObject | string;
- /** LD-Signature */
- signature?: {
- type: string;
- created: Date;
- creator: string;
- domain?: string;
- nonce?: string;
- signatureValue: string;
- };
-}
-
-export interface ICollection extends IObject {
- type: 'Collection';
- totalItems: number;
- items: ApObject;
-}
-
-export interface IOrderedCollection extends IObject {
- type: 'OrderedCollection';
- totalItems: number;
- orderedItems: ApObject;
-}
-
-export const validPost = ['Note', 'Question', 'Article', 'Audio', 'Document', 'Image', 'Page', 'Video', 'Event'];
-
-export const isPost = (object: IObject): object is IPost =>
- validPost.includes(getApType(object));
-
-export interface IPost extends IObject {
- type: 'Note' | 'Question' | 'Article' | 'Audio' | 'Document' | 'Image' | 'Page' | 'Video' | 'Event';
- _misskey_content?: string;
- _misskey_quote?: string;
- quoteUrl?: string;
- _misskey_talk: boolean;
-}
-
-export interface IQuestion extends IObject {
- type: 'Note' | 'Question';
- _misskey_content?: string;
- _misskey_quote?: string;
- quoteUrl?: string;
- oneOf?: IQuestionChoice[];
- anyOf?: IQuestionChoice[];
- endTime?: Date;
- closed?: Date;
-}
-
-export const isQuestion = (object: IObject): object is IQuestion =>
- getApType(object) === 'Note' || getApType(object) === 'Question';
-
-interface IQuestionChoice {
- name?: string;
- replies?: ICollection;
- _misskey_votes?: number;
-}
-export interface ITombstone extends IObject {
- type: 'Tombstone';
- formerType?: string;
- deleted?: Date;
-}
-
-export const isTombstone = (object: IObject): object is ITombstone =>
- getApType(object) === 'Tombstone';
-
-export const validActor = ['Person', 'Service', 'Group', 'Organization', 'Application'];
-
-export const isActor = (object: IObject): object is IActor =>
- validActor.includes(getApType(object));
-
-export interface IActor extends IObject {
- type: 'Person' | 'Service' | 'Organization' | 'Group' | 'Application';
- name?: string;
- preferredUsername?: string;
- manuallyApprovesFollowers?: boolean;
- discoverable?: boolean;
- inbox: string;
- sharedInbox?: string; // 後方互換性のため
- publicKey?: {
- id: string;
- publicKeyPem: string;
- };
- followers?: string | ICollection | IOrderedCollection;
- following?: string | ICollection | IOrderedCollection;
- featured?: string | IOrderedCollection;
- outbox: string | IOrderedCollection;
- endpoints?: {
- sharedInbox?: string;
- };
- 'vcard:bday'?: string;
- 'vcard:Address'?: string;
-}
-
-export const isCollection = (object: IObject): object is ICollection =>
- getApType(object) === 'Collection';
-
-export const isOrderedCollection = (object: IObject): object is IOrderedCollection =>
- getApType(object) === 'OrderedCollection';
-
-export const isCollectionOrOrderedCollection = (object: IObject): object is ICollection | IOrderedCollection =>
- isCollection(object) || isOrderedCollection(object);
-
-export interface IApPropertyValue extends IObject {
- type: 'PropertyValue';
- identifier: IApPropertyValue;
- name: string;
- value: string;
-}
-
-export const isPropertyValue = (object: IObject): object is IApPropertyValue =>
- object &&
- getApType(object) === 'PropertyValue' &&
- typeof object.name === 'string' &&
- typeof (object as any).value === 'string';
-
-export interface IApMention extends IObject {
- type: 'Mention';
- href: string;
-}
-
-export const isMention = (object: IObject): object is IApMention=>
- getApType(object) === 'Mention' &&
- typeof object.href === 'string';
-
-export interface IApHashtag extends IObject {
- type: 'Hashtag';
- name: string;
-}
-
-export const isHashtag = (object: IObject): object is IApHashtag =>
- getApType(object) === 'Hashtag' &&
- typeof object.name === 'string';
-
-export interface IApEmoji extends IObject {
- type: 'Emoji';
- updated: Date;
-}
-
-export const isEmoji = (object: IObject): object is IApEmoji =>
- getApType(object) === 'Emoji' && !Array.isArray(object.icon) && object.icon.url != null;
-
-export interface ICreate extends IActivity {
- type: 'Create';
-}
-
-export interface IDelete extends IActivity {
- type: 'Delete';
-}
-
-export interface IUpdate extends IActivity {
- type: 'Update';
-}
-
-export interface IRead extends IActivity {
- type: 'Read';
-}
-
-export interface IUndo extends IActivity {
- type: 'Undo';
-}
-
-export interface IFollow extends IActivity {
- type: 'Follow';
-}
-
-export interface IAccept extends IActivity {
- type: 'Accept';
-}
-
-export interface IReject extends IActivity {
- type: 'Reject';
-}
-
-export interface IAdd extends IActivity {
- type: 'Add';
-}
-
-export interface IRemove extends IActivity {
- type: 'Remove';
-}
-
-export interface ILike extends IActivity {
- type: 'Like' | 'EmojiReaction' | 'EmojiReact';
- _misskey_reaction?: string;
-}
-
-export interface IAnnounce extends IActivity {
- type: 'Announce';
-}
-
-export interface IBlock extends IActivity {
- type: 'Block';
-}
-
-export interface IFlag extends IActivity {
- type: 'Flag';
-}
-
-export const isCreate = (object: IObject): object is ICreate => getApType(object) === 'Create';
-export const isDelete = (object: IObject): object is IDelete => getApType(object) === 'Delete';
-export const isUpdate = (object: IObject): object is IUpdate => getApType(object) === 'Update';
-export const isRead = (object: IObject): object is IRead => getApType(object) === 'Read';
-export const isUndo = (object: IObject): object is IUndo => getApType(object) === 'Undo';
-export const isFollow = (object: IObject): object is IFollow => getApType(object) === 'Follow';
-export const isAccept = (object: IObject): object is IAccept => getApType(object) === 'Accept';
-export const isReject = (object: IObject): object is IReject => getApType(object) === 'Reject';
-export const isAdd = (object: IObject): object is IAdd => getApType(object) === 'Add';
-export const isRemove = (object: IObject): object is IRemove => getApType(object) === 'Remove';
-export const isLike = (object: IObject): object is ILike => getApType(object) === 'Like' || getApType(object) === 'EmojiReaction' || getApType(object) === 'EmojiReact';
-export const isAnnounce = (object: IObject): object is IAnnounce => getApType(object) === 'Announce';
-export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
-export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
diff --git a/src/remote/logger.ts b/src/remote/logger.ts
deleted file mode 100644
index 9ffad4d716..0000000000
--- a/src/remote/logger.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-import Logger from '@/services/logger';
-
-export const remoteLogger = new Logger('remote', 'cyan');
diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts
deleted file mode 100644
index a12396abc8..0000000000
--- a/src/remote/resolve-user.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { URL } from 'url';
-import webFinger from './webfinger';
-import config from '@/config/index';
-import { createPerson, updatePerson } from './activitypub/models/person';
-import { remoteLogger } from './logger';
-import * as chalk from 'chalk';
-import { User, IRemoteUser } from '@/models/entities/user';
-import { Users } from '@/models/index';
-import { toPuny } from '@/misc/convert-host';
-
-const logger = remoteLogger.createSubLogger('resolve-user');
-
-export async function resolveUser(username: string, host: string | null, option?: any, resync = false): Promise<User> {
- const usernameLower = username.toLowerCase();
-
- if (host == null) {
- logger.info(`return local user: ${usernameLower}`);
- return await Users.findOne({ usernameLower, host: null }).then(u => {
- if (u == null) {
- throw new Error('user not found');
- } else {
- return u;
- }
- });
- }
-
- host = toPuny(host);
-
- if (config.host == host) {
- logger.info(`return local user: ${usernameLower}`);
- return await Users.findOne({ usernameLower, host: null }).then(u => {
- if (u == null) {
- throw new Error('user not found');
- } else {
- return u;
- }
- });
- }
-
- const user = await Users.findOne({ usernameLower, host }, option) as IRemoteUser;
-
- const acctLower = `${usernameLower}@${host}`;
-
- if (user == null) {
- const self = await resolveSelf(acctLower);
-
- logger.succ(`return new remote user: ${chalk.magenta(acctLower)}`);
- return await createPerson(self.href);
- }
-
- // resyncオプション OR ユーザー情報が古い場合は、WebFilgerからやりなおして返す
- if (resync || user.lastFetchedAt == null || Date.now() - user.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
- // 繋がらないインスタンスに何回も試行するのを防ぐ, 後続の同様処理の連続試行を防ぐ ため 試行前にも更新する
- await Users.update(user.id, {
- lastFetchedAt: new Date(),
- });
-
- logger.info(`try resync: ${acctLower}`);
- const self = await resolveSelf(acctLower);
-
- if (user.uri !== self.href) {
- // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping.
- logger.info(`uri missmatch: ${acctLower}`);
- logger.info(`recovery missmatch uri for (username=${username}, host=${host}) from ${user.uri} to ${self.href}`);
-
- // validate uri
- const uri = new URL(self.href);
- if (uri.hostname !== host) {
- throw new Error(`Invalid uri`);
- }
-
- await Users.update({
- usernameLower,
- host: host
- }, {
- uri: self.href
- });
- } else {
- logger.info(`uri is fine: ${acctLower}`);
- }
-
- await updatePerson(self.href);
-
- logger.info(`return resynced remote user: ${acctLower}`);
- return await Users.findOne({ uri: self.href }).then(u => {
- if (u == null) {
- throw new Error('user not found');
- } else {
- return u;
- }
- });
- }
-
- logger.info(`return existing remote user: ${acctLower}`);
- return user;
-}
-
-async function resolveSelf(acctLower: string) {
- logger.info(`WebFinger for ${chalk.yellow(acctLower)}`);
- const finger = await webFinger(acctLower).catch(e => {
- logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: ${ e.statusCode || e.message }`);
- throw new Error(`Failed to WebFinger for ${acctLower}: ${ e.statusCode || e.message }`);
- });
- const self = finger.links.find(link => link.rel != null && link.rel.toLowerCase() === 'self');
- if (!self) {
- logger.error(`Failed to WebFinger for ${chalk.yellow(acctLower)}: self link not found`);
- throw new Error('self link not found');
- }
- return self;
-}
diff --git a/src/remote/webfinger.ts b/src/remote/webfinger.ts
deleted file mode 100644
index f63fd03628..0000000000
--- a/src/remote/webfinger.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import { URL } from 'url';
-import { getJson } from '@/misc/fetch';
-import { query as urlQuery } from '@/prelude/url';
-
-type ILink = {
- href: string;
- rel?: string;
-};
-
-type IWebFinger = {
- links: ILink[];
- subject: string;
-};
-
-export default async function(query: string): Promise<IWebFinger> {
- const url = genUrl(query);
-
- return await getJson(url, 'application/jrd+json, application/json');
-}
-
-function genUrl(query: string) {
- if (query.match(/^https?:\/\//)) {
- const u = new URL(query);
- return `${u.protocol}//${u.hostname}/.well-known/webfinger?` + urlQuery({ resource: query });
- }
-
- const m = query.match(/^([^@]+)@(.*)/);
- if (m) {
- const hostname = m[2];
- return `https://${hostname}/.well-known/webfinger?` + urlQuery({ resource: `acct:${query}` });
- }
-
- throw new Error(`Invalid query (${query})`);
-}