summaryrefslogtreecommitdiff
path: root/packages/frontend/src/utility/warning-external-website.ts
blob: 33cf379b500491e1bf412012343254cebcb89641 (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
/*
 * SPDX-FileCopyrightText: syuilo and misskey-project
 * SPDX-License-Identifier: AGPL-3.0-only
 */

import { instance } from '@/instance.js';
import * as os from '@/os.js';
import MkUrlWarningDialog from '@/components/MkUrlWarningDialog.vue';
import { prefer } from '@/preferences';

const isRegExp = /^\/(.+)\/(.*)$/;

function extractHostname(maybeUrl: string): string | null {
	try {
		const url = new URL(maybeUrl);
		return url.host;
	} catch {
		return null;
	}
}

export async function warningExternalWebsite(url: string) {
	const hostname = extractHostname(url);

	if (!hostname) return false;

	const isTrustedByInstance = instance.trustedLinkUrlPatterns.some(expression => {
		const r = isRegExp.exec(expression);

		if (r) {
			return new RegExp(r[1], r[2]).test(url);
		} else if (expression.includes(' ')) {
			return expression.split(' ').every(keyword => url.includes(keyword));
		} else {
			return `.${hostname}`.endsWith(`.${expression}`);
		}
	});

	const isTrustedByUser = prefer.r.trustedDomains.value.includes(hostname);
	const isDisabledByUser = !prefer.r.warnExternalUrl.value;

	if (!isTrustedByInstance && !isTrustedByUser && !isDisabledByUser) {
		const confirm = await new Promise<{ canceled: boolean }>(resolve => {
			const { dispose } = os.popup(MkUrlWarningDialog, {
				url,
			}, {
				done: result => {
					resolve(result ?? { canceled: true });
				},
				closed: () => dispose(),
			});
		});

		if (confirm.canceled) return false;

		return window.open(url, '_blank', 'nofollow noopener popup=false');
	}

	return window.open(url, '_blank', 'nofollow noopener popup=false');
}