Konkurs fotograficzny — dokumentacja aplikacji

wersja 1.3.1
← Wróć do aplikacji

Architektura

Całość działa w chmurze Cloudflare, bez własnego serwera. Cztery warstwy: urządzenie → frontend → backend → dane (+ usługi zewnętrzne). Kliknij dowolny kafelek, by zobaczyć opis i ścieżkę pliku.

1 · Urządzenie / przeglądarka (PWA)

↓ ładuje

2 · Frontend — public/ (vanilla JS, moduły ES)

↓ fetch / SSE (HTTPS)

3 · Backend — Pages Functions functions/api/ (+ lib/)

↓ czyta / zapisuje

4 · Dane (Cloudflare)

5 · Usługi zewnętrzne · Wydanie

wskazówka

Kliknij kafelek diagramu

Po lewej cztery warstwy: urządzenie → frontend → backend → dane. Tu pojawi się opis i ścieżka pliku.

  1. Start: index.html → app.js → GET /api/me (cookie JWT).
  2. Brak sesji: e‑mail → kod PIN (Resend) → verify-pin ustawia JWT (7 dni).
  3. Bramka zgody na regulamin → accept-regulamin (KV).
  4. Zakładki: Moje zdjęcia · Moje głosy · Wyniki · (Ustawienia – admin).
  5. Faza (KV) decyduje, co wolno: zgłaszanie → głosowanie → wyniki.

1. Cel i skala

20uczestników (max) + admin
4kategorie (definiuje owner)
3zdjęcia / kategorię
9 MBna zdjęcie (4–12)
~240zdjęć (teoretyczny szczyt)
Zakres i zasady
Domena

Nowa domena zarejestrowana w Cloudflare: czwarta-b.com. Aplikacja działa na subdomenie foto-konkurs.czwarta-b.com. Domena czwarta-b.com jest stroną www klasy IVb, a zarazem hubem grupującym dostęp do usług i aplikacji.

2. Role, uprawnienia i etapy

Administrator

Admin = jeden z uczestników (też wgrywa i głosuje, też nie na własne). Dodatkowo:

Uwaga — fair play

Admin też startuje, więc NIE widzi cudzych ocen w trakcie — tylko postęp (kto już zagłosował, liczby). Pełne rankingi pokazują się wszystkim dopiero po odsłonięciu.

Role i uprawnienia

Wszystkie trzy role wgrywają zdjęcia i głosują; różnią się dostępem do funkcji administracyjnych:

Rolawidzi ustawieniadodaje kategoriedane testowe
Właściciel (owner)taktaktak
Administrator (admin)taknienie
Uczestnik (user)nienienie

Trzy role — od najszerszych uprawnień do najwęższych:

