summaryrefslogtreecommitdiff
path: root/src/remote/activitypub/resolve-person.ts
blob: ddb8d68715e6a99150fa671659362925fc369347 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import { JSDOM } from 'jsdom';
import { toUnicode } from 'punycode';
import parseAcct from '../../acct/parse';
import config from '../../config';
import User, { validateUsername, isValidName, isValidDescription } from '../../models/user';
import webFinger from '../webfinger';
import Resolver from './resolver';
import uploadFromUrl from '../../services/drive/upload-from-url';
import { isCollectionOrOrderedCollection } from './type';

export default async (value, verifier?: string) => {
	const id = value.id || value;
	const localPrefix = config.url + '/@';

	if (id.startsWith(localPrefix)) {
		return User.findOne(parseAcct(id.slice(localPrefix)));
	}

	const resolver = new Resolver();

	const object = await resolver.resolve(value) as any;

	if (
		object == null ||
		object.type !== 'Person' ||
		typeof object.preferredUsername !== 'string' ||
		!validateUsername(object.preferredUsername) ||
		!isValidName(object.name == '' ? null : object.name) ||
		!isValidDescription(object.summary)
	) {
		throw new Error('invalid person');
	}

	const [followersCount = 0, followingCount = 0, notesCount = 0, finger] = await Promise.all([
		resolver.resolve(object.followers).then(
			resolved => isCollectionOrOrderedCollection(resolved) ? resolved.totalItems : undefined,
			() => undefined
		),
		resolver.resolve(object.following).then(
			resolved => isCollectionOrOrderedCollection(resolved) ? resolved.totalItems : undefined,
			() => undefined
		),
		resolver.resolve(object.outbox).then(
			resolved => isCollectionOrOrderedCollection(resolved) ? resolved.totalItems : undefined,
			() => undefined
		),
		webFinger(id, verifier)
	]);

	const host = toUnicode(finger.subject.replace(/^.*?@/, ''));
	const hostLower = host.replace(/[A-Z]+/, matched => matched.toLowerCase());
	const summaryDOM = JSDOM.fragment(object.summary);

	// Create user
	const user = await User.insert({
		avatarId: null,
		bannerId: null,
		createdAt: Date.parse(object.published) || null,
		description: summaryDOM.textContent,
		followersCount,
		followingCount,
		notesCount,
		name: object.name,
		driveCapacity: 1024 * 1024 * 8, // 8MiB
		username: object.preferredUsername,
		usernameLower: object.preferredUsername.toLowerCase(),
		host,
		hostLower,
		publicKey: {
			id: object.publicKey.id,
			publicKeyPem: object.publicKey.publicKeyPem
		},
		inbox: object.inbox,
		uri: id
	});

	const [avatarId, bannerId] = (await Promise.all([
		object.icon,
		object.image
	].map(img =>
		img == null
			? Promise.resolve(null)
			: uploadFromUrl(img.url, user)
	))).map(file => file != null ? file._id : null);

	User.update({ _id: user._id }, { $set: { avatarId, bannerId } });

	user.avatarId = avatarId;
	user.bannerId = bannerId;

	return user;
};