teasanctuary.ru/src/lib/util/LinkResolver.ts

83 lines
No EOL
3 KiB
TypeScript

const icons: Record<string, string> = {
none: 'material-symbols:link',
'steamcommunity.com': 'simple-icons:steam',
'twitter.com': 'simple-icons:x',
'x.com': 'simple-icons:x',
'github.com': 'simple-icons:github',
'youtube.com': 'simple-icons:youtube',
'itch.io': 'simple-icons:itchdotio',
'discord.gg': 'simple-icons:discord',
'gamebanana.com': 'simple-icons:gamebanana',
'bsky.app': 'simple-icons:bluesky',
'bsky.social': 'simple-icons:bluesky',
// https://хамяк.рф
'xn--80auf8a2c.xn--p1ai': 'fluent-emoji-high-contrast:hamster',
'teasanctuary.ru': '/icons/tea-sanctuary-white.svg',
'hl.teasanctuary.ru': '/icons/half-life.svg',
'git.teasanctuary.ru': 'devicon-plain:git',
localhost: '/icons/tea-sanctuary-white.svg',
email: 'material-symbols:alternate-email'
};
// Особые случаи, когда одним доменом второго уровня не ограничишься (например, randomtrash.itch.io)
const specialResolvers: Record<string, (url: URL) => string> = {
'teasanctuary.ru': (url) => {
// Домены третьего уровня и выше
const prefix = url.hostname.split('.').toReversed();
prefix.shift();
prefix.shift();
if (prefix[0] === "hl") {
return 'hl.teasanctuary.ru';
}
if (prefix[0] === "git") {
return 'git.teasanctuary.ru';
}
return 'teasanctuary.ru';
},
// Игнорируем имя пользователя
'bsky.app': (url) => 'bsky.app',
'bsky.social': (url) => 'bsky.social',
'itch.io': (url) => 'itch.io'
}
function getIconFromUrl(url: URL): string | undefined {
const href = url.href;
if (href.startsWith('mailto:'))
return 'email';
const hostname = url.hostname;
const secondLevel = hostname.match(/(([A-Za-z0-9\-])+\.([A-Za-z0-9\-])+)$/)?.at(0) ?? '';
if (specialResolvers[secondLevel])
return icons[specialResolvers[secondLevel](url)];
return icons[hostname];
}
export function tryGetIcon(link: string): string {
let url: URL;
try {
url = new URL(link);
} catch {
return icons['none'];
}
return getIconFromUrl(url) ?? icons['none'];
}
/**
* Проверка на "локальность" ссылки - принадлежность её этому сайту. Используется, например,
* в социальных ссылках, чтобы не открывать новую вкладку для страниц сайта.
* @param link Локальная либо глобальная ссылка
* @returns true, если ссылка не ведёт за пределы сайта
*/
export function isLinkLocal(link: string): boolean {
let url: URL;
try {
url = new URL(link);
} catch {
return true;
}
// Поддомены teasanctuary.ru должны всегда быть "внешними"
return url.hostname === "" || url.hostname === "teasanctuary.ru";
}