🔒 Repository is read-only – file editing is disabled.

PaganLinux/pagan-web-v2/templates/admin/iso.html main

225 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
{% extends "admin/layout.html" %}
{% block admin_title %}Obrazy ISO{% endblock %}
{% block admin_h1 %}Obrazy ISO{% endblock %}
{% block admin_sub %}Upload obrazów – po wgraniu otrzymasz link https://paganlinux.eu/download/iso/<wersja>{% endblock %}
{% block admin_head %}
<style>
.iso-prog{margin-top:0.9rem;border:1px solid var(--border);border-radius:var(--radius-sm);padding:0.7rem 0.9rem;background:var(--bg2)}
.iso-prog-head{display:flex;justify-content:space-between;align-items:center;gap:1rem;font-size:0.8rem;font-weight:600;margin-bottom:0.45rem}
.iso-prog-track{height:8px;border-radius:99px;background:var(--bg);border:1px solid var(--border);overflow:hidden}
.iso-prog-fill{height:100%;width:0;border-radius:99px;background:linear-gradient(90deg,#2ea043,#3fb950);transition:width 0.25s ease}
.iso-prog-sub{font-size:0.72rem;color:var(--muted);margin-top:0.45rem;word-break:break-word}
.iso-prog.err .iso-prog-fill{background:linear-gradient(90deg,#da3633,#f85149)}
.iso-prog.done .iso-prog-fill{background:linear-gradient(90deg,#238636,#3fb950)}
.iso-clamp{max-width:230px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:0.78rem;color:var(--muted)}
.iso-edit-row td{background:var(--bg2)}
.iso-edit-box{display:grid;grid-template-columns:1fr 1fr;gap:0.75rem;align-items:start}
.iso-edit-box textarea{width:100%;min-height:74px;font-size:0.8rem}
.iso-edit-actions{display:flex;gap:0.4rem;align-items:center;margin-top:0.5rem}
@media(max-width:700px){.iso-edit-box{grid-template-columns:1fr}}
</style>
{% endblock %}
{% block admin_content %}
<div class="card" style="margin-bottom:1rem">
 <div class="card-header"><h2><svg class="i16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></svg> {{ t('iso_upload_title') }}</h2></div>
 <form id="iso-upload-form" method="POST" action="/admin/iso/upload" enctype="multipart/form-data">
  <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:0.75rem">
   <div>
    <label style="font-size:0.75rem">{{ t('iso_version') }} (np. 0.0.1-kde)</label>
    <input name="version" id="iso-version" required placeholder="0.0.1-kde" style="width:100%">
   </div>
   <div>
    <label style="font-size:0.75rem">{{ t('iso_file') }} (.iso)</label>
    <input type="file" name="file" id="iso-file" accept=".iso" required style="width:100%">
   </div>
  </div>
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:0.75rem;margin-top:0.75rem">
   <div>
    <label style="font-size:0.75rem">Opis – krótka informacja o wersji (widoczna na /download)</label>
    <textarea name="description" id="iso-description" rows="2" placeholder="np. KDE Plasma – pierwsze wydanie z pełnym pulpitem KDE."></textarea>
   </div>
   <div>
    <label style="font-size:0.75rem">Zmiany / changelog – każda linia to jeden wpis</label>
    <textarea name="changes" id="iso-changes" rows="2" placeholder="np.:&#10;- dodano: instalator z podziałem dysku&#10;- poprawiono: dźwięk po restarcie"></textarea>
   </div>
  </div>
  <div style="display:flex;align-items:center;gap:0.75rem;margin-top:0.85rem;flex-wrap:wrap">
   <button type="submit" id="iso-upload-btn" class="btn btn-sm" style="background:#2ea043;color:#fff;font-size:0.85rem;padding:0.5rem 1.2rem">
    <svg class="i16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></svg> <span id="iso-upload-btn-lbl">{{ t('iso_upload_btn') }}</span>
   </button>
   <span id="iso-file-info" style="font-size:0.75rem;color:var(--muted)"></span>
  </div>
  <div id="iso-progress" class="iso-prog" style="display:none">
   <div class="iso-prog-head"><span id="iso-prog-label">Wysyłanie pliku…</span><b id="iso-prog-pct">0%</b></div>
   <div class="iso-prog-track"><div class="iso-prog-fill" id="iso-prog-fill"></div></div>
   <div class="iso-prog-sub" id="iso-prog-sub"></div>
  </div>
  <p style="font-size:0.78rem;color:var(--muted);margin:0.7rem 0 0">
   Duży plik (1–2 GB) – upload może potrwać kilka minut. Nie zamykaj karty podczas przesyłania.
   Plik trafia do <code>{{ iso_dir }}</code>. Wgranie wersji, która już istnieje, <strong>nadpisze</strong> poprzedni plik
   (opis i zmiany zostaną zachowane, jeśli pozostawisz pola puste).
  </p>
 </form>
</div>

<div class="card">
 <div class="card-header"><h2>{{ t('iso_list') }} ({{ isos|length }})</h2></div>
 {% if isos %}
 <div style="overflow-x:auto">
 <table style="width:100%">
 <thead><tr><th>{{ t('iso_version') }}</th><th>Opis</th><th>Rozmiar</th><th>SHA-256</th><th>Link</th><th>{{ t('admin_actions') }}</th></tr></thead>
 <tbody>
 {% for i in isos %}
 <tr>
  <td><strong>{{ i.version }}</strong>
   {% if i.date %}<div style="font-size:0.68rem;color:var(--muted)">{{ i.date }}</div>{% endif %}
  </td>
  <td>
   {% if i.description %}<div class="iso-clamp" title="{{ i.description }}{% if i.changes %}&#10;&#10;ZMIANY:&#10;{{ i.changes }}{% endif %}">{{ i.description }}</div>
   {% else %}<span style="color:var(--muted);font-size:0.75rem"></span>{% endif %}
  </td>
  <td style="font-size:0.8rem;color:var(--muted)">{{ (i.size / 1048576)|round(0)|int }} MB</td>
  <td style="font-size:0.65rem;color:var(--muted)"><code>{{ i.sha256[:16] }}…</code></td>
  <td style="font-size:0.75rem;word-break:break-all">
   <a href="{{ i.link }}" target="_blank">{{ i.link }}</a>
   <div style="font-size:0.65rem;margin-top:0.15rem"><a href="{{ i.link }}.sha256">suma (.sha256)</a></div>
  </td>
  <td>
   <div style="display:flex;gap:0.3rem;flex-wrap:wrap">
    <button type="button" class="btn btn-sm" data-edit-toggle="edit-row-{{ i.id }}" style="font-size:0.7rem;padding:0.15rem 0.4rem" title="Edytuj opis / zmiany"><svg class="i16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M17 3a2.83 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/></svg></button>
    <form method="POST" action="/admin/iso/{{ i.id }}/delete" style="margin:0" onsubmit="return confirm('Usunąć ISO {{ i.version }}? Plik zniknie z serwera.')">
     <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
     <button class="btn btn-sm" style="font-size:0.7rem;padding:0.15rem 0.4rem;background:#da3633;color:#fff" title="Usuń ISO"><svg class="i16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M10 11v6M14 11v6"/></svg></button>
    </form>
   </div>
  </td>
 </tr>
 <tr class="iso-edit-row" id="edit-row-{{ i.id }}" style="display:none">
  <td colspan="6">
   <form method="POST" action="/admin/iso/{{ i.id }}/update">
    <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
    <div class="iso-edit-box">
     <div>
      <label style="font-size:0.75rem">Opis</label>
      <textarea name="description" placeholder="Krótka informacja o wersji">{{ i.description }}</textarea>
     </div>
     <div>
      <label style="font-size:0.75rem">Zmiany / changelog (linia = wpis)</label>
      <textarea name="changes" placeholder="Co nowego w tej wersji?">{{ i.changes }}</textarea>
     </div>
    </div>
    <div class="iso-edit-actions">
     <button class="btn btn-sm" style="background:#2ea043;color:#fff">Zapisz opis / zmiany</button>
     <button type="button" class="btn btn-sm" data-edit-toggle="edit-row-{{ i.id }}">Zamknij</button>
    </div>
   </form>
  </td>
 </tr>
 {% endfor %}
 </tbody>
 </table>
 </div>
 {% else %}
 <p style="text-align:center;color:var(--muted);padding:2rem">{{ t('iso_empty') }}</p>
 {% endif %}
</div>
<script>
(function () {
 var form = document.getElementById('iso-upload-form');
 var fileIn = document.getElementById('iso-file');
 var info = document.getElementById('iso-file-info');
 var btn = document.getElementById('iso-upload-btn');
 var btnLbl = document.getElementById('iso-upload-btn-lbl');
 var prog = document.getElementById('iso-progress');
 var progFill = document.getElementById('iso-prog-fill');
 var progPct = document.getElementById('iso-prog-pct');
 var progLbl = document.getElementById('iso-prog-label');
 var progSub = document.getElementById('iso-prog-sub');

 function fmt(n) { return (n / 1048576).toFixed(1) + ' MB'; }
 if (fileIn) fileIn.addEventListener('change', function () {
  var f = fileIn.files && fileIn.files[0];
  info.textContent = f ? (f.name + ' · ' + fmt(f.size)) : '';
 });

 document.querySelectorAll('[data-edit-toggle]').forEach(function (b) {
  b.addEventListener('click', function () {
   var row = document.getElementById(b.getAttribute('data-edit-toggle'));
   if (row) row.style.display = (row.style.display === 'none') ? '' : 'none';
  });
 });

 if (!form) return;
 form.addEventListener('submit', function (e) {
  var f = fileIn.files && fileIn.files[0];
  var version = (document.getElementById('iso-version').value || '').trim();
  if (!f) { alert('Wybierz plik ISO.'); e.preventDefault(); return; }
  if (!version) { alert('Podaj wersję.'); e.preventDefault(); return; }

  // Upload przez XHR z paskiem postępu
  e.preventDefault();
  var fd = new FormData(form);
  var xhr = new XMLHttpRequest();
  prog.style.display = '';
  prog.classList.remove('err', 'done');
  progFill.style.width = '0%';
  progPct.textContent = '0%';
  progLbl.textContent = 'Wysyłanie pliku…';
  progSub.textContent = '';
  btn.disabled = true;
  btnLbl.textContent = 'Przesyłanie…';

  xhr.upload.addEventListener('progress', function (ev) {
   if (!ev.lengthComputable) return;
   var pct = Math.round(ev.loaded / ev.total * 100);
   progFill.style.width = pct + '%';
   progPct.textContent = pct + '%';
   progLbl.textContent = 'Wysyłanie pliku…';
   progSub.textContent = fmt(ev.loaded) + ' / ' + fmt(ev.total);
  });
  xhr.upload.addEventListener('load', function () {
   progLbl.textContent = 'Zapis na serwerze i obliczanie sumy SHA-256…';
   progFill.style.width = '100%';
   progSub.textContent = 'Duży plik – to może potrwać chwilę.';
  });
  xhr.addEventListener('load', function () {
   btn.disabled = false;
   btnLbl.textContent = 'Wgraj ISO';
   var msg = '', ok = false, redir = '/admin/iso';
   try {
    var j = JSON.parse(xhr.responseText);
    ok = !!j.ok; msg = j.message || ''; redir = j.redirect || redir;
   } catch (e) { ok = xhr.status >= 200 && xhr.status < 300; }
   if (ok) {
    prog.classList.add('done');
    progLbl.textContent = msg || 'Gotowe – przeładowuję listę…';
    progSub.textContent = '';
    setTimeout(function () { window.location.href = redir; }, 900);
   } else {
    prog.classList.add('err');
    progFill.style.width = '100%';
    progLbl.textContent = msg || 'Błąd – spróbuj ponownie.';
    progSub.textContent = '';
   }
  });
  xhr.addEventListener('error', function () {
   prog.classList.add('err');
   progLbl.textContent = 'Błąd połączenia – spróbuj ponownie.';
   progSub.textContent = '';
   btn.disabled = false;
   btnLbl.textContent = 'Wgraj ISO';
  });
  xhr.addEventListener('abort', function () {
   prog.classList.add('err');
   progLbl.textContent = 'Przerwano.';
   btn.disabled = false;
   btnLbl.textContent = 'Wgraj ISO';
  });
  xhr.open('POST', form.getAttribute('action'));
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  xhr.send(fd);
 });
})();
</script>
{% endblock %}