Files

98 lines
4.2 KiB
HTML

{% extends "base.html" %}
{% block title %}Краулеры{% endblock %}
{% block content %}
<h4 class="mb-3">🤖 Краулеры</h4>
<div class="row mb-3">
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h6 class="card-title text-muted">Очередь (pending)</h6>
<h3 class="mb-0">{{ pending }}</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h6 class="card-title text-muted">В обработке (processing)</h6>
<h3 class="mb-0">{{ processing }}</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h6 class="card-title text-muted">Обработано сегодня</h6>
<h3 class="mb-0">{{ done_today }}</h3>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-center">
<div class="card-body">
<h6 class="card-title text-muted">Источники</h6>
<h3 class="mb-0">{{ alive }}<small class="text-muted">/{{ total_sources}}</small>
{% if dead %} <span class="badge bg-danger">{{ dead }} dead</span>{% endif %}</h3>
</div>
</div>
</div>
</div>
<div class="d-flex gap-2 mb-3">
<form method="post" action="/crawlers/run" class="d-inline">
<label class="me-1"><small>воркеры</small>
<input type="number" name="workers" value="8" min="1" max="32" class="form-control form-control-sm d-inline-block" style="width:70px">
</label>
<label class="me-2"><small>лимит</small>
<input type="number" name="limit" value="200" min="1" max="1000" class="form-control form-control-sm d-inline-block" style="width:80px">
</label>
<button class="btn btn-success btn-sm">▶ Запустить воркер</button>
</form>
<form method="post" action="/crawlers/reset" class="d-inline">
<button class="btn btn-warning btn-sm">↻ Сбросить dead</button>
</form>
</div>
{% if request.query_params.get('run') == 'started' %}
<div class="alert alert-success py-2">Воркер запущен ({{ request.query_params.get('workers') }} потоков, лимит {{ request.query_params.get('limit') }}). Следите за processing → done.</div>
{% elif request.query_params.get('run') == 'error' %}
<div class="alert alert-danger py-2">Ошибка запуска: {{ request.query_params.get('msg') }}</div>
{% elif request.query_params.get('reset') %}
<div class="alert alert-info py-2">Сброшено {{ request.query_params.get('reset') }} source(s) в alive.</div>
{% endif %}
<h5 class="mt-4">Источники</h5>
<table class="table table-sm table-striped">
<thead><tr><th>Источник</th><th>Тип</th><th>Статус</th><th>Последний сбор</th><th>Ошибки</th><th>Приоритет</th></tr></thead>
<tbody>
{% for s in sources %}
<tr>
<td><b>{{ s.name }}</b><br><small class="text-muted">{{ s.slug }}</small></td>
<td><span class="badge bg-secondary">{{ s.crawler }}</span></td>
<td>
{% if s.status == 'dead' %}<span class="badge bg-danger">dead</span>
{% elif s.enabled %}<span class="badge bg-success">alive</span>
{% else %}<span class="badge bg-secondary">off</span>{% endif %}
</td>
<td>{{ s.last_fetch | dt if s.last_fetch else '—' }}</td>
<td>{% if s.error_count %}<span class="text-danger">{{ s.error_count }}</span>{% else %}—{% endif %}
{% if s.last_error %}<br><small class="text-muted">{{ s.last_error }}</small>{% endif %}</td>
<td>{{ s.priority }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5 class="mt-4">Последние запуски</h5>
<table class="table table-sm table-striped">
<thead><tr><th>Время</th><th>Источник</th><th>Статус</th><th>Постов</th><th>Длительность</th></tr></thead>
<tbody>
{% for r in runs %}
<tr><td>{{ r.started_at | dt }}</td><td>{{ r.source_name or r.source_id }}</td>
<td>{{ r.status }}</td><td>{{ r.posts_new }}/{{ r.posts_fetched }}</td>
<td>{% if r.duration_ms %}{{ "%.1f"|format(r.duration_ms/1000) }}s{% else %}—{% endif %}</td></tr>
{% endfor %}
</tbody>
</table>
{% endblock %}