c73ba6a964
Demo idzie szeroko i do różnych osób, często na cudzych komputerach — więc
wyjście z aplikacji jest tu potrzebne bardziej niż w pełnej wersji, a Basic go
nie miał: przeglądarka zapamiętuje hasło i dosyła je sama przy każdym żądaniu.
Pierwszy klient zostawiał otwartą sesję drugiemu.
Ta sama konstrukcja co w pełnej aplikacji: własny ekran logowania, podpisane
ciasteczko (HMAC-SHA256), HttpOnly + SameSite=Strict, wylogowanie POST-em, kres
bezczynności i twardy, sito na adres powrotu, zdarzenia w dzienniku bez haseł.
Moduł session.py skopiowany, tak samo jak link_crypto — usługi są osobnymi
obrazami i nie importują się nawzajem.
DWIE RÓŻNICE WOBEC PEŁNEJ WERSJI, obie wynikające z tego, że demo nie ma
własnego wolumenu:
* nazwa ciasteczka jest inna. Gdyby obie aplikacje stanęły kiedyś pod jedną
domeną, ciasteczka o tej samej nazwie nadpisywałyby się i człowiek wypadałby
z jednej, logując się do drugiej.
* nie ma licznika pokolenia sesji, bo nie ma go gdzie zapisać. Zdalne
unieważnienie robi się przez DEMO_USERS: usunięcie konta albo zmiana hasła
NATYCHMIAST ubija jego otwarte sesje, bo odcisk poświadczenia w ciasteczku
przestaje pasować. Osobny test tego pilnuje. Wylogowanie i tak działa
natychmiast, bo polega na skasowaniu ciasteczka.
Klucz podpisu jest WŁASNY, nie ten z pełnej aplikacji: demo i produkcja nie mają
powodu uznawać nawzajem swoich sesji, a wspólny klucz znaczyłby, że sesja z demo
bywa ważna tam, gdzie nie powinna.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
65 lines
2.9 KiB
CSS
65 lines
2.9 KiB
CSS
/* astroklient-demo — arkusz CELOWO minimalny. Nie kopiujemy stylów pełnej aplikacji:
|
|
demo ma dwa formularze i tabelę wyników, a każda przeniesiona klasa niosłaby
|
|
nazwy funkcji, których tu nie ma. */
|
|
:root {
|
|
--bg: #11121b; --panel: #191b26; --line: #2b2e3d;
|
|
--fg: #e8e9f0; --muted: #9aa0b4; --accent: #8ab4ff;
|
|
}
|
|
* { box-sizing: border-box; }
|
|
body {
|
|
margin: 0; background: var(--bg); color: var(--fg);
|
|
font: 15px/1.5 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
}
|
|
main { max-width: 880px; margin: 0 auto; padding: 1.5rem 1rem 3rem; }
|
|
.topbar { padding: .5rem 0 1rem; border-bottom: 1px solid var(--line);
|
|
display: flex; align-items: baseline; justify-content: space-between; gap: 1rem; }
|
|
.kto { display: flex; align-items: center; gap: .5rem; }
|
|
.whoami { color: var(--muted); font-size: .85rem; }
|
|
.wyloguj-form { display: inline; margin: 0; }
|
|
button.wyloguj {
|
|
background: none; border: 1px solid var(--line); color: var(--muted);
|
|
font: inherit; font-weight: 400; padding: .35rem .8rem; border-radius: 8px; cursor: pointer;
|
|
}
|
|
button.wyloguj:hover { color: var(--fg); }
|
|
|
|
/* Ekran logowania — wąski, bez niczego poza formularzem. */
|
|
.login-card label { display: block; margin-top: .75rem; }
|
|
.login-card input { width: 100%; }
|
|
h1 { margin: 0; font-size: 1.4rem; letter-spacing: .5px; }
|
|
h2 { margin: 0 0 .3rem; font-size: 1.05rem; }
|
|
h3 { margin: 0 0 .5rem; font-size: 1rem; }
|
|
|
|
.card {
|
|
margin-top: 1.25rem; padding: 1rem 1.25rem;
|
|
background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
|
|
}
|
|
.row { display: flex; gap: .75rem; flex-wrap: wrap; align-items: flex-end; margin-top: .6rem; }
|
|
label { display: flex; flex-direction: column; gap: .25rem; color: var(--muted); font-size: .85rem; flex: 1; min-width: 10rem; }
|
|
input {
|
|
width: 100%; padding: .45rem .6rem; background: #0e0f18; color: var(--fg);
|
|
border: 1px solid var(--line); border-radius: 8px; font: inherit;
|
|
}
|
|
button {
|
|
padding: .5rem 1.25rem; background: var(--accent); color: #10121a;
|
|
border: none; border-radius: 8px; font: inherit; font-weight: 600; cursor: pointer;
|
|
}
|
|
|
|
table { width: 100%; border-collapse: collapse; margin-top: .3rem; }
|
|
td { padding: .35rem .5rem; border-bottom: 1px solid var(--line); vertical-align: top; font-size: .92rem; }
|
|
td.key { color: var(--accent); white-space: nowrap; padding-right: 1rem; }
|
|
tr:last-child td { border-bottom: none; }
|
|
|
|
.sig { margin-top: .75rem; }
|
|
.sig-head { color: var(--muted); font-size: .85rem; }
|
|
.meta { color: var(--muted); margin: 1.5rem 0 .5rem; font-size: .9rem; }
|
|
.muted { color: var(--muted); }
|
|
.small { font-size: .85rem; }
|
|
.foot { margin-top: 2.5rem; color: var(--muted); font-size: .8rem; }
|
|
|
|
.alert, .ok {
|
|
margin: 1rem 0; padding: .6rem .9rem; border-radius: 8px; font-size: .92rem;
|
|
border: 1px solid var(--line); border-left-width: 4px;
|
|
}
|
|
.alert { border-left-color: #b8860b; background: rgba(184, 134, 11, .1); }
|
|
.ok { border-left-color: var(--accent); background: rgba(138, 180, 255, .08); }
|