Админка + индикатор подписки + автопродление + де-брендинг входа
- /admin: дашборд (активные подписки, платежи, заходы, документы), доступ по ADMIN_TOKEN - Уникальные посетители (cookie p2s_vid) + экспорт документов в CSV - Индикатор активной подписки: бейдж в шапке + карточка на главной (/api/subscription) - Автопродление ЮKassa: save_payment_method + рекуррентные списания + фоновый цикл - Кнопка входа де-брендирована (нейтральная иконка, «Войти / Регистрация») Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -850,8 +850,8 @@
|
||||
|
||||
<div id="header-auth" style="position:relative;">
|
||||
<button id="tg-login-header-btn" onclick="toggleLoginMenu(event)" style="display:none;">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="white"><path d="M12 0C5.373 0 0 5.373 0 12s5.373 12 12 12 12-5.373 12-12S18.627 0 12 0zm5.894 8.221l-1.97 9.28c-.145.658-.537.818-1.084.508l-3-2.21-1.447 1.394c-.16.16-.295.295-.605.295l.213-3.053 5.56-5.023c.242-.213-.054-.333-.373-.12L8.32 13.617l-2.96-.924c-.643-.204-.657-.643.136-.953l11.57-4.461c.537-.194 1.006.131.828.942z"/></svg>
|
||||
<span data-i18n="login_btn">Войти</span>
|
||||
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><path d="M10 17l5-5-5-5"/><path d="M15 12H3"/></svg>
|
||||
<span data-i18n="login_btn">Войти / Регистрация</span>
|
||||
</button>
|
||||
<div id="login-menu" style="display:none;position:absolute;right:0;top:calc(100% + 6px);background:#fff;border:1px solid var(--border,#d0d7e2);border-radius:10px;box-shadow:0 6px 20px rgba(0,0,0,.12);padding:6px;z-index:50;min-width:190px;">
|
||||
<button onclick="startTgAuth();closeLoginMenu();" style="display:flex;align-items:center;gap:8px;width:100%;padding:9px 12px;background:none;border:none;border-radius:8px;cursor:pointer;font-family:inherit;font-size:14px;color:#1c2b45;text-align:left;">
|
||||
@@ -874,6 +874,10 @@
|
||||
<div id="tg-user-info" style="display:none;">
|
||||
<img id="tg-user-photo" src="" alt="" onerror="this.style.display='none'">
|
||||
<span id="tg-user-name"></span>
|
||||
<a id="sub-badge" href="javascript:void(0)" onclick="scrollToSubCard()" title=""
|
||||
style="display:none;align-items:center;gap:5px;background:rgba(52,199,89,.16);color:#1f9d3f;font-size:12.5px;font-weight:700;padding:4px 10px;border-radius:20px;text-decoration:none;white-space:nowrap;">
|
||||
<span style="font-size:13px;line-height:1">★</span><span id="sub-badge-text"></span>
|
||||
</a>
|
||||
<button id="tg-logout-btn" onclick="doLogout()" data-i18n="logout_btn">Выйти</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -910,6 +914,27 @@
|
||||
|
||||
<main>
|
||||
|
||||
<!-- Статус активной подписки -->
|
||||
<div class="card" id="sub-card" style="display:none;border:1px solid rgba(52,199,89,.4);background:linear-gradient(180deg,rgba(52,199,89,.09),rgba(52,199,89,.02));">
|
||||
<div style="padding:16px;display:flex;align-items:center;gap:14px;">
|
||||
<div style="width:46px;height:46px;flex:none;border-radius:50%;background:rgba(52,199,89,.18);display:flex;align-items:center;justify-content:center;font-size:23px;color:#1f9d3f;">★</div>
|
||||
<div style="flex:1;min-width:0">
|
||||
<div style="font-size:16px;font-weight:700;color:var(--text-primary,#1c2b45)" data-i18n="sub_active_title">Подписка активна</div>
|
||||
<div id="sub-card-until" style="font-size:13.5px;color:var(--text-secondary)"></div>
|
||||
</div>
|
||||
<button id="sub-renew-btn" type="button" onclick="renewSub()" style="flex:none;background:#34c759;color:#fff;border:none;border-radius:10px;padding:9px 16px;font-size:14px;font-weight:600;cursor:pointer;font-family:inherit" data-i18n="sub_renew">Продлить</button>
|
||||
</div>
|
||||
<div style="padding:0 16px 14px;display:flex;flex-wrap:wrap;gap:8px 18px;font-size:13px;color:var(--text-secondary)">
|
||||
<span>✓ <span data-i18n="sub_perk_files">Большие файлы без лимита</span></span>
|
||||
<span>✓ <span data-i18n="sub_perk_batch">Пакетная обработка</span></span>
|
||||
<span>✓ <span data-i18n="sub_perk_priority">Приоритетная очередь</span></span>
|
||||
</div>
|
||||
<div id="sub-autorenew" style="padding:10px 16px;border-top:0.5px solid rgba(52,199,89,.25);font-size:12.5px;color:var(--text-secondary);display:flex;align-items:center;gap:8px;flex-wrap:wrap">
|
||||
<span id="sub-autorenew-label"></span>
|
||||
<a id="sub-autorenew-toggle" href="javascript:void(0)" onclick="toggleAutorenew()" style="color:#007AFF;text-decoration:underline;cursor:pointer;display:none"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<p data-i18n="page_description" style="font-size:13px; line-height:1.45; color:var(--text-secondary); padding: 0 4px;">
|
||||
🧷 Выберите тип сшивки и при необходимости дополнительные настройки, затем загрузите файл — получите готовый скан.
|
||||
@@ -1065,6 +1090,10 @@
|
||||
<button id="paywall-file-btn" onclick="payForFile()" style="flex:1;min-width:150px;padding:12px;background:var(--blue,#007AFF);color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;font-family:inherit"><span data-i18n="paywall_pay_file">Оплатить файл</span> · <span id="paywall-file-price">99</span> ₽</button>
|
||||
<button id="paywall-sub-btn" onclick="payForSub()" style="flex:1;min-width:150px;padding:12px;background:#10b981;color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:600;cursor:pointer;font-family:inherit"><span data-i18n="paywall_pay_sub">Подписка</span> · <span id="paywall-sub-price">299</span> ₽/<span data-i18n="paywall_month">мес</span></button>
|
||||
</div>
|
||||
<label id="paywall-autorenew-row" style="display:flex;align-items:center;justify-content:center;gap:7px;margin-top:12px;font-size:12.5px;color:var(--text-secondary);cursor:pointer">
|
||||
<input type="checkbox" id="paywall-autorenew" checked style="width:16px;height:16px;cursor:pointer">
|
||||
<span data-i18n="paywall_autorenew">Продлевать автоматически каждый месяц</span>
|
||||
</label>
|
||||
<div id="paywall-msg" style="font-size:12px;color:var(--text-secondary);margin-top:10px"></div>
|
||||
<button onclick="resetApp()" style="margin-top:8px;background:none;border:none;color:var(--text-secondary);font-size:13px;cursor:pointer;text-decoration:underline;font-family:inherit" data-i18n="paywall_cancel">Отмена</button>
|
||||
</div>
|
||||
@@ -1194,7 +1223,7 @@
|
||||
ru: {
|
||||
page_title: 'pdf2scan — онлайн сканирование PDF',
|
||||
logo_subtitle: 'Создание растровых pdf с эффектом сканирования из электронных pdf документов',
|
||||
login_btn: 'Войти',
|
||||
login_btn: 'Войти / Регистрация',
|
||||
login_via_tg: 'Через Telegram',
|
||||
login_via_yandex: 'Через Яндекс',
|
||||
login_via_vk: 'Через VK',
|
||||
@@ -1309,6 +1338,19 @@
|
||||
paywall_pay_file: 'Оплатить файл',
|
||||
paywall_pay_sub: 'Подписка',
|
||||
paywall_month: 'мес',
|
||||
paywall_renew_title: 'Продлить подписку',
|
||||
paywall_renew_desc: 'Продлите доступ к большим файлам и пакетной обработке.',
|
||||
sub_active_title: 'Подписка активна',
|
||||
sub_until_label: 'до',
|
||||
sub_renew: 'Продлить',
|
||||
sub_perk_files: 'Большие файлы без лимита',
|
||||
sub_perk_batch: 'Пакетная обработка',
|
||||
sub_perk_priority: 'Приоритетная очередь',
|
||||
paywall_autorenew: 'Продлевать автоматически каждый месяц',
|
||||
sub_autorenew_on: 'Автопродление включено',
|
||||
sub_autorenew_off: 'Автопродление выключено',
|
||||
sub_autorenew_disable: 'Отключить',
|
||||
sub_autorenew_enable: 'Включить',
|
||||
paywall_cancel: 'Отмена',
|
||||
paywall_need_login: 'Для подписки войдите в аккаунт',
|
||||
seo_title: 'Как работает pdf2scan',
|
||||
@@ -1323,7 +1365,7 @@
|
||||
en: {
|
||||
page_title: 'pdf2scan — online PDF scan',
|
||||
logo_subtitle: 'Convert PDF to scanned document online',
|
||||
login_btn: 'Login',
|
||||
login_btn: 'Log in / Sign up',
|
||||
login_via_tg: 'Via Telegram',
|
||||
login_via_yandex: 'Via Yandex',
|
||||
login_via_vk: 'Via VK',
|
||||
@@ -1439,6 +1481,19 @@
|
||||
paywall_pay_file: 'Pay for file',
|
||||
paywall_pay_sub: 'Subscription',
|
||||
paywall_month: 'mo',
|
||||
paywall_renew_title: 'Renew subscription',
|
||||
paywall_renew_desc: 'Extend access to large files and batch processing.',
|
||||
sub_active_title: 'Subscription active',
|
||||
sub_until_label: 'until',
|
||||
sub_renew: 'Renew',
|
||||
sub_perk_files: 'Large files, no limit',
|
||||
sub_perk_batch: 'Batch processing',
|
||||
sub_perk_priority: 'Priority queue',
|
||||
paywall_autorenew: 'Renew automatically every month',
|
||||
sub_autorenew_on: 'Auto-renewal on',
|
||||
sub_autorenew_off: 'Auto-renewal off',
|
||||
sub_autorenew_disable: 'Turn off',
|
||||
sub_autorenew_enable: 'Turn on',
|
||||
paywall_cancel: 'Cancel',
|
||||
paywall_need_login: 'Log in to subscribe',
|
||||
seo_title: 'How pdf2scan works',
|
||||
@@ -1453,7 +1508,7 @@
|
||||
de: {
|
||||
page_title: 'pdf2scan — Online-PDF-Scan',
|
||||
logo_subtitle: 'PDF online in gescannte Dokumente umwandeln',
|
||||
login_btn: 'Anmelden',
|
||||
login_btn: 'Anmelden / Registrieren',
|
||||
login_via_tg: 'Über Telegram',
|
||||
login_via_yandex: 'Über Yandex',
|
||||
login_via_vk: 'Über VK',
|
||||
@@ -1569,6 +1624,19 @@
|
||||
paywall_pay_file: 'Datei bezahlen',
|
||||
paywall_pay_sub: 'Abo',
|
||||
paywall_month: 'Mon',
|
||||
paywall_renew_title: 'Abo verlängern',
|
||||
paywall_renew_desc: 'Verlängern Sie den Zugriff auf große Dateien und Stapelverarbeitung.',
|
||||
sub_active_title: 'Abo aktiv',
|
||||
sub_until_label: 'bis',
|
||||
sub_renew: 'Verlängern',
|
||||
sub_perk_files: 'Große Dateien ohne Limit',
|
||||
sub_perk_batch: 'Stapelverarbeitung',
|
||||
sub_perk_priority: 'Prioritäts-Warteschlange',
|
||||
paywall_autorenew: 'Automatisch monatlich verlängern',
|
||||
sub_autorenew_on: 'Automatische Verlängerung an',
|
||||
sub_autorenew_off: 'Automatische Verlängerung aus',
|
||||
sub_autorenew_disable: 'Ausschalten',
|
||||
sub_autorenew_enable: 'Einschalten',
|
||||
paywall_cancel: 'Abbrechen',
|
||||
paywall_need_login: 'Zum Abonnieren anmelden',
|
||||
seo_title: 'So funktioniert pdf2scan',
|
||||
@@ -1583,7 +1651,7 @@
|
||||
fr: {
|
||||
page_title: 'pdf2scan — scan PDF en ligne',
|
||||
logo_subtitle: 'Convertir des PDF en documents scannés en ligne',
|
||||
login_btn: 'Connexion',
|
||||
login_btn: 'Connexion / Inscription',
|
||||
login_via_tg: 'Via Telegram',
|
||||
login_via_yandex: 'Via Yandex',
|
||||
login_via_vk: 'Via VK',
|
||||
@@ -1699,6 +1767,19 @@
|
||||
paywall_pay_file: 'Payer le fichier',
|
||||
paywall_pay_sub: 'Abonnement',
|
||||
paywall_month: 'mois',
|
||||
paywall_renew_title: 'Renouveler votre abonnement',
|
||||
paywall_renew_desc: 'Prolongez votre accès aux gros fichiers et au traitement par lots.',
|
||||
sub_active_title: 'Abonnement actif',
|
||||
sub_until_label: 'expire le',
|
||||
sub_renew: 'Renouveler',
|
||||
sub_perk_files: 'Gros fichiers sans limite',
|
||||
sub_perk_batch: 'Traitement par lots',
|
||||
sub_perk_priority: 'File prioritaire',
|
||||
paywall_autorenew: 'Renouveler automatiquement chaque mois',
|
||||
sub_autorenew_on: 'Renouvellement automatique activé',
|
||||
sub_autorenew_off: 'Renouvellement automatique désactivé',
|
||||
sub_autorenew_disable: 'Désactiver',
|
||||
sub_autorenew_enable: 'Activer',
|
||||
paywall_cancel: 'Annuler',
|
||||
paywall_need_login: 'Connectez-vous pour vous abonner',
|
||||
seo_title: 'Comment fonctionne pdf2scan',
|
||||
@@ -1713,7 +1794,7 @@
|
||||
it: {
|
||||
page_title: 'pdf2scan — scansione PDF online',
|
||||
logo_subtitle: 'Converti PDF in documenti scansionati online',
|
||||
login_btn: 'Accedi',
|
||||
login_btn: 'Accedi / Registrati',
|
||||
login_via_tg: 'Via Telegram',
|
||||
login_via_yandex: 'Via Yandex',
|
||||
login_via_vk: 'Via VK',
|
||||
@@ -1829,6 +1910,19 @@
|
||||
paywall_pay_file: 'Paga file',
|
||||
paywall_pay_sub: 'Abbonamento',
|
||||
paywall_month: 'mese',
|
||||
paywall_renew_title: 'Rinnova abbonamento',
|
||||
paywall_renew_desc: 'Estendi accesso ai file grandi e alla elaborazione multipla.',
|
||||
sub_active_title: 'Abbonamento attivo',
|
||||
sub_until_label: 'fino al',
|
||||
sub_renew: 'Rinnova',
|
||||
sub_perk_files: 'File grandi senza limiti',
|
||||
sub_perk_batch: 'Elaborazione multipla',
|
||||
sub_perk_priority: 'Coda prioritaria',
|
||||
paywall_autorenew: 'Rinnova automaticamente ogni mese',
|
||||
sub_autorenew_on: 'Rinnovo automatico attivo',
|
||||
sub_autorenew_off: 'Rinnovo automatico disattivato',
|
||||
sub_autorenew_disable: 'Disattiva',
|
||||
sub_autorenew_enable: 'Attiva',
|
||||
paywall_cancel: 'Annulla',
|
||||
paywall_need_login: 'Accedi per abbonarti',
|
||||
seo_title: 'Come funziona pdf2scan',
|
||||
@@ -1843,7 +1937,7 @@
|
||||
es: {
|
||||
page_title: 'pdf2scan — escaneo PDF en línea',
|
||||
logo_subtitle: 'Convierte PDF en documentos escaneados en línea',
|
||||
login_btn: 'Iniciar sesión',
|
||||
login_btn: 'Entrar / Registro',
|
||||
login_via_tg: 'Con Telegram',
|
||||
login_via_yandex: 'Con Yandex',
|
||||
login_via_vk: 'Con VK',
|
||||
@@ -1959,6 +2053,19 @@
|
||||
paywall_pay_file: 'Pagar archivo',
|
||||
paywall_pay_sub: 'Suscripción',
|
||||
paywall_month: 'mes',
|
||||
paywall_renew_title: 'Renovar suscripción',
|
||||
paywall_renew_desc: 'Amplía el acceso a archivos grandes y al procesamiento por lotes.',
|
||||
sub_active_title: 'Suscripción activa',
|
||||
sub_until_label: 'hasta el',
|
||||
sub_renew: 'Renovar',
|
||||
sub_perk_files: 'Archivos grandes sin límite',
|
||||
sub_perk_batch: 'Procesamiento por lotes',
|
||||
sub_perk_priority: 'Cola prioritaria',
|
||||
paywall_autorenew: 'Renovar automáticamente cada mes',
|
||||
sub_autorenew_on: 'Renovación automática activada',
|
||||
sub_autorenew_off: 'Renovación automática desactivada',
|
||||
sub_autorenew_disable: 'Desactivar',
|
||||
sub_autorenew_enable: 'Activar',
|
||||
paywall_cancel: 'Cancelar',
|
||||
paywall_need_login: 'Inicia sesión para suscribirte',
|
||||
seo_title: 'Cómo funciona pdf2scan',
|
||||
@@ -1973,7 +2080,7 @@
|
||||
pt: {
|
||||
page_title: 'pdf2scan — digitalização PDF online',
|
||||
logo_subtitle: 'Converta PDF em documentos digitalizados online',
|
||||
login_btn: 'Entrar',
|
||||
login_btn: 'Entrar / Registrar',
|
||||
login_via_tg: 'Via Telegram',
|
||||
login_via_yandex: 'Via Yandex',
|
||||
login_via_vk: 'Via VK',
|
||||
@@ -2089,6 +2196,19 @@
|
||||
paywall_pay_file: 'Pagar arquivo',
|
||||
paywall_pay_sub: 'Assinatura',
|
||||
paywall_month: 'mês',
|
||||
paywall_renew_title: 'Renovar assinatura',
|
||||
paywall_renew_desc: 'Amplie o acesso a arquivos grandes e ao processamento em lote.',
|
||||
sub_active_title: 'Assinatura ativa',
|
||||
sub_until_label: 'até',
|
||||
sub_renew: 'Renovar',
|
||||
sub_perk_files: 'Arquivos grandes sem limite',
|
||||
sub_perk_batch: 'Processamento em lote',
|
||||
sub_perk_priority: 'Fila prioritária',
|
||||
paywall_autorenew: 'Renovar automaticamente todo mês',
|
||||
sub_autorenew_on: 'Renovação automática ativada',
|
||||
sub_autorenew_off: 'Renovação automática desativada',
|
||||
sub_autorenew_disable: 'Desativar',
|
||||
sub_autorenew_enable: 'Ativar',
|
||||
paywall_cancel: 'Cancelar',
|
||||
paywall_need_login: 'Entre para assinar',
|
||||
seo_title: 'Como funciona o pdf2scan',
|
||||
@@ -2157,6 +2277,8 @@
|
||||
});
|
||||
// Re-render dynamic button state
|
||||
updateSubmitBtn();
|
||||
// Re-render subscription badge/card in the new language
|
||||
if (subUntilTs) renderSubStatus({ active: subUntilTs > (Date.now() / 1000), until: subUntilTs });
|
||||
}
|
||||
|
||||
function setLang(lang) {
|
||||
@@ -2241,14 +2363,16 @@
|
||||
paywallJob = jobId;
|
||||
paywallFilePrice = filePrice || 99;
|
||||
paywallSubPrice = subPrice || 299;
|
||||
const isBatch = paywallMode === 'batch';
|
||||
const subOnly = (paywallMode === 'batch' || paywallMode === 'renew');
|
||||
document.getElementById('paywall-file-price').textContent = paywallFilePrice;
|
||||
document.getElementById('paywall-sub-price').textContent = paywallSubPrice;
|
||||
document.getElementById('paywall-file-btn').style.display = isBatch ? 'none' : '';
|
||||
document.getElementById('paywall-file-btn').style.display = subOnly ? 'none' : '';
|
||||
const titleEl = document.querySelector('#paywall-card [data-i18n="paywall_title"]');
|
||||
const descEl = document.querySelector('#paywall-card [data-i18n="paywall_desc"]');
|
||||
if (titleEl) titleEl.textContent = t(isBatch ? 'paywall_batch_title' : 'paywall_title');
|
||||
if (descEl) descEl.textContent = t(isBatch ? 'paywall_batch_desc' : 'paywall_desc');
|
||||
const tKey = paywallMode === 'renew' ? 'paywall_renew_title' : (paywallMode === 'batch' ? 'paywall_batch_title' : 'paywall_title');
|
||||
const dKey = paywallMode === 'renew' ? 'paywall_renew_desc' : (paywallMode === 'batch' ? 'paywall_batch_desc' : 'paywall_desc');
|
||||
if (titleEl) titleEl.textContent = t(tKey);
|
||||
if (descEl) descEl.textContent = t(dKey);
|
||||
const emailEl = document.getElementById('paywall-email');
|
||||
if (sessionUser && sessionUser.email && !emailEl.value) emailEl.value = sessionUser.email;
|
||||
document.getElementById('paywall-file-btn').disabled = false;
|
||||
@@ -2269,7 +2393,7 @@
|
||||
const fb = document.getElementById('paywall-file-btn'), sb = document.getElementById('paywall-sub-btn');
|
||||
fb.disabled = true; sb.disabled = true;
|
||||
// Если файл ещё не загружен (пейволл показан ДО загрузки) — грузим сейчас
|
||||
if (!paywallJob && paywallMode !== 'batch' && selectedFile) {
|
||||
if (!paywallJob && paywallMode === 'file' && selectedFile) {
|
||||
msg.textContent = t('pay_uploading');
|
||||
try {
|
||||
const data = await uploadFileChunked(selectedFile, collectSettings(), null);
|
||||
@@ -2277,6 +2401,7 @@
|
||||
} catch (e) { msg.textContent = (e && e.message) ? e.message : t('err_network'); fb.disabled = false; sb.disabled = false; return; }
|
||||
}
|
||||
const body = { purpose, email };
|
||||
if (purpose === 'sub') { const ar = document.getElementById('paywall-autorenew'); body.autorenew = ar ? ar.checked : false; }
|
||||
if (paywallJob) body.job_id = paywallJob;
|
||||
if (tgUser) body.user_id = tgUser.user_id || tgUser.id || 0;
|
||||
msg.textContent = t('pay_creating');
|
||||
@@ -2454,6 +2579,85 @@
|
||||
loginBtn.style.display = 'flex';
|
||||
userInfo.style.display = 'none';
|
||||
}
|
||||
loadSubscription(); // обновляем бейдж/карточку подписки под текущий вход
|
||||
}
|
||||
|
||||
// ── Статус подписки (бейдж в шапке + карточка на главной) ─────────────────
|
||||
let subUntilTs = 0, subAutorenew = false, subHasMethod = false;
|
||||
const _DATE_LOC = { ru: 'ru-RU', en: 'en-US', de: 'de-DE', fr: 'fr-FR', it: 'it-IT', es: 'es-ES', pt: 'pt-PT' };
|
||||
function _subDate(ts, opts) {
|
||||
try { return new Date(ts * 1000).toLocaleDateString(_DATE_LOC[currentLang] || undefined, opts); }
|
||||
catch (_) { return new Date(ts * 1000).toLocaleDateString(); }
|
||||
}
|
||||
async function loadSubscription() {
|
||||
const badge = document.getElementById('sub-badge');
|
||||
const card = document.getElementById('sub-card');
|
||||
if (!tgUser && !sessionUser) { renderSubStatus({ active: false }); return; }
|
||||
try {
|
||||
const uid = tgUser ? (tgUser.user_id || tgUser.id || 0) : 0;
|
||||
const r = await fetch('/api/subscription' + (uid ? ('?user_id=' + encodeURIComponent(uid)) : ''));
|
||||
renderSubStatus(await r.json());
|
||||
} catch (_) { renderSubStatus({ active: false }); }
|
||||
}
|
||||
function renderSubStatus(d) {
|
||||
const badge = document.getElementById('sub-badge');
|
||||
const card = document.getElementById('sub-card');
|
||||
subUntilTs = (d && d.active) ? (d.until || 0) : 0;
|
||||
if (d && d.price) paywallSubPrice = d.price;
|
||||
if (d && d.active && d.until) {
|
||||
const shortD = _subDate(d.until, { day: '2-digit', month: '2-digit', year: 'numeric' });
|
||||
const longD = _subDate(d.until, { day: '2-digit', month: 'long', year: 'numeric' });
|
||||
if (badge) {
|
||||
document.getElementById('sub-badge-text').textContent = t('sub_until_label') + ' ' + shortD;
|
||||
badge.title = t('sub_active_title') + ' — ' + t('sub_until_label') + ' ' + longD;
|
||||
badge.style.display = 'inline-flex';
|
||||
}
|
||||
if (card) {
|
||||
document.getElementById('sub-card-until').textContent = t('sub_until_label') + ' ' + longD;
|
||||
card.style.display = 'block';
|
||||
}
|
||||
// автопродление
|
||||
subAutorenew = !!(d && d.autorenew);
|
||||
subHasMethod = !!(d && d.has_method);
|
||||
const arLabel = document.getElementById('sub-autorenew-label');
|
||||
const arToggle = document.getElementById('sub-autorenew-toggle');
|
||||
if (arLabel && arToggle) {
|
||||
if (subAutorenew) {
|
||||
arLabel.textContent = t('sub_autorenew_on');
|
||||
arToggle.textContent = t('sub_autorenew_disable');
|
||||
arToggle.style.display = 'inline';
|
||||
} else {
|
||||
arLabel.textContent = t('sub_autorenew_off');
|
||||
if (subHasMethod) { arToggle.textContent = t('sub_autorenew_enable'); arToggle.style.display = 'inline'; }
|
||||
else { arToggle.style.display = 'none'; }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (badge) badge.style.display = 'none';
|
||||
if (card) card.style.display = 'none';
|
||||
}
|
||||
}
|
||||
async function toggleAutorenew() {
|
||||
if (!tgUser && !sessionUser) return;
|
||||
const body = { enable: !subAutorenew };
|
||||
if (tgUser) body.user_id = tgUser.user_id || tgUser.id || 0;
|
||||
const arToggle = document.getElementById('sub-autorenew-toggle');
|
||||
if (arToggle) arToggle.style.pointerEvents = 'none';
|
||||
try {
|
||||
const r = await fetch('/api/subscription/autorenew', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
|
||||
const d = await r.json();
|
||||
subAutorenew = !!d.autorenew;
|
||||
} catch (e) {}
|
||||
if (arToggle) arToggle.style.pointerEvents = '';
|
||||
loadSubscription();
|
||||
}
|
||||
function scrollToSubCard() {
|
||||
const card = document.getElementById('sub-card');
|
||||
if (card && card.style.display !== 'none') card.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
}
|
||||
function renewSub() {
|
||||
if (!tgUser && !sessionUser) return;
|
||||
showPaywall(null, paywallFilePrice, paywallSubPrice, 'renew');
|
||||
}
|
||||
|
||||
// ── Меню входа (Telegram / Яндекс) ────────────────────────────────────────
|
||||
@@ -2577,6 +2781,13 @@
|
||||
renderHeaderAuth();
|
||||
if (tgUser) loadHistory();
|
||||
if (!tgUser) loadMe(); // проверяем cookie-сессию (вход через Яндекс/VK)
|
||||
// Маячок посещения (раз в сессию браузера) — считаем реальные заходы для админки
|
||||
try {
|
||||
if (!sessionStorage.getItem('p2s_hit')) {
|
||||
sessionStorage.setItem('p2s_hit', '1');
|
||||
fetch('/api/hit', { method: 'POST', keepalive: true }).catch(function () {});
|
||||
}
|
||||
} catch (_) {}
|
||||
// Возврат после OAuth: чистим ?login и показываем ошибку при неудаче
|
||||
(function () {
|
||||
const p = new URLSearchParams(location.search);
|
||||
|
||||
Reference in New Issue
Block a user