Każda rola ma niezależny przełącznik zgłaszania zdjęć (w panelu „zdjęcia +/−"; w bazie odwrotne pole voter_only). Domyślnie jest włączony — osoba zgłasza własne zdjęcia i głosuje. Po wyłączeniu osoba tylko głosuje, nie zgłasza zdjęć; przydaje się dla zaproszonych gości albo szerszego grona oceniającego.

W skrócie: rola mówi, co ktoś może robić w konkursie, a przełącznik zgłaszania zdjęć — czy sam staje do rywalizacji ze swoimi zdjęciami. Jedno z drugim nie jest powiązane, więc można je łączyć dowolnie: nawet administratorowi (czy właścicielowi) można wyłączyć zgłaszanie zdjęć — wtedy prowadzi konkurs, ale sam w nim nie startuje, jedynie ocenia prace innych. W ustawieniach odpowiadają temu dwa niezależne przełączniki: jeden dla roli, drugi dla zgłaszania zdjęć.

Etapy (uruchamia admin)
  1. Zgłoszenia (upload) — uczestnicy wgrywają zdjęcia; dostępne „Zapytaj Claude".
  2. Głosowanie (voting) — admin zamyka zgłoszenia, otwiera układanie TOP3; można modyfikować oceny.
  3. Wyniki (results) — admin kończy ocenę; progresywne odsłanianie wyników + wyróżnienia komisji.

Progresywne odsłanianie wyników — szczegóły w sekcji 14.

3. Stack — w całości Cloudflare

Stack
ElementRola
Pagesfrontend statyczny pod foto-konkurs.czwarta-b.com
Pages Functionsbackend serverless w /functions — bez osobnego Workera
D1dane (uczestnicy, zdjęcia, głosy, recenzje AI) — env.DB
R2pliki zdjęć: oryginały + lekkie kopie — env.BUCKET
KVfaza, terminy, parametry, akceptacje regulaminu, dzienny limit maili, tryb testowy, ulubione komisji, liczniki dławienia
Resendmaile: PIN + potwierdzenia/przypomnienia + zaproszenie
Anthropic APIfunkcja „Zapytaj Claude" (Opus 4.8) — patrz sekcja 9
Uwaga — e-mail (Resend)

Domena czwarta-b.com weryfikowana w Resend → wysyłka z dowolnego adresu (no-reply@ dla automatów, admin@/konkurs@ dla korespondencji), bez zakładania skrzynek. Dostawca maili — Resend (wdrożone). Moduł email.js jest provider-agnostyczny (aplikacja woła tylko sendEmail), więc podmiana dostawcy = zmiana tego modułu + sekrety, bez zmian w logice. Strażnik dziennego limitu maili w KV. SES rozważany na przyszłość (własna domena), ale niezaimplementowany (placeholder zwraca provider_not_implemented:ses). Odbiór poczty (skrzynka admin@) — osobno, niższy priorytet (Cloudflare Email Routing → Gmail).

4. Logowanie — PIN na e-mail (OTP) + długa sesja

Logowanie
  1. E-mail → Functions sprawdza allowlist (users). Brak → odmowa.
  2. PIN losowany kryptograficznie (CSPRNG), przechowywany jako hash z solą w D1 (~10 min), PIN wychodzi mailem przez Resend.
  3. Wpisany PIN → weryfikacja (hash, niewygasły, limit prób, jednorazowość) → sesja (JWT w cookie HttpOnly, ~7 dni).
  4. Sesja 7 dni → PIN mniej więcej raz w tygodniu (skrócone, bo cichych wejść nie logujemy).

PIN ponownie tylko gdy: sesja wygaśnie, wylogowanie, czyszczenie ciasteczek, nowe urządzenie. WhatsApp jako kanał — odrzucony.

Ochrona przed nadużyciem (wdrożone): prośba o kod — maks. 10 kodów/h na e-mail i 30 żądań/h na IP; weryfikacja — 5 prób na kod + twardy licznik 20 nietrafień/h na e-mail (domyka brute-force). Liczniki w KV, przy awarii KV fail-open (nie blokuje logowania). Podgląd PIN na ekranie (tryb dev) tylko z localhost/LAN.

SSO — wspólne logowanie z portalem czwarta-b

Konkurs (foto-konkurs.czwarta-b.com) i portal czwarta-b.com dzielą jedną sesję. Zalogowanie w jednej aplikacji uznaje druga; wylogowanie kończy sesję po obu stronach.

Warstwa SSO: lib/session.js (wspólne podpisywanie/weryfikacja/kasowanie ciasteczka).

5. Model danych (D1 / SQLite) — szkic

Schemat
CREATE TABLE users (
  id INTEGER PRIMARY KEY, name TEXT NOT NULL,
  email TEXT NOT NULL UNIQUE,
  gender TEXT,                          -- poprawna odmiana / zwroty AI
  is_admin INTEGER NOT NULL DEFAULT 0,
  is_owner INTEGER NOT NULL DEFAULT 0,  -- chroniony superadmin
  active   INTEGER NOT NULL DEFAULT 1,  -- dezaktywacja zamiast usuwania
  voter_only INTEGER NOT NULL DEFAULT 0,-- „tylko głosuje": nie wgrywa zdjęć
  ai_sessions_used INTEGER NOT NULL DEFAULT 0,  -- licznik sesji AI
  invited_at TEXT);                     -- znacznik wysłania zaproszenia

CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT NOT NULL, description TEXT);  -- definiuje owner (obecnie 4)

