diff --git a/src/remote/resolve-user.ts b/src/remote/resolve-user.ts index 15401557f5..d562d3512e 100644 --- a/src/remote/resolve-user.ts +++ b/src/remote/resolve-user.ts @@ -1,13 +1,18 @@ import { toUnicode, toASCII } from 'punycode'; -import User, { IUser } from '../models/user'; +import User, { IUser, IRemoteUser } from '../models/user'; import webFinger from './webfinger'; import config from '../config'; -import { createPerson } from './activitypub/models/person'; +import { createPerson, updatePerson } from './activitypub/models/person'; +import { URL } from 'url'; +import * as debug from 'debug'; -export default async (username: string, _host: string, option?: any): Promise => { +const log = debug('misskey:remote:resolve-user'); + +export default async (username: string, _host: string, option?: any, resync?: boolean): Promise => { const usernameLower = username.toLowerCase(); if (_host == null) { + log(`return local user: ${usernameLower}`); return await User.findOne({ usernameLower, host: null }); } @@ -15,22 +20,64 @@ export default async (username: string, _host: string, option?: any): Promise link.rel && link.rel.toLowerCase() === 'self'); - if (!self) { - throw new Error('self link not found'); - } - - user = await createPerson(self.href); + log(`return new remote user: ${acctLower}`); + return await createPerson(self.href); } + if (resync) { + log(`try resync: ${acctLower}`); + const self = await resolveSelf(acctLower); + + if ((user as IRemoteUser).uri !== self.href) { + // if uri mismatch, Fix (user@host <=> AP's Person id(IRemoteUser.uri)) mapping. + log(`uri missmatch: ${acctLower}`); + console.log(`recovery missmatch uri for (username=${username}, host=${host}) from ${(user as IRemoteUser).uri} to ${self.href}`); + + // validate uri + const uri = new URL(self.href); + if (uri.hostname !== hostAscii) { + throw new Error(`Invalied uri`); + } + + await User.update({ + usernameLower, + host: host + }, { + $set: { + uri: self.href + } + }); + + await updatePerson(self.href); + + log(`return resynced remote user: ${acctLower}`); + return await User.findOne({ uri: self.href }); + } else { + log(`uri is fine: ${acctLower}`); + } + } + + log(`return existing remote user: ${acctLower}`); return user; }; + +async function resolveSelf(acctLower: string) { + log(`WebFinger for ${acctLower}`); + const finger = await webFinger(acctLower); + const self = finger.links.find(link => link.rel && link.rel.toLowerCase() === 'self'); + if (!self) { + throw new Error('self link not found'); + } + return self; +} diff --git a/src/server/api/endpoints/users/show.ts b/src/server/api/endpoints/users/show.ts index 8ec0eb8dd9..67408deff3 100644 --- a/src/server/api/endpoints/users/show.ts +++ b/src/server/api/endpoints/users/show.ts @@ -26,6 +26,10 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => const [host, hostErr] = $.str.optional.nullable.get(params.host); if (hostErr) return rej('invalid host param'); + // Get 'resync' parameter + const [resync = false, resyncErr] = $.bool.optional.get(params.resync); + if (resyncErr) return rej('invalid resync param'); + if (userIds) { const users = await User.find({ _id: { @@ -40,7 +44,7 @@ export default (params: any, me: ILocalUser) => new Promise(async (res, rej) => // Lookup user if (typeof host === 'string') { try { - user = await resolveRemoteUser(username, host, cursorOption); + user = await resolveRemoteUser(username, host, cursorOption, resync); } catch (e) { console.warn(`failed to resolve remote user: ${e}`); return rej('failed to resolve remote user'); diff --git a/src/tools/resync-remote-user.ts b/src/tools/resync-remote-user.ts new file mode 100644 index 0000000000..3a63512f45 --- /dev/null +++ b/src/tools/resync-remote-user.ts @@ -0,0 +1,32 @@ +import parseAcct from "../misc/acct/parse"; +import resolveUser from '../remote/resolve-user'; +import * as debug from 'debug'; + +debug.enable('*'); + +async function main(acct: string): Promise { + const { username, host } = parseAcct(acct); + await resolveUser(username, host, {}, true); +} + +// get args +const args = process.argv.slice(2); +let acct = args[0]; + +// normalize args +acct = acct.replace(/^@/, ''); + +// check args +if (!acct.match(/^\w+@\w/)) { + throw `Invalied acct format. Valied format are user@host`; +} + +console.log(`resync ${acct}`); + +main(acct).then(() => { + console.log('success'); + process.exit(0); +}).catch(e => { + console.warn(e); + process.exit(1); +});