Files
harbor-net/src/web/utils/clientControls.js
Dmitriy Petrov 84efbe7450
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 12s
Build and Deploy Gateway / deploy (push) Successful in 1s
Rename product to Harbor and refine connection timer
2026-07-11 13:20:32 +03:00

120 lines
4.1 KiB
JavaScript

export function connectionAction({ connected, selectedTag, configExists }) {
if (connected) return { type: 'stop' };
if (selectedTag) return { type: 'apply', selectedTag };
if (configExists) return { type: 'restart' };
return null;
}
export function formatConnectionDuration(startedAt, now = Date.now()) {
const { totalHours, minutes, seconds } = connectionDurationParts(startedAt, now);
return [totalHours, minutes.value, seconds.value]
.map((part) => String(part).padStart(2, '0'))
.join(':');
}
function durationLabel(value, forms) {
const mod10 = value % 10;
const mod100 = value % 100;
return mod10 === 1 && mod100 !== 11
? forms[0]
: mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14) ? forms[1] : forms[2];
}
export function connectionDurationParts(startedAt, now = Date.now()) {
const started = Date.parse(startedAt);
const totalSeconds = Number.isFinite(started)
? Math.max(0, Math.floor((now - started) / 1000))
: 0;
const days = Math.floor(totalSeconds / 86_400);
const totalHours = Math.floor(totalSeconds / 3600);
const hours = Math.floor((totalSeconds % 86_400) / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
return {
totalHours,
days: { value: days, label: durationLabel(days, ['день', 'дня', 'дней']) },
hours: { value: hours, label: durationLabel(hours, ['час', 'часа', 'часов']) },
minutes: { value: minutes, label: durationLabel(minutes, ['минута', 'минуты', 'минут']) },
seconds: { value: seconds, label: durationLabel(seconds, ['секунда', 'секунды', 'секунд']) },
};
}
export function formatConnectionDurationWords(startedAt, now = Date.now()) {
const { days, hours, minutes, seconds } = connectionDurationParts(startedAt, now);
return [
days.value && `${days.value} ${days.label}`,
hours.value && `${hours.value} ${hours.label}`,
minutes.value && `${minutes.value} ${minutes.label}`,
`${seconds.value} ${seconds.label}`,
].filter(Boolean).join(' ');
}
export function subscriptionDomain(subscriptionHost) {
const value = String(subscriptionHost || '');
try {
return new URL(value).host;
} catch {
return value.split('/')[0];
}
}
export function localProxyUrls(port = 8082, host = '127.0.0.1') {
const urlHost = host.includes(':') && !host.startsWith('[') ? `[${host}]` : host;
return {
socks5: `socks5://${urlHost}:${port}`,
http: `http://${urlHost}:${port}`,
};
}
export async function copyText(text, options = {}) {
const clipboard = options.clipboard ?? globalThis.navigator?.clipboard;
const documentRef = options.documentRef ?? globalThis.document;
if (documentRef?.execCommand) {
const textarea = documentRef.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
documentRef.body.append(textarea);
textarea.select();
const copied = documentRef.execCommand('copy');
textarea.remove();
if (copied) return;
}
if (!clipboard?.writeText) throw new Error('Copy failed');
await clipboard.writeText(text);
}
export function subscriptionUsage(userInfo = {}) {
const upload = Math.max(0, Number(userInfo.upload) || 0);
const download = Math.max(0, Number(userInfo.download) || 0);
const total = Math.max(0, Number(userInfo.total) || 0);
const used = upload + download;
return {
upload,
download,
total,
used,
percent: total ? Math.min(100, (used / total) * 100) : null,
expiresAt: userInfo.expire ? new Date(Number(userInfo.expire) * 1000) : null,
};
}
export function subscriptionDaysLeft(expiresAt, now = Date.now()) {
const days = Math.ceil((expiresAt?.getTime() - now) / 86_400_000);
if (!Number.isFinite(days)) return '';
if (days <= 0) return 'срок истёк';
const mod10 = days % 10;
const mod100 = days % 100;
const unit = mod10 === 1 && mod100 !== 11
? 'день'
: mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14) ? 'дня' : 'дней';
return `${days === 1 ? 'остался' : 'осталось'} ${days} ${unit}`;
}