CREATE TABLE photos (
  id INTEGER PRIMARY KEY,
  owner_id    INTEGER NOT NULL REFERENCES users(id),
  category_id INTEGER NOT NULL REFERENCES categories(id),
  r2_key_original TEXT NOT NULL,        -- oryginał (nietknięty)
  r2_key_display  TEXT,                 -- lekka kopia do siatki / AI (~1500 px)
  title TEXT, filename TEXT, bytes INTEGER,
  taken_at TEXT,                        -- data wykonania z EXIF (jeśli jest)
  created_at TEXT DEFAULT (datetime('now')));

CREATE TABLE votes (
  voter_id    INTEGER NOT NULL REFERENCES users(id),
  category_id INTEGER NOT NULL REFERENCES categories(id),
  photo_id    INTEGER NOT NULL REFERENCES photos(id),
  rank   INTEGER NOT NULL,              -- 1..3
  points INTEGER NOT NULL,              -- 5-3-1
  PRIMARY KEY (voter_id, category_id, rank),
  UNIQUE (voter_id, category_id, photo_id));

CREATE TABLE ai_reviews (               -- cache „Zapytaj Claude" — per zdjęcie
  photo_id INTEGER PRIMARY KEY REFERENCES photos(id),
  review_text TEXT NOT NULL,
  titles_json TEXT,                     -- 3 tytuły + wskazany najlepszy
  followups_json TEXT,                  -- pytanie z 5 chipów + odpowiedź
  created_at TEXT DEFAULT (datetime('now')));

Pozostałe tabele (pełny schemat w schema.sql): favorites (ulubione per głosujący), ai_usage (tokeny Anthropic), jury_awards (3 wyróżnienia: pos, photo_id, degree, reason), login_codes (kody PIN — OTP), events (log zdarzeń, retencja 60 dni).

PIN-y w D1 (login_codes, mocna spójność); sesja jako JWT w cookie. Faza/terminy/parametry/akceptacje/limit maili — w KV.

6. Endpointy (Pages Functions)

Endpointy
EndpointDziałanie
POST /api/auth/request-pin{email} → allowlist → PIN (CSPRNG), hash do D1, Resend; dławienie per IP/e-mail
POST /api/auth/verify-pin{email, pin} → cookie sesji (długa); limit prób + twardy licznik nietrafień
POST /api/auth/logoutczyści ciasteczko sesji we wszystkich wariantach (host-only + Domain=.czwarta-b.com + stare session) — wspólne wylogowanie (SSO)
GET /api/mebieżący użytkownik + faza + terminy + czy wymagana akceptacja regulaminu; przy sesji SSO spoza konkursu → 401 { ssoEmail } (front pokazuje komunikat)
POST /api/accept-regulaminzapis akceptacji aktualnej wersji regulaminu
GET /api/photosmoje zdjęcia + lista kategorii (z opisami) + limity/liczniki
POST /api/photos/uploadoryginał + lekka kopia + miniatura, limit 3/kategorię, faza upload; blokada dla „tylko głosy"
DELETE /api/photos/:idusunięcie zdjęcia (właściciel; admin — moderacja) — faza upload
PATCH /api/photos/:idzmiana tytułu / kategorii własnego zdjęcia — faza upload
GET /api/img/:idstrumień zdjęcia z R2 (wymaga sesji); ?t=1 — miniatura (awaryjnie lekka kopia)
GET /api/download/:idpobranie oryginału na dysk — tylko właściciel
GET /api/voteprzegląd: faza + wszystkie kategorie (kandydaci, mój TOP, ulubione)
GET /api/vote/:categorykandydaci kategorii bez własnych, stała losowa kolejność + mój TOP/ulubione
POST /api/vote/:category{top:[…], favorites:[…]} → punkty 5-3-1 → zapis (nadpisuje, transakcyjnie), faza voting
GET /api/ai/:idrecenzja z cache (jeśli jest) + stan budżetu AI
POST /api/ai/:idrecenzja strumieniowana (SSE, Opus 4.8) albo 1 dodatkowe pytanie; faza upload, własne zdjęcie
GET /api/resultspodium per kategoria + zbiorczo + wyróżnienia — faza results
GET /api/my-awardsnagrody zalogowanego (dyplomy) — faza results
...admin/*uczestnicy, kategorie (owner), terminy, etapy, parametry, postęp, moderacja zdjęć, wyróżnienia, komunikacja, log, dane testowe

Fazy upload → voting → results sterowane flagą w KV.

7. Punktacja i wyniki

Punktacja
Uwaga — mała liczba głosujących

Przy ~20 osobach, dobrowolnym głosowaniu i braku głosów na własne, remisy będą częste, a wyniki czułe na pojedynczy głos. Suma punktów faworyzuje kategorie z większą frekwencją głosowania — świadoma decyzja przy konkursie towarzyskim.

8. Interfejs, głosowanie i obsługa zdjęć

„all devices first"
Mechanizm głosowania TOP3

W kategorii może być 20+ (nawet ~100) zdjęć → przeciąganie z dużej puli jest niepraktyczne. Dlatego:

Obsługa zdjęć (oryginał nietknięty)
HEIC / miniatury — rozstrzygnięte

Kopie (lekka + miniatura) robi przeglądarka (canvas), bez Cloudflare Images. Urządzenia Apple i tak zamieniają HEIC na JPEG przed wysłaniem, więc do aplikacji trafia JPEG — potwierdzone na iPhonie i Macu, w Safari i Chrome. Miniatury starszych zdjęć dorabiają się w tle po zalogowaniu (przeglądarka właściciela).

Pozostałe funkcje

9. Funkcja AI — „Zapytaj Claude"

AI

Cel: autor dostaje rzeczową, ludzką recenzję własnego zdjęcia + propozycje tytułów i może „poradzić się" AI przed decyzją o zgłoszeniu.

AI — cache i budżet
AI — prompt (verbatim, co do słowa)

Tekst promptu zapisany dokładnie jak ustalono — nie parafrazujemy go ani nie zastępujemy własnym opisem:

Jesteś recenzentem amatorskich fotografii grupy przyjaciół z tej samej klasy maturalnej. Bądź rzeczowy i miły, okaż empatię. Zwracaj się bezpośrednio. Nie stosuj sformułowań typowych dla AI, w tym przesadnego chwalenia autora zdjęcia.

Opisz załączone zdjęcie w około ${reviewWords} słowach — to długość samego opisu, dąż do niej; 3 tytuły i 5 pytań dodajesz osobno, poza tym limitem słów. Pisz pełnymi zdaniami, nie urywaj zdania w połowie. Oceń jego walory artystyczne, pokazane emocje i warsztat twórcy. Skup się na pozytywnym przekazie. Liczbę uwag krytycznych dopasuj do jakości zdjęcia: im zdjęcie lepsze, tym mniej rzeczy do poprawy — nie szukaj wad na siłę; przy bardzo dobrym zdjęciu zasugeruj wysoką ocenę i ogranicz uwagi krytyczne do minimum. Przekaż sugestie.

Zaproponuj 3 tytuły dla tego zdjęcia, wskaż najlepiej dopasowany.

Jeśli autor chce, odpowiesz na jedno dodatkowe pytanie. Zaproponuj pięć pytań, które AUTOR mógłby zadać TOBIE o to zdjęcie i na które potrafisz odpowiedzieć — sformułowane z perspektywy autora proszącego o radę lub wyjaśnienie (np. „Jak mógłbym…?", „Jakie ustawienia…?", „Jak obrobić…?"). NIE pytaj autora o jego intencje ani decyzje. Po jednym z obszarów, w kolejności: 1) przekaz i wymowa zdjęcia (jak je wzmocnić), 2) ujęcie (kompozycja, kadr, światło), 3) ustawienia aparatu, 4) obróbka w postprodukcji, 5) lekkie, z przymrużeniem oka.

Implementacja obok promptu (bez zmiany słów): płeć / kategoria / tytuł przekazywane w wiadomości jako dane (źródło prawdy); zdjęcie jako kopia ~1500 px; z 5 chipów autor wybiera jedno pytanie.

10. Skala vs darmowe limity Cloudflare

Przeliczone na ~240 zdjęć
ZasóbZużycie (max)Darmowy próg
R2 (magazyn)oryginały do 9 MB × 240 ≈ do ~2,1 GB; kopie — ułamek tego10 GB
D1 (dane)głosy ~setki + ulubione + recenzje5 GB, mln operacji/dzień
Functionskilka–kilkanaście tys. żądań100 tys./dzień
Resend (maile)kilkadziesiąt–setka~100/dzień, 3 000/mies.
Uwaga — koszty niezerowe

Infrastruktura CF mieści się w darmowych progach (R2 z dużym zapasem — zmniejszanie do kopii robimy dla szybkości galerii, nie dla limitu). Płatne: domena czwarta-b.com (~kilkadziesiąt zł/rok) i tokeny Claude (zmienny koszt, ograniczany budżetem sesji AI).

11. Plan pracy (etapy)

Etapy realizacji
  1. Fundament / scaffold — katalog + git (osobne, prywatne repo), Cloudflare Pages, wrangler.toml (D1/R2/KV), sekrety (Resend, Anthropic), schema.sql + seed, flaga fazy, domena.
  2. UX / prototyp — klikalna makieta HTML w PREVIEW (logowanie, upload, „Zapytaj Claude", głosowanie TOP3, wyniki, panel admina). Zatwierdzenie przed kodem.
  3. Autentykacja — OTP PIN (Resend), allowlist, długa sesja (JWT), anty-brute-force.
  4. Ładowanie zdjęć — upload R2 (oryginał + kopia), HEIC→wyświetlana, lightbox, limit 3/kategorię, „Moje zgłoszenia", postęp.
  5. „Zapytaj Claude" — Opus 4.8 streaming, budżet sesji + licznik, cache per zdjęcie, kopiuj/udostępnij.
  6. Ocena — galeria per kategoria (bez własnych, anonimowo, losowa), ulubione → TOP3 (tap + drag), modyfikacja głosów.
  7. Komunikacja — maile potwierdzające + przypomnienia (admin).
  8. Panel admina — uczestnicy, kategorie, postęp (bez cudzych ocen), usuwanie zdjęć, etapy, wyróżnienia komisji, odsłanianie.
  9. Wyniki — podium per kategoria + zbiorczo + wyróżnienia, progresywne odsłanianie, eksport/link.
  10. Testy + wdrożenie — smoke (logowanie, upload, AI, głos, wynik), deploy, seed realnych osób, próbny przebieg.

12. Struktura projektu i git

Repozytorium

Osobno od Rambulo — celowo. Rambulo = Vite + Express + SQLite na Railway. Konkurs = serverless w całości na Cloudflare, lekka technologia.

konkurs-foto/
  SPEC.md  PREVIEW/                     # dokumentacja + makiety
  public/                              # frontend (vanilla JS, lekko)
  functions/api/...                    # Pages Functions (login, photos, vote, ai, results, admin)
  schema.sql                           # tabele D1 + seed
  scripts/                             # stamp-version, tag-release
  _WYDANIE.COMMAND  _WDROZENIE.COMMAND # launchery wydania (dwuklik) + _STATUS / _SERWER
  wrangler.toml  .dev.vars  .gitignore  README.md

Wydanie i wdrożenie — dwa skrypty (model jak w Rambulo)

Zamiast osobnych skryptów COMMIT / RELEASE / DEPLOY / PUSHdwa launchery .command (dwuklik w Finderze):

Bramką „nie wdrażaj tej samej wersji dwa razy" jest tag git-WERSJA (tworzony w postdeploy) — przy powtórnym wdrożeniu tej samej wersji predeploy przerywa i odsyła do _WYDANIE. W odróżnieniu od Rambulo nie ma tu maszyny stanów .deploy-state, testów E2E, mirrora GitLab ani purge Cloudflare — konkurs wdraża bezpośrednio przez wrangler. Pomocniczo: _STATUS.COMMAND (podgląd stanu) i _SERWER.COMMAND (serwer lokalny).

13. Decyzje

Podjęte ✅

Cloudflare + Resend + Anthropic domena czwarta-b.com → foto-konkurs.* 20 + admin 4 kategorie (definiuje owner) 3 zdjęcia / kategorię · do 9 MB JPG/HEIC/WEBP/PNG TOP3 · 5-3-1 · pełny · dobrowolny remisy: więcej głosów / ex aequo brak głosów na własne PIN na e-mail (kryptograficzny) · długa sesja dławienie logowania per IP/e-mail + fail-open zapis głosów transakcyjny admin NIE widzi cudzych ocen komisja: wyróżnienia wpisuje admin usuwanie uczestnika → dezaktywacja all devices first (3/2/1) oryginał nietknięty + lekkie kopie głosowanie: tap z puli + drag w TOP3 + ulubione „Zapytaj Claude" (Opus 4.8, budżet sesji) anonimowość · maile · „Moje zgłoszenia" dezaktywacja uczestnika → zdjęcia nieaktywne (FK) budżet AI = kategorie×3 + bufor role: owner / admin / uczestnik · tryb: tylko głosuje „tylko głosuje" — bez zgłaszania kategorie i dane testowe tylko owner punktacja w jednym pliku scoring.js adres aplikacji w mailu zaproszenia wyniki: odsłanianie od najniższego miejsca maile: moduł wymienny + strażnik limitu owner/superadmin nadaje admina konkurs jako PWA

14. Ekrany, nawigacja i język wizualny

Język wizualny
Nawigacja
Ekran logowania
  1. Pole e-mail + „Wyślij kod".
  2. Pole PIN (6 cyfr) + „Zaloguj"; komunikat „Wysłaliśmy kod na <e-mail>".

Komunikaty ludzkie: e-mail spoza listy → „Nie znaleźliśmy tego adresu — sprawdź pisownię albo napisz do organizatora"; zły/wygasły PIN → „PIN niepoprawny lub wygasł (pozostały próby: N)". Ochrona (limit prób, hash, TTL, jednorazowość) zostaje.

Ekran zgód (jednorazowy)
Zakładka 1 „Moje zdjęcia"
Przycisk AI (3 stany)

Wyzwalacz: przycisk tekstowy (bez ikony) — ustalone (rezygnacja z gwiazdki Claude).

Zakładka 2 „Głosowanie"
Zakładka 3 „Wyniki"
Ekran wyników (progresywne odsłanianie)

U góry przyciski tylko dla admina: Wyróżnienia, Kategoria 1, …, ostatnia kategoria. Naciśnięcie → dana sekcja odsłania się ładnie, od najniższego miejsca podium w górę. „Wyróżnienia" pokazują uznaniowe wyróżnienia komisji.

Zakładka 4 „Ustawienia" (panel admina)
Ekran „O aplikacji" (modal-akordeon ze stopki)
Komunikaty błędów (zasada)

15. Struktura plików (stan 2026-07-03)

konkurs-foto/
  public/                         # frontend (serwowany)
    app.js
    dev-lan.json
    dokumentacja.html
    dyplom.html
    icon_180.png
    icon_192.png
    icon_512.png
    index.html
    manifest.webmanifest
    portal-4b.svg
    przewodnik.html
    regulamin.html
    style.css
    sw.js
    trophy_192.png
    trophy_512.png
    js/                           # moduły ES
      about.js
      accordion.js
      ai.js
      auth.js
      confirm.js
      consent.js
      dom-anim.js
      dom.js
      help.js
      infobar.js
      install-banner.js
      lightbox.js
      phone.js
      photo-image.js
      photos.js
      results.js
      session.js
      settings-comm.js
      settings-events.js
      settings-forms.js
      settings-handlers.js
      settings-help.js
      settings-jury.js
      settings-views.js
      settings.js
      tabs.js
      toast.js
      version.js
      voting.js
  functions/
    api/                          # Pages Functions (endpointy)
      accept-regulamin.js
      admin/categories.js
      admin/categories/[id].js
      admin/check-delivery.js
      admin/deadlines.js
      admin/events.js
      admin/jury-favorites.js
      admin/jury.js
      admin/notify.js
      admin/overview.js
      admin/params.js
      admin/phase.js
      admin/photos.js
      admin/test-mode.js
      admin/users.js
      admin/users/[id].js
      ai/[id].js
      auth/logout.js
      auth/request-pin.js
      auth/verify-pin.js
      download/[id].js
      health.js
      img/[id].js
      me.js
      my-awards.js
      photos.js
      photos/[id].js
      photos/upload.js
      results.js
      thumbs/[id].js
      thumbs/missing.js
      vote.js
      vote/[category].js
    lib/                          # wspólna logika
      ai.js
      auth.js
      email.js
      events.js
      jwt.js
      notify.js
      params.js
      phase.js
      ratelimit.js
      regulamin.js
      scoring.js
      session.js
      testmode.js
      version.js
  schema.sql  wrangler.toml  package.json  scripts/  dokumenty/

16. Rozmiary plików .js i .css (KB + linie, stan 2026-07-17)

PlikKBLinie
functions/api/
accept-regulamin.js0.510
admin/categories.js1.228
admin/categories/[id].js2.458
admin/check-delivery.js1.744
admin/deadlines.js1.839
admin/events.js1.842
admin/jury-favorites.js1.641
admin/jury.js3.999
admin/notify.js5.5136
admin/overview.js3.372
admin/params.js0.820
admin/phase.js0.922
admin/photos.js1.435
admin/test-mode.js1.842
admin/users.js1.748
admin/users/[id].js4.691
ai/[id].js7.9183
auth/logout.js0.48
auth/request-pin.js6.2147
auth/verify-pin.js4.6118
download/[id].js1.431
health.js0.37
img/[id].js1.941
me.js1.539
my-awards.js4.4122
photos.js1.640
photos/[id].js4.7109
photos/upload.js4.9114
results.js2.871
thumbs/[id].js1.429
thumbs/missing.js1.232
vote.js2.972
vote/[category].js5.2136
functions/lib/
ai.js6.9135
auth.js1.330
email.js5.4140
events.js4.372
jwt.js2.677
notify.js12.5264
params.js1.537
phase.js1.746
ratelimit.js2.146
regulamin.js0.719
scoring.js0.34
session.js2.245
testmode.js7.4153
version.js0.12
public/js/
about.js6.5137
accordion.js3.3114
ai.js9.4265
auth.js2.377
confirm.js3.8100
consent.js2.356
dom-anim.js1.024
dom.js0.726
help.js7.082
infobar.js4.0121
install-banner.js6.6137
lightbox.js1.444
phone.js2.266
photo-image.js3.387
photos.js18.4466
results.js10.5272
session.js0.517
settings-comm.js9.7204
settings-events.js9.4225
settings-forms.js7.3166
settings-handlers.js12.8324
settings-help.js8.350
settings-jury.js8.5202
settings-views.js16.0320
settings.js18.4347
tabs.js2.255
toast.js1.135
version.js0.12
voting.js21.3525
public/
app.js3.473
style.css51.3755
sw.js1.622

Razem: JS — 78 plików, 7837 linii, ~340 KB · CSS — 1 plik, 755 linii, ~51 KB.

wyróżnione = pliki ponad 400 linii (kandydaci do podziału).

← Wróć do aplikacji