Moderní webová aplikace pro organizaci školních EDUCHEM LAN party akcí. Řeší prezentační web, účty účastníků, administraci, profily, realtime rezervace míst, docházku a provozní statistiky.
- Popis projektu
- Aktuální vývojáři
- Funkcionality
- Rezervace
- Realtime a cache
- Bezpečnost, autentizace a šifrování
- Administrace, nastavení a docházka
- Technologie
- Screenshoty z aplikace
- Struktura projektu
- Instalace a spuštění dev verze
- Konfigurace prostředí
- Databáze a migrace
- Produkce a Docker
- Adresy aplikace
- API a SignalR
- Pravidla commitování
EDUCHEM LAN Party App je full-stack aplikace pro přípravu a správu LAN party událostí na škole EDUCHEM. Projekt spojuje veřejnou prezentační část pro účastníky s přihlášenou aplikací pro studenty, organizátory a administrátory.
Veřejný web ukazuje informace o aktuální akci, historii, pravidla, harmonogram, FAQ a vstup do rezervací. Přihlášená část řeší dashboard, správu účtu, profily, administraci účastníků, bezpečnostní logy, nastavení aplikace, docházku účastníků a samotné rezervace počítačů nebo místností. Backend poskytuje REST API, SignalR hub pro realtime rezervace, JWT přihlašování s access a refresh cookies, PostgreSQL databázi, Redis cache, aplikační cache a HTML emaily renderované přes Razor šablony.
|
Stanislav Škudrna Web · GitHub |
Serhii Yavorskyi Web · GitHub |
Aktuálně vyvíjejí a spravují aplikaci EDUCHEM LAN Party App.
- Prezentační web události: hlavní stránka, informace, historie, pravidla, harmonogram, FAQ a veřejný vstup do rezervací.
- Uživatelské účty: JWT přihlášení s databázovými refresh sessions, CSRF ochranou, správou přihlášených zařízení, změnou hesla, resetem hesla přes email a přihlašovacím linkem.
- Profily účastníků: vlastní profil, veřejné profily podle UUID, avatar, banner, třída, škola a role.
- Administrace účtů: vytváření, úprava, mazání, reset hesla, impersonace, odeslání přihlašovacích údajů a filtrování účtů.
- Bezpečnostní logy: databázové logování důležitých akcí, filtrování a administrátorský přehled v aplikaci.
- Nastavení aplikace: administrace globálních voleb, zapnutí chatu, otevření/uzavření rezervací, rezervační časovač a vyčištění aplikační cache.
- Docházka: check-in/check-out účastníků, důvod odchodu, přehled aktuálně přítomných a možnost zápisu za jiného účastníka pro organizátory.
- Role a oprávnění:
Student,Teacher,TeacherOrg,AdminaSuperAdmin. - Rezervace míst: realtime mapa počítačů a místností s možností rezervovat, změnit nebo zrušit vlastní rezervaci.
- Dashboard a statistiky: přehled účtů, aktivních uživatelů, povolených rezervací, staffu, tříd a kapacity.
- Emailové šablony: registrace, reset hesla a nové přihlašovací údaje přes Razor views.
- Containerizace: produkční Docker image s .NET backendem, Next.js standalone frontendem a Nginx reverse proxy.
Rezervační část je nyní plnohodnotná součást aplikace a běží na adrese /app/reservations.
- Interaktivní mapa: mapa je posuvná a zoomovatelná přes komponentu
MovableMap. - Více pater / zón: aktuálně jsou připravené záložky
IT Hub (Spodní patro)aSpirála (Horní patro). - Počítače i místnosti: uživatel může rezervovat konkrétní počítač nebo místnost s kapacitou pro vlastní setup.
- Jedna rezervace na účet: nová rezervace automaticky nahrazuje předchozí rezervaci stejného účtu.
- Zrušení rezervace: uživatel může vlastní rezervaci zrušit přes SignalR metodu
Unbook. - Oprávnění přes účet: rezervovat mohou jen účty s
EnableReservations = true. - Globální stav rezervací: administrace umí rezervace vynutit otevřené, zavřené nebo řídit podle časovače
UseTimer. - Rezervační odpočet: UI používá serverový čas a ukazuje začátek/konec rezervačního okna podle nastavení aplikace.
- Učitelská místa: počítače označené jako
IsTeachersComputerjsou dostupné pouze pro účty s rolí alespoňTeacher. - Kapacity místností: místnost lze obsadit jen do hodnoty
Room.Capacity. - Ochrana proti souběhu: zápis rezervace běží v serializable transakci a řeší kolize při rychlém souběžném kliknutí.
- Stavy v UI: mapa rozlišuje volné místo, obsazeno/nedostupné a vlastní rezervaci.
- Pravý panel: ukazuje statistiky, seznam rezervací, profily přihlášených účastníků a upozornění, když účet nemá rezervace povolené.
- Stav připojení: UI ukazuje připojeno, připojování, reconnect a ztrátu spojení.
- Počet online klientů: SignalR posílá do mapy aktuální počet připojených klientů.
Anonymní návštěvník vidí obsazenost bez detailních profilů. Přihlášený uživatel vidí u rezervací profily a může přejít na detail účastníka.
Rezervace jsou postavené na kombinaci SignalR, PostgreSQL a aplikační cache:
- SignalR hub:
/hubs/reservationsposílá počáteční snapshot rezervací a následně jen změny. - Oddělená data podle přihlášení: přihlášení klienti dostávají DTO s profily, anonymní klienti anonymizovaná DTO.
- Delta update: po rezervaci nebo zrušení se neposílá celý seznam znovu, ale jen
previousReservationa nováreservation. - Client-side delta merge: frontend změnu slepí do aktuálního seznamu lokálně přes
useReservationsHub. - Connection status throttling: počet připojených klientů se broadcastuje maximálně jednou za sekundu.
- Memory cache pro rezervace:
ReservationCacheServicedrží zvlášť cache pro přihlášené a anonymní snapshoty. - Sdílená aplikační cache:
AppCacheServicesjednocuje práci sIMemoryCachea umožňuje administrátorské kompletní vyčištění cache. - Cache pro mapová data: místnosti a počítače se načítají přes cache klíč
reservations:rooms-and-computers. - Krátká status cache: souhrnný status rezervací se cachuje na 30 sekund.
- Anti-stampede zámky: cache používá
SemaphoreSlim, aby se při prázdné cache nespustilo více stejných DB dotazů najednou. - Redis: slouží pro perzistenci Data Protection keyringu (sdílené napříč restarty a instancemi) a připravenou distribuovanou cache; všechny klíče a kanály jsou bezpečně izolovány prefixem
REDIS_KEY_PREFIX(výchozíedulp:). - Nginx cache headers: statické Next.js assety z
/_next/static/mají dlouhou immutable cache. - Build cache: Docker build používá cache mounty pro npm i NuGet balíčky.
Aplikace klade důraz na bezpečnost, ochranu proti zneužití a striktní oddělení kryptografických principů:
V architektuře je přesně rozlišen účel digitálního podpisu a šifrování:
- Digitální podepisování (JWT s
JWT_SECRET):- Krátkodobé Access tokeny (uložené v HttpOnly cookie
edulp_accessnebo v hlavičceAuthorization: Bearer) slouží k bezestavovému ověření identity a rolí uživatele. - Data v tokenu nejsou šifrovaná, ale jsou kryptograficky podepsaná algoritmem HMAC-SHA256 s tajemstvím
JWT_SECRET. Server tak okamžitě ověří integritu a autentičnost (uživatel nemůže token pozměnit ani si přidat roliAdmin), aniž by musel při každém HTTP požadavku sahat do databáze. - Čitelná klientská cookie
edulp_access_expires(v produkci__Host-edlp_access_expires) nese pouze čas expirace v unixových sekundách a slouží frontendu k naplánování automatické tiché obnovy tokenu.
- Krátkodobé Access tokeny (uložené v HttpOnly cookie
- Dlouhodobé sessions (Refresh Sessions):
- Refresh tokeny jsou náhodné kryptografické řetězce navázané na záznam v PostgreSQL tabulce
AuthSessions(včetně klientské IP adresy, User-Agentu a času expirace). Umožňují bezpečné vystavení nového access tokenu a správu či revokaci jednotlivých aktivních sessions přímo v profilu uživatele.
- Refresh tokeny jsou náhodné kryptografické řetězce navázané na záznam v PostgreSQL tabulce
- Obousměrné šifrování dat (ASP.NET Core Data Protection, AES-256):
- Slouží k utajení citlivých dat, která nesmí nikdo nepovolaný přečíst:
- Discord OAuth tokeny v DB: Access a refresh tokeny propojených Discord účtů se před uložením do tabulky
OAuthConnectionsšifrují přesIDataProtector(tokenProtector.Protect). V databázi jsou uložena pouze šifrovaná data, takže ani při případném úniku DB nehrozí zneužití Discord účtů. - Antiforgery (CSRF) cookies: Cookie
X-XSRF-TOKENobsahuje šifrovaný stav a kryptografický nonce, které chrání formuláře před Cross-Site Request Forgery útoky. - Dočasné OAuth cookies: Cookie
educhemlanparty_externalchrání stav přihlašování třetích stran během přesměrování.
- Discord OAuth tokeny v DB: Access a refresh tokeny propojených Discord účtů se před uložením do tabulky
- Slouží k utajení citlivých dat, která nesmí nikdo nepovolaný přečíst:
- Šifrování keyringu v Redisu (
DataProtectionKeyEncryptor):- XML keyring Data Protection obsahuje master klíče pro celou aplikaci. Před uložením do Redisu se XML payload šifruje pomocí AES-256-GCM.
- Šifrovací klíč se bezpečně odvozuje pomocí HKDF (SHA-256) z existujícího
JWT_SECRET, takže v konfiguraci není nutné spravovat další tajnou proměnnou.
- Uživatelská hesla a jednorázové odkazy:
- Uživatelská hesla jsou bezpečně hashována přes BCrypt (
EnhancedHashPasswords SHA-384 a work factorem 12). - Jednorázové přihlašovací a resetovací odkazy (Magic linky) se evidují v PostgreSQL tabulce
AccountEmailTokenss kryptografickým hashem (SHA-256), účelem a časem expirace. Při použití se v serializable transakci atomicky ověří a smažou.
- Uživatelská hesla jsou bezpečně hashována přes BCrypt (
Aplikace implementuje víceúrovňovou ochranu proti útokům hrubou silou, botům a přetížení:
- ASP.NET Core Rate Limiter (pro HTTP endpointy, při překročení vrací HTTP
429 Too Many Requests):auth-login: 60 požadavků / 1 min (podle IP adresy klienta)auth-forgot-password: 10 požadavků / 15 min (podle IP adresy klienta)auth-change-password: 10 požadavků / 1 hodina (podle uživatele nebo IP adresy)email-change: 30 požadavků / 1 min (podle uživatele nebo IP adresy)
- Brute-force ochrana přihlášení (
AuthService):- Sledování neúspěšných pokusů o heslo podle kombinace klientské IP adresy a identifikátoru účtu v cache. Po 5 neúspěšných pokusech se přihlášení pro daný účet z dané IP dočasně zablokuje.
- SignalR Rate Limiting (
HubRateLimitManager):- Plovoucí okno (sliding window) pro mutace rezervací (
Reserve,Unbook): max. 5 požadavků za 10 sekund na jednoho uživatele. Chrání rezervační hub před klikacími makry a spamováním serveru.
- Plovoucí okno (sliding window) pro mutace rezervací (
- Antiforgery validace:
- Globální
AntiforgeryValidationMiddlewarevaliduje přítomnost a platnostX-XSRF-TOKENhlavičky u všech stav měnících HTTP metod (POST,PUT,DELETE,PATCH).
- Globální
Při sdílení stejného Redis serveru mezi více aplikacemi nebo prostředími (dev / staging / prod):
- Konfigurovatelný prefix (
REDIS_KEY_PREFIX): Výchozí hodnota jeedulp:. Pokud uživatel zadá prefix bez dvojtečky (např.edulp), aplikace ji automaticky doplní naedulp:. - Data Protection keyring: Ukládá se pod názvem
${REDIS_KEY_PREFIX}DataProtection-Keys, což brání kolizi s obecným Microsoft výchozím klíčem"DataProtection-Keys". - Distribuovaná cache: Běží pod jmenným prostorem
${REDIS_KEY_PREFIX}cache:. - Pub/Sub kanály: Konfigurace StackExchange.Redis automaticky aplikuje
ChannelPrefix = RedisChannel.Literal(redisPrefix). - Logické databáze (
REDIS_DATABASE/REDIS_DB): Volitelná podpora pro výběr Redis databáze (0–15).
Administrace na /app/administration je rozdělená do záložek:
- Uživatelé: správa účtů, filtrování podle role, pohlaví, třídy, školy a povolených rezervací, reset hesla a impersonace dostupná podle role.
- Bezpečnostní logy: přehled databázových logů z
administration.Logs; endpoint je dostupný pro roleAdminaSuperAdmin. - Nastavení aplikace: stav rezervací
Closed,OpenneboUseTimer, časové okno rezervací, přepínač chatu a tlačítko pro vyčištění memory cache; dostupné pro roleAdminaSuperAdmin.
Docházka běží na /app/attendance a zapisuje záznamy do schématu attendance. Přihlášený uživatel zapisuje vlastní příchod/odchod, u odchodu musí vyplnit důvod. Role od TeacherOrg výš může zapisovat docházku za spravovatelné účty a stránka průběžně ukazuje počty přítomných, nepřítomných a celkový seznam účastníků s povolenými rezervacemi.
- Next.js 16 s App Routerem a standalone buildem
- React 19
- TypeScript 6
- @microsoft/signalr pro realtime rezervace
- SWR pro klientský data fetching
- Zustand pro lokální stav výběru rezervace
- Sass pro globální i modulové styly
react-hot-toastpro klientské hlášky- produkční hashování CSS module tříd přes custom
next.config.ts
- .NET 10
- ASP.NET Core controllers a SignalR hub
- Entity Framework Core 10
- PostgreSQL 18, mimo jiné kvůli
uuidv7() - Redis pro perzistenci Data Protection klíčů a distribuovanou cache s jmenným prostorem a prefixovou izolací
- ASP.NET Core Data Protection s AES-256-GCM šifrováním master keyringu odvozeného z
JWT_SECRETpřes HKDF - ASP.NET Core Rate Limiter pro ochranu auth endpointů a HubRateLimitManager pro SignalR plovoucí okna
- IMemoryCache pro rychlé rezervační snapshoty a brute-force ochranu
- MailKit pro SMTP emaily
- BCrypt (Enhanced SHA-384) pro bezpečné hashování hesel
- Czech vocative data pro oslovení uživatelů
- Razor view rendering pro HTML emailové šablony
- Docker multi-stage build
- Nginx reverse proxy s podporou WebSocket upgrade
- Node.js 24 build stage
- PostgreSQL
- Redis
Hlavní stránka |
Dashboard aplikace |
Realtime rezervace |
Administrace účtů |
.
|-- client/ # Next.js frontend
| |-- public/ # obrázky, ikony a fonty
| |-- src/app/(presentation)/ # veřejné prezentační routy
| |-- src/app/%5Fapi/payment-qr/route.ts # serverový handler pro platební QR
| |-- src/app/app/(withlayout)/account/ # účet, nastavení, achievementy a sessions
| |-- src/app/app/(withlayout)/reservations # přihlášené rezervace
| |-- src/app/app/(withlayout)/attendance # evidence příchodů a odchodů
| |-- src/app/app/(withlayout)/administration # správa uživatelů, logů a nastavení
| |-- src/components/reservation_areas/ # mapové oblasti pro rezervace
| |-- src/hooks/useSignalRHub.ts # obecný SignalR hook
| |-- src/lib/apiClient.ts # CSRF, refresh a opakování API požadavků
| |-- src/schemas/ # Zod schémata API odpovědí
| `-- package.json
|-- server/ # ASP.NET Core backend
| |-- Controllers/ # REST API v1
| |-- Data/Entities/ # databázové entity všech domén
| |-- Dto/ # datové modely API
| |-- Hubs/ReservationsHub.cs # realtime rezervace
| |-- Infrastructure/AuthConstants.cs # JWT konfigurace, cookies a auth policies
| |-- Migrations/ # EF Core migrace
| |-- Services/AuthService.cs # access tokeny a refresh sessions
| |-- Services/AppSettingsService.cs # globální nastavení aplikace
| |-- Services/DbLoggerService.cs # databázové bezpečnostní logy
| |-- Services/OAuth/ # jednotlivé OAuth platformy
| |-- Services/ReservationCacheService.cs # cache rezervačních dat
| |-- Views/Emails/ # HTML emailové šablony
| `-- server.csproj
|-- Dockerfile # produkční build celé aplikace
|-- nginx.conf # proxy pro /api, /hubs a Next.js frontend
|-- start.sh # start backendu, frontendu a nginxu
`-- Educhem LAN Party App.slnx
git clone https://github.com/AldiiX/Educhem-LAN-Party-App.git
cd Educhem-LAN-Party-App- .NET SDK 10
- Node.js 24
- Docker Desktop pro PostgreSQL a Redis
dotnet-efpro práci s migracemi:
dotnet tool restorecd client
npm installProjekt používá PostgreSQL 18 a Redis. Pro lokální vývoj stačí:
docker run --name edulp-postgres -e POSTGRES_DB=edulp_dev -e POSTGRES_USER=edulp -e POSTGRES_PASSWORD=edulp -p 5432:5432 -d postgres:18
docker run --name edulp-redis -p 6379:6379 -d redis:8Vytvoř soubor server/.env:
Nejdřív v PowerShellu vygeneruj náhodný JWT secret:
$bytes = [byte[]]::new(32)
[Security.Cryptography.RandomNumberGenerator]::Fill($bytes)
[Convert]::ToBase64String($bytes)Výstup vlož do JWT_SECRET:
PSQL_DB_HOST=localhost
PSQL_DB_PORT=5432
PSQL_DB_NAME=edulp_dev
PSQL_DB_USER=edulp
PSQL_DB_PASSWORD=edulp
REDIS_IP=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_KEY_PREFIX=edulp:
# REDIS_DATABASE=0
JWT_SECRET=vloz-sem-vygenerovanou-base64-hodnotu
WEB_URL=http://localhost:3547
STEAM_WEB_API_KEY=change-me
# Netuvio Identity (lokalne proti netuvio-identity dev serveru)
NETUVIO_AUTHORITY=http://localhost:3471
NETUVIO_CLIENT_ID=educhem-lanparty
NETUVIO_CLIENT_SECRET=stejna-hodnota-jako-v-netuvio-identity/secrets/educhem-lanparty-oidc
# Apple je vypnuty a jeho registrace zustava v kodu zakomentovana
# APPLE_CLIENT_ID=cz.example.educhemlanparty.web
# APPLE_TEAM_ID=change-me
# APPLE_KEY_ID=change-me
# APPLE_PRIVATE_KEY_BASE64=change-me
SMTP_HOST=smtp.example.com
SMTP_PORT=465
SMTP_EMAIL_USERNAME=lanparty@example.com
SMTP_EMAIL_PASSWORD=change-mePokud nechceš lokálně posílat emaily, SMTP hodnoty nech jako placeholdery. Funkce, které email odesílají, při špatné konfiguraci vrátí chybu do logu, ale aplikace zůstane běžet.
cd server
dotnet restore
dotnet ef database update
dotnet runBackend běží podle server/appsettings.json na:
http://localhost:8080
V druhém terminálu:
cd client
npm run devFrontend běží na:
http://localhost:3547
Next.js v dev režimu proxyuje API volání z /api/* na backend http://localhost:8080/api/*. SignalR hub je dostupný na /hubs/reservations.
Backend načítá proměnné z server/.env přes dotenv.net.
| Proměnná | Popis |
|---|---|
PSQL_DB_HOST |
Host PostgreSQL serveru |
PSQL_DB_PORT |
Port PostgreSQL serveru |
PSQL_DB_NAME |
Název databáze |
PSQL_DB_USER |
Uživatel databáze |
PSQL_DB_PASSWORD |
Heslo databáze |
REDIS_IP |
Host Redis serveru |
REDIS_PORT |
Port Redis serveru |
REDIS_PASSWORD |
Redis heslo, může být prázdné |
REDIS_KEY_PREFIX |
Prefix klíčů v Redisu (výchozí edulp:) pro izolaci od ostatních aplikací |
REDIS_DATABASE |
Volitelný index logické databáze v Redisu (výchozí 0), lze použít i alias REDIS_DB |
JWT_SECRET |
Náhodný Base64 secret pro podepisování access JWT; po dekódování musí mít alespoň 32 bajtů |
WEB_URL |
Pevný veřejný HTTP(S) origin aplikace pro emailové odkazy a OAuth callbacky |
STEAM_WEB_API_KEY |
Steam Web API klíč pro načtení jména a avataru propojeného Steam účtu |
NETUVIO_AUTHORITY |
Origin Netuvio Identity; lokálně http://localhost:3471, v produkci https://auth.netuvio.com |
NETUVIO_CLIENT_ID |
OIDC client ID educhem-lanparty |
NETUVIO_CLIENT_SECRET |
OIDC client secret shodný se souborem secrets/educhem-lanparty-oidc v Netuvio Identity |
APPLE_CLIENT_ID |
Apple Services ID použité jako OAuth client_id |
APPLE_TEAM_ID |
Team ID z Apple Developer účtu |
APPLE_KEY_ID |
ID privátního klíče s povoleným Sign in with Apple |
APPLE_PRIVATE_KEY_BASE64 |
Celý Apple .p8 privátní klíč zakódovaný v Base64; neukládat do Gitu |
SMTP_HOST |
SMTP server |
SMTP_PORT |
SMTP port, typicky 465 |
SMTP_EMAIL_USERNAME |
Odesílací email a SMTP login |
SMTP_EMAIL_PASSWORD |
SMTP heslo |
Nastavení jako ChatEnabled, ReservationsStatus, ReservationsEnabledFrom, ReservationsEnabledTo a ReservationsEnabledRightNow se ukládají do databázové tabulky administration.AppSettings a při startu aplikace se seedují výchozí hodnoty.
Netuvio používá OpenID Connect authorization-code flow s PKCE a scopes openid, profile a email. V Netuvio Identity musí být klient educhem-lanparty zapnutý a jeho secret musí odpovídat NETUVIO_CLIENT_SECRET.
Registrované callbacky jsou:
http://localhost:3547/api/v1/netuvio/callback
https://lanparty.educhem.it/api/v1/netuvio/callback
Secret vygeneruj alespoň 32 znaků dlouhý, například openssl rand -base64 48, a nikdy ho neukládej do Gitu.
Integrace zůstává v kódu připravená, ale registrace backend provideru i položka v UI jsou záměrně zakomentované. Pro pozdější zpřístupnění je odkomentuj a nastav níže popsané APPLE_* proměnné.
Sign in with Apple neposkytuje profilovou fotku ani URL avataru. Apple proto zůstává mimo nabídku synchronizace avataru a propojení může sloužit jen k přihlášení a identifikaci účtu.
Apple webové přihlášení vyžaduje členství v Apple Developer Programu, primární App ID s povoleným Sign in with Apple, navázané Services ID a privátní .p8 klíč. V Apple Developer portálu zaregistruj produkční doménu a přesnou návratovou URL:
https://tvoje-domena.cz/api/v1/apple/callback
Apple nepovoluje jako návratovou URL localhost, IP adresu ani nezabezpečené HTTP. Proto Apple přihlášení při lokálním WEB_URL=http://localhost:3547 záměrně vrátí stav 503; ověřuje se až přes registrovanou HTTPS doménu.
Hodnotu APPLE_PRIVATE_KEY_BASE64 vytvoř z obsahu staženého .p8 klíče, například v PowerShellu:
[Convert]::ToBase64String([IO.File]::ReadAllBytes("AuthKey_CHANGE_ME.p8"))Projekt používá EF Core migrace v server/Migrations.
Migrace se při startu aplikace automaticky neaplikují. Před spuštěním nové verze je potřeba aktualizovat databázi ručně. Startup pouze doplňuje chybějící výchozí nastavení do již existujícího schématu.
Vytvoření nebo aktualizace databáze:
cd server
dotnet ef database updateVytvoření nové migrace:
cd server
dotnet ef migrations add NazevMigraceAktuální model obsahuje hlavně:
Accountspro uživatelské účty, role, profily a povolení rezervací.AuthSessionspro refresh sessions, jejich expiraci a revokaci přihlášených zařízení.EmailChangeRequestspro čekající změnu e-mailu aEmailChangeAttemptspro limity žádostí a odesílání.AccountEmailLinks(entitaAccountEmailToken) pro jednorázové přihlašovací a resetovací tokeny, které změna hesla nebo e-mailu zneplatní.OAuthConnectionspro propojené Discord, Google, GitHub, Steam a připravené Apple účty.EnrollmentsaSchoolspro školu a volitelnou třídu zobrazovanou u profilu.Achievements,Badges,AccountAchievementsaAccountBadgespro achievement systém.Computersv databázovém schématureservations.Roomsv databázovém schématureservations.Reservationsjako společný základ proComputerReservationaRoomReservation.ProblemReportspro hlášení problémů a jejich stav.AttendanceEntriesve schématuattendancepro příchody a odchody účastníků.Logsve schématuadministrationpro bezpečnostní a provozní logy.AppSettingsve schématuadministrationpro globální nastavení aplikace.- Unikátní index na
Reservation.AccountId, takže jeden účet může mít jen jednu aktivní rezervaci. - PostgreSQL enumy
AccountGenderaAccountType.
Produkce se balí do jednoho image:
- frontend se sestaví jako Next.js standalone aplikace,
- backend se publikuje jako .NET aplikace,
- Nginx slouží jako reverse proxy na portu
80, /api/*jde na ASP.NET Core backend,/hubs/*a SignalR komunikace používají proxy s WebSocket upgrade hlavičkami,- ostatní routy jdou na Next.js frontend.
Build image:
docker build -t educhem-lan-party-app .Při buildu se do image zkopíruje server/.env, pokud existuje. Pokud chceš předat konfiguraci bezpečněji přes BuildKit secret, Dockerfile podporuje secret BACKEND_ENV_B64.
Spuštění image:
docker run --name educhem-lan-party-app -p 80:80 educhem-lan-party-apphttp://localhost:3547/- hlavní stránkahttp://localhost:3547/info- informace o akcihttp://localhost:3547/history- historie LAN partyhttp://localhost:3547/reservation- veřejná rezervační stránkahttp://localhost:3547/rules- pravidlahttp://localhost:3547/schedule- harmonogramhttp://localhost:3547/faq- často kladené otázkyhttp://localhost:3547/organizers- organizátoři akcehttp://localhost:3547/login- prezentační vstup do přihlášení
http://localhost:3547/app/login- přihlašovací formulářhttp://localhost:3547/app/login-link- dokončení přihlášení přes jednorázový odkaz z emailuhttp://localhost:3547/app/reset-password- formulář pro nastavení nového hesla z emailového odkazuhttp://localhost:3547/app/change-email- potvrzení nebo zrušení změny emailové adresy z odkazuhttp://localhost:3547/app- dashboardhttp://localhost:3547/app/announcements- oznámeníhttp://localhost:3547/app/map- mapa rezervací bez hlavního rezervačního paneluhttp://localhost:3547/app/account- přehled účtuhttp://localhost:3547/app/account/settings- nastavení profilu, hesla a propojených platforemhttp://localhost:3547/app/account/achievements- achievementy a odznaky účtuhttp://localhost:3547/app/account/devices- správa aktivních přihlášeníhttp://localhost:3547/app/profile- vlastní profilhttp://localhost:3547/app/profile/{uuid}- veřejný profil účastníkahttp://localhost:3547/app/reservations- realtime rezervace místhttp://localhost:3547/app/attendance- docházka účastníkůhttp://localhost:3547/app/tournaments- turnajehttp://localhost:3547/app/support- nahlášení problému; původní/app/problemsem přesměrujehttp://localhost:3547/app/administration/users- administrace účtůhttp://localhost:3547/app/administration/logs- bezpečnostní logyhttp://localhost:3547/app/administration/settings- globální nastavení aplikacehttp://localhost:3547/app/system-disabled- systémová stránka pro vypnutou aplikaci
GET /api/v1/account- aktuálně přihlášený účetGET /api/v1/account/dashboard- dashboard statistikyGET /api/v1/account/all- seznam účtů pro organizátoryPOST /api/v1/account- vytvoření účtuPUT /api/v1/account/{id}- úprava účtuDELETE /api/v1/account/{id}- smazání účtuPOST /api/v1/account/{id}/reset-password- reset hesla účtu administrátoremPOST /api/v1/account/{id}/impersonate- přihlášení jako jiný účet podle oprávněníGET /api/v1/auth/csrf- vystavení CSRF tokenuPOST /api/v1/auth/login- přihlášení a vytvoření access a refresh cookiesPOST /api/v1/auth/refresh- obnova access tokenu přes refresh sessionPOST /api/v1/auth/logout- odhlášení a revokace aktuální refresh sessionGET /api/v1/account/login-link- přesměrování z odkazu v emailu na frontendPOST /api/v1/account/login-link/preview- náhled cílového účtu pro jednorázový přihlašovací odkazPOST /api/v1/account/login-link- přihlášení přes jednorázový odkaz z emailuPOST /api/v1/account/forgot-password- odeslání reset odkazuPOST /api/v1/account/reset-password/preview- náhled cílového účtu pro reset heslaPOST /api/v1/account/reset-password- potvrzení resetu heslaGET /api/v1/account/email-change- stav rozpracované žádosti o změnu emailuPOST /api/v1/account/email-change- zahájení dvoufázové změny emailuPOST /api/v1/account/email-change/resend- opětovné odeslání potvrzovacích emailůPOST /api/v1/account/email-change/cancel- zrušení žádosti o změnu emailuPOST /api/v1/account/email-change/preview- veřejný náhled tokenu z odkazu v emailuPOST /api/v1/account/email-change/confirm- potvrzení nebo zrušení změny přes token z odkazuPUT /api/v1/account/me- úprava vlastního účtuPUT /api/v1/account/avatar-sync-platform- nastavení synchronizace avataru z propojené platformyPOST /api/v1/account/me/password- změna vlastního heslaGET /api/v1/account/sessions- seznam aktivních sessions účtuDELETE /api/v1/account/sessions/{id}- revokace vybrané sessionDELETE /api/v1/account/sessions/other- revokace všech ostatních sessionsGET /api/v1/{provider}/login- zahájení přihlášení přes externí platformuGET /api/v1/{provider}/connect- propojení platformy s přihlášeným účtemDELETE /api/v1/{provider}/connection- odpojení platformy od účtuPUT /api/v1/account/me/achievements/{id}- nastavení viditelnosti / skrytí achievementuPUT /api/v1/account/me/badges/{id}- připnutí nebo odepnutí odznaku (badge) na profiluGET /api/v1/profile- profil aktuálního uživateleGET /api/v1/profile/{uuid}- veřejný profil podle UUIDGET /api/v1/reservations/rooms-and-computers- místnosti a počítače pro mapuGET /api/v1/reservations/computers-and-rooms- alias pro mapová dataGET /api/v1/reservations/status- souhrnný stav kapacity a povolených rezervacíGET /api/v1/reservations- seznam rezervací pouze v debug buildu; v produkci rezervace probíhají přes socketGET /api/v1/attendance- přehled docházky přihlášeného uživatele nebo organizátoraPOST /api/v1/attendance- zápis příchodu/odchoduGET /api/v1/adm/logs- bezpečnostní logy proAdminaSuperAdminGET /api/v1/appsettings- nastavení aplikace proAdminaSuperAdminPUT /api/v1/appsettings- úprava nastavení aplikace proAdminaSuperAdminPOST /api/v1/appsettings/cache/clear- vyčištění memory cache proAdminaSuperAdminGET /api/v1/problem-reports- seznam hlášení problémůGET /api/v1/problem-reports/availability- dostupnost vytváření hlášeníPOST /api/v1/problem-reports- vytvoření hlášení problémuPUT /api/v1/problem-reports/{id}/status- změna stavu hlášeníDELETE /api/v1/problem-reports/{id}- smazání hlášení
Hodnota {provider} může být discord, google, github, steam nebo apple; Apple endpointy jsou dostupné až po zapnutí a úplné konfiguraci provideru.
Hub běží na:
/hubs/reservations
Access JWT zůstává v HttpOnly cookie. Samostatná čitelná cookie edlp_access_expires (v produkci __Host-edlp_access_expires) obsahuje pouze čas expirace v unixových sekundách a slouží klientovi k naplánování obnovy. Při odhlášení se maže společně s tokeny; sama o sobě nepovoluje přístup.
Server ukončí přihlášené spojení po expiraci access tokenu. Před prvním připojením i automatickým reconnectem přihlášený klient zkontroluje čas expirace. Refresh volá jen při chybějícím údaji, zbývající platnosti nejvýše 15 sekund nebo odpovědi 401 při vyjednání spojení. Platný token se při běžném reconnectu nemění. Souběžné obnovy přes API a SignalR v jedné kartě sdílejí jeden požadavek.
Přihlášený klient přidává requireAuthentication=true, takže chybějící nebo neplatný JWT vede k 401 místo tichého připojení jako anonym. Neplatná refresh session ukončí opakování a zobrazí odkaz na přihlášení; síťové chyby používají běžné opakování spojení. Anonymní návštěvníci refresh nevolají. Každé nové spojení dostane aktuální snapshot rezervací.
Serverové metody volané klientem:
Reserve({ id, type })- rezervuje počítač nebo místnost, kdetypejecomputerneboroom.Unbook()- zruší aktuální rezervaci přihlášeného účtu.
Události posílané klientům:
ReceiveReservations- počáteční snapshot rezervací po připojení.ReservationsChanged- delta změna po rezervaci nebo zrušení.ReceiveStatus- počet aktuálně připojených klientů.ReceiveError- chybová hláška pro volajícího klienta.
Doporučené předpony commitu:
FEAT- přidání nové funkcionalityFIX- oprava chybyDOCS- úprava dokumentaceSTYLE- formátování bez změny chování kóduREFACTOR- úprava kódu bez přidání funkce nebo opravy chybyTEST- testyCHORE- údržba projektuBUILD- build system nebo závislostiCI- kontinuální integracePERF- výkonnostní zlepšeníREVERT- návrat změn
Vytvořili Stanislav Škudrna, Serhii Yavorskyi pro Střední školu EDUCHEM, a.s. v roce 2026.
Původní projekt byl vytvořen v roce 2024: https://github.com/aldiix/EDUCHEM-LAN-Party-Web




