358 lines
14 KiB
HTML
358 lines
14 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Telegram Scraper Control Panel</title>
|
|
<link rel="stylesheet" href="/static/style.css?v=8" />
|
|
</head>
|
|
<body>
|
|
<div class="app-shell">
|
|
<aside class="sidebar">
|
|
<div class="brand-block">
|
|
<svg class="brand-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="m21 3-7.6 18-3.8-7.6L2 9.6zM9.6 13.4 14 10" />
|
|
</svg>
|
|
<div class="eyebrow">Telegram Scraper</div>
|
|
<h1>Control</h1>
|
|
<p class="muted">Local scraper console for channels, jobs, and continuous runs.</p>
|
|
</div>
|
|
|
|
<nav class="nav-links">
|
|
<a class="nav-link active" href="/"
|
|
><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<rect x="4" y="4" width="6" height="6" />
|
|
<rect x="14" y="4" width="6" height="6" />
|
|
<rect x="4" y="14" width="6" height="6" />
|
|
<rect x="14" y="14" width="6" height="6" /></svg
|
|
>Dashboard</a
|
|
>
|
|
<a class="nav-link" href="/settings"
|
|
><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<circle cx="12" cy="12" r="3" />
|
|
<path
|
|
d="M12 2v3m0 14v3M2 12h3m14 0h3m-2.9-7.1-2.1 2.1M4.9 19.1 7 17m0-10-2.1-2.1m12.2 14.2-2.1-2.1" /></svg
|
|
>Settings</a
|
|
>
|
|
<a class="nav-link" href="/viewer"
|
|
><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v12H8l-4 3z" /></svg>Message
|
|
Viewer</a
|
|
>
|
|
<a class="nav-link" href="/swagger"
|
|
><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true">
|
|
<path d="m8 5-5 7 5 7m8-14 5 7-5 7M14 3l-4 18" /></svg
|
|
>API Docs</a
|
|
>
|
|
<a class="nav-link" href="/health"
|
|
><svg class="nav-icon" viewBox="0 0 24 24" aria-hidden="true"><path d="M3 12h4l2-6 4 12 2-6h6" /></svg
|
|
>Health</a
|
|
>
|
|
</nav>
|
|
|
|
<section class="status-card" id="active-account-card">
|
|
<div class="section-title">Active Account</div>
|
|
<div id="active-account-name" class="status-badge">Loading...</div>
|
|
<p id="active-account-status" class="muted small"></p>
|
|
</section>
|
|
|
|
<section class="status-card">
|
|
<div class="section-title">Actions</div>
|
|
<button class="button primary" id="scrape-all-btn">Scrape all channels</button>
|
|
<button class="button" id="export-all-btn">Export all data</button>
|
|
<button class="button" id="refresh-dialogs-btn">Refresh dialogs</button>
|
|
<a class="button" id="open-settings-btn" href="/settings">Settings</a>
|
|
</section>
|
|
</aside>
|
|
|
|
<main class="content">
|
|
<header class="dashboard-topbar">
|
|
<div class="dashboard-title"><span class="menu-icon" aria-hidden="true">☰</span>Dashboard</div>
|
|
<div class="runtime-status">
|
|
<span class="runtime-dot"></span>Service: Running <i></i> Scraper: Idle <i></i
|
|
><span class="runtime-clock"></span>
|
|
</div>
|
|
</header>
|
|
<!-- Account Tabs -->
|
|
<div id="account-tabs" class="account-tabs"></div>
|
|
|
|
<!-- Account Dashboard Panels -->
|
|
<div id="account-panels">
|
|
<!-- Each account's dashboard is rendered here dynamically -->
|
|
</div>
|
|
|
|
<!-- Fallback for no accounts -->
|
|
<section id="no-accounts-msg" class="panel hidden">
|
|
<div class="panel-header">
|
|
<div>
|
|
<h2>No Accounts Configured</h2>
|
|
<p class="muted">
|
|
Click "Add Account" in Settings to get started, or use the legacy single-account mode.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
</div>
|
|
|
|
<!-- ════════ Settings Dialog ════════ -->
|
|
<dialog id="settings-dialog" class="settings-dialog">
|
|
<div class="dialog-shell">
|
|
<div class="dialog-header">
|
|
<div>
|
|
<div class="eyebrow">Settings</div>
|
|
<h2>Global & Accounts</h2>
|
|
</div>
|
|
<button class="button button-small" id="close-settings-btn" type="button">Close</button>
|
|
</div>
|
|
|
|
<!-- ── Accounts Management ── -->
|
|
<section class="settings-section">
|
|
<div class="section-title">Accounts</div>
|
|
<div id="accounts-list" class="accounts-list"></div>
|
|
|
|
<form id="add-account-form" class="stack-form add-account-form">
|
|
<input id="add-account-id" name="account_id" placeholder="Account ID (e.g. work)" required />
|
|
<input id="add-account-label" name="label" placeholder="Display name (e.g. Work Account)" />
|
|
<input id="add-account-api-id" name="api_id" placeholder="API ID" />
|
|
<input id="add-account-api-hash" name="api_hash" placeholder="API Hash" />
|
|
<button class="button primary" type="submit">Add Account</button>
|
|
</form>
|
|
<label class="import-account-row">
|
|
<span class="muted small">Import account settings JSON</span>
|
|
<input id="import-account-file" type="file" accept="application/json,.json" />
|
|
</label>
|
|
</section>
|
|
|
|
<!-- ── Account Auth ── (shown per account selected in UI) -->
|
|
<section class="settings-section" id="account-auth-section">
|
|
<div class="section-title">Account Auth: <span id="auth-account-label">-</span></div>
|
|
<form id="credentials-form" class="stack-form">
|
|
<input id="api-id-input" name="api_id" placeholder="API ID" />
|
|
<input id="api-hash-input" name="api_hash" placeholder="API Hash" />
|
|
<button class="button" type="submit">Save credentials</button>
|
|
</form>
|
|
|
|
<div class="auth-actions">
|
|
<button class="button primary" id="start-qr-btn" type="button">Start QR login</button>
|
|
</div>
|
|
|
|
<div id="qr-wrap" class="qr-wrap hidden">
|
|
<img id="qr-image" alt="Telegram QR login" />
|
|
<p class="muted small">Open Telegram -> Settings -> Devices -> Scan QR.</p>
|
|
</div>
|
|
|
|
<form id="phone-form" class="stack-form">
|
|
<input id="phone-input" name="phone" placeholder="+1234567890" />
|
|
<button class="button" type="submit">Send code</button>
|
|
</form>
|
|
|
|
<form id="code-form" class="stack-form hidden">
|
|
<input id="code-input" name="code" placeholder="Telegram code" />
|
|
<button class="button" type="submit">Confirm code</button>
|
|
</form>
|
|
|
|
<form id="password-form" class="stack-form hidden">
|
|
<input id="password-input" name="password" type="password" placeholder="2FA password" />
|
|
<button class="button" type="submit">Confirm password</button>
|
|
</form>
|
|
</section>
|
|
|
|
<!-- ── Scraping ── -->
|
|
<section class="settings-section">
|
|
<div class="section-title">Scraping</div>
|
|
<label class="toggle-row">
|
|
<span>Download media</span>
|
|
<span class="switch">
|
|
<input id="scrape-media-toggle" type="checkbox" />
|
|
<span class="switch-slider"></span>
|
|
</span>
|
|
</label>
|
|
</section>
|
|
</div>
|
|
</dialog>
|
|
|
|
<!-- ════════ Templates ════════ -->
|
|
|
|
<!-- Account Tab -->
|
|
<template id="account-tab-template">
|
|
<button class="account-tab" type="button">
|
|
<span class="account-tab-name"></span>
|
|
<span class="account-tab-status"></span>
|
|
</button>
|
|
</template>
|
|
|
|
<!-- Account Dashboard Panel -->
|
|
<template id="account-panel-template">
|
|
<section class="account-panel">
|
|
<!-- Dashboard tabs: Overview, Continuous -->
|
|
<div class="content-tabs">
|
|
<button class="content-tab active" data-tab-target="overview" type="button">Overview</button>
|
|
<button class="content-tab" data-tab-target="continuous" type="button">Continuous</button>
|
|
</div>
|
|
|
|
<!-- Overview Tab -->
|
|
<div class="tab-panel active" data-panel="overview">
|
|
<section class="panel-grid">
|
|
<div class="panel stat-panel">
|
|
<div class="section-title">Channels</div>
|
|
<div class="stat-value channel-count">0</div>
|
|
</div>
|
|
<div class="panel stat-panel">
|
|
<div class="section-title">Media scraping</div>
|
|
<div class="stat-value stat-compact scrape-media-label">OFF</div>
|
|
</div>
|
|
<div class="panel stat-panel">
|
|
<div class="section-title">Forwarding rules</div>
|
|
<div class="stat-value forwarding-count">0</div>
|
|
</div>
|
|
<div class="panel stat-panel">
|
|
<div class="section-title">Account health</div>
|
|
<div class="stat-value stat-compact account-health-label">-</div>
|
|
<div class="muted small account-health-detail"></div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel">
|
|
<div class="panel-header">
|
|
<div>
|
|
<h2>Tracked Channels</h2>
|
|
<p class="muted">Per-account channel list.</p>
|
|
</div>
|
|
<form class="inline-form add-channel-form">
|
|
<input class="add-channel-id" name="channel_id" placeholder="ID or @username" required />
|
|
<input class="add-channel-name" name="name" placeholder="Display name" />
|
|
<button class="button primary" type="submit">Add</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Channel</th>
|
|
<th>Messages</th>
|
|
<th>Media</th>
|
|
<th>Last message</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="channels-table"></tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel jobs-panel">
|
|
<div class="panel-header">
|
|
<div>
|
|
<h2>Jobs</h2>
|
|
<p class="muted">Queued work runs one job at a time.</p>
|
|
</div>
|
|
<button class="button refresh-jobs-btn">Refresh</button>
|
|
</div>
|
|
<div class="jobs-list"></div>
|
|
</section>
|
|
</div>
|
|
|
|
<!-- Continuous Tab -->
|
|
<div class="tab-panel" data-panel="continuous">
|
|
<section class="panel">
|
|
<div class="panel-header">
|
|
<div>
|
|
<h2>Continuous Scraping</h2>
|
|
<p class="muted">Per-account continuous scrape settings.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<form class="continuous-form">
|
|
<label class="toggle-row">
|
|
<span>Enabled</span>
|
|
<span class="switch">
|
|
<input type="checkbox" class="continuous-enabled" checked />
|
|
<span class="switch-slider"></span>
|
|
</span>
|
|
</label>
|
|
<label class="stack-form">
|
|
<span class="muted small">Interval, minutes</span>
|
|
<input type="number" class="continuous-interval" min="1" value="1" />
|
|
</label>
|
|
<label class="toggle-row">
|
|
<span>All tracked channels</span>
|
|
<span class="switch">
|
|
<input type="checkbox" class="continuous-all" checked />
|
|
<span class="switch-slider"></span>
|
|
</span>
|
|
</label>
|
|
<div class="channel-picker">
|
|
<div class="muted small">Continuous channel set</div>
|
|
<div class="checkbox-grid continuous-channel-list"></div>
|
|
</div>
|
|
<button class="button primary" type="submit">Save continuous settings</button>
|
|
</form>
|
|
|
|
<div class="continuous-meta">
|
|
<div>Status: <span class="continuous-status">-</span></div>
|
|
<div>Last run: <span class="continuous-last-iteration">-</span></div>
|
|
<div>Next run: <span class="continuous-next-run">-</span></div>
|
|
<div>Last error: <span class="continuous-last-error">-</span></div>
|
|
</div>
|
|
|
|
<div class="log-viewer continuous-logs"></div>
|
|
</section>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<!-- Channel Row -->
|
|
<template id="channel-row-template">
|
|
<tr>
|
|
<td>
|
|
<div class="channel-name"></div>
|
|
<div class="muted small channel-id"></div>
|
|
</td>
|
|
<td class="message-count"></td>
|
|
<td class="media-count"></td>
|
|
<td class="last-date"></td>
|
|
<td>
|
|
<div class="action-row">
|
|
<button class="button button-small scrape-btn">Scrape</button>
|
|
<button class="button button-small export-btn">Export</button>
|
|
<button class="button button-small export-view-btn">Viewer</button>
|
|
<button class="button button-small media-btn">Rescrape media</button>
|
|
<button class="button button-small danger remove-btn">Remove</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<!-- Job Template -->
|
|
<template id="job-template">
|
|
<article class="job-card">
|
|
<div class="job-head">
|
|
<div class="job-title"></div>
|
|
<div class="job-status"></div>
|
|
</div>
|
|
<div class="muted small job-time"></div>
|
|
<pre class="job-logs"></pre>
|
|
</article>
|
|
</template>
|
|
|
|
<!-- Account list item in settings -->
|
|
<template id="account-list-item-template">
|
|
<div class="account-list-item">
|
|
<div class="account-list-info">
|
|
<span class="account-list-label"></span>
|
|
<span class="muted small account-list-id"></span>
|
|
<span class="account-list-auth-status"></span>
|
|
</div>
|
|
<div class="account-list-actions">
|
|
<button class="button button-small account-select-btn">Select</button>
|
|
<button class="button button-small account-export-btn">Export</button>
|
|
<button class="button button-small danger account-remove-btn">Remove</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script src="/static/app.js?v=4"></script>
|
|
</body>
|
|
</html>
|