ShareBook — platforma wypożyczania książek w lokalnej społeczności
Aplikacja webowa, która łączy czytelników w jednej społeczności: udostępniasz swoje książki, przeglądasz katalog innych użytkowników, wysyłasz prośby o wypożyczenie i dogadujesz szczegóły przez wbudowany real-time chat. Za kulisami — trzy warstwy AI: agent wyszukujący metadane przez OpenAI web search, chatbot-bibliotekarz z RAG (pgvector) i automatyczne generowanie okładek przez DALL-E 3.
Projekt rozwiązuje konkretny problem: zamiast kupować książkę, którą przeczytasz raz, możesz wypożyczyć ją od kogoś z okolicy. Każdy użytkownik skanuje ISBN posiadanych książek — resztą zajmuje się aplikacja: wyszukuje metadane (tytuł, autor, gatunek, opis), pobiera lub generuje okładkę i wystawia egzemplarz do wypożyczenia przez społeczność.
Backend zbudowany jest na FastAPI z async SQLAlchemy 2.0, z pełną warstwą interfejsów
serwisów (Protocol), rate limitingiem (slowapi), walidacją siły hasła (zxcvbn),
strukturyzowanymi logami (structlog) i typowaniem przez mypy.
Frontend używa TanStack React Query do cache’owania i synchronizacji stanu serwera
oraz Zustand do lokalnego stanu biblioteki użytkownika.
01Funkcjonalności
Dodawanie książek przez ISBN
Wpisujesz ISBN, agent AI szuka metadanych przez OpenAI web search + Open Library. Okładkę pobiera automatycznie lub generuje przez DALL-E 3.
Przeglądanie społeczności
Katalog książek dostępnych od innych użytkowników z filtrowaniem po tytule, autorze, gatunku i lokalizacji właściciela.
Prośby o wypożyczenie
Wyślij prośbę do właściciela książki. Właściciel akceptuje lub odrzuca z podaniem powodu. Każda prośba ma własny wątek czatu.
Real-time chat
Wiadomości między wypożyczającym a właścicielem przez WebSocket. Status przeczytana/nieprzeczytana dla każdej wiadomości.
Chatbot-bibliotekarz
RAG po katalogu książek — zadajesz pytanie (“coś o Japonii”), model wyszukuje semantycznie w opisach i rekomenduje pozycje z bazy.
Generowanie okładek AI
Gdy Open Library nie ma okładki dla danego ISBN — DALL-E 3 generuje artystyczną okładkę na podstawie tytułu, autora i gatunku.
Autentykacja JWT + CSRF
Triple-cookie: access token (httpOnly), refresh token, CSRF token. Rate limiting logowania, bcrypt z 12 rundami, walidacja siły hasła przez zxcvbn.
Panel administracyjny
Dashboard ze statystykami, zarządzanie użytkownikami (role, blokowanie), książkami w katalogu i kopiami użytkowników.
02Architektura systemu
03Interfejs — ciepła estetyka biblioteki
Frontend utrzymany jest w palecie warm-beige (#FAF7F2), złota book-gold (#C4A77D)
i głębokiej brązu book-brown (#2D2A26). Nagłówki w Playfair Display (serif, elegancki),
treść w Inter. Karty książek mają cień shadow-book i unoszą się przy hoverze o 8px z mocniejszym cieniem.
Scrollbar w kolorze złota — każdy element interfejsu spójny z klimatem tradycyjnej biblioteki.
Ekrany aplikacji
- Landing page — strona powitalna z sekcją “jak to działa”, CTA do rejestracji
- Browse — katalog książek społeczności, filtry, karty z okładką, autorem, statusem dostępności
- Book Detail — szczegóły książki, status egzemplarzy, przycisk prośby o wypożyczenie
- Reader Panel — moje książki / wypożyczone / oddane / prośby przychodzące i wychodzące
- Add Book Dialog — dodanie przez ISBN: agent AI zwraca metadane, podgląd okładki generowanej live przez WebSocket
- AI Chat — rozmowa z asystentem-bibliotekarzem, wyniki z podaniem źródeł (tytuł, autor, similarity score)
- Profile — edycja profilu, zmiana hasła, liczba posiadanych książek
- Admin Panel — Dashboard / Users / Books / User Books — 4 sekcje na jednej stronie z Tabs
Szczegół UX: status egzemplarzy (available, reserved, borrowed, unavailable)
jest color-coded — zielony, bursztynowy, niebieski, szary — przez klasy Tailwind .status-available itd.
Każdy badge czytelnie informuje o tym, czy książkę da się teraz wypożyczyć.
04Trzy warstwy AI
1. BookSearchAgent — metadane przez OpenAI web search
Gdy użytkownik wpisuje ISBN, aplikacja nie odpytuje tylko Open Library — uruchamia agenta OpenAI
z narzędziem web_search, który szuka informacji o książce w internecie i zwraca
ustrukturyzowany JSON (tytuł, autor, opis, gatunek, ISBN-13, rok wydania). Fallback na Open Library
jeśli OpenAI jest niedostępny.
async def search(self, query: str) -> BookData:
search_query = self.query_builder.build(query)
response = await self.client.responses.create(
model=self.model,
tools=[{"type": "web_search"}], # ← OpenAI web_search tool
input=[
{"role": "system", "content": self.query_builder.get_system_prompt()},
{"role": "user", "content": self.query_builder.build_prompt(search_query)}
]
)
llm_data = self._extract_json(response.output_text)
cover_url = await self._get_cover_url(llm_data, search_query.normalized_isbn)
return self.result_formatter.format(llm_data, cover_url)
2. AIService — chatbot-bibliotekarz z RAG
Zapytanie użytkownika (“coś o podróżach po Azji”) jest zamieniane na embedding, który trafia do pgvector i szuka semantycznie podobnych fragmentów opisów książek z katalogu. Znalezione chunki wchodzą jako kontekst do promptu systemowego — model odpowiada wyłącznie na ich podstawie, bez wymyślania nieistniejących tytułów. Odpowiedź zawiera listę źródeł z similarity score.
3. CoverAIGenerator — okładki przez DALL-E 3
Gdy okładka nie jest dostępna w Open Library, serwis generuje ją przez DALL-E 3. Prompt zapewnia,
że okładka jest artystyczna, bez tekstu i liter, w proporcjach pionowej książki, skalowana
przez PIL do 180px szerokości z crop do proporcji 1:1.5. Wynik jest serwowany przez StaticFiles /covers.
Generowanie przebiega asynchronicznie — frontend jest informowany przez WebSocket gdy okładka jest gotowa.
prompt = (
f"Professional book cover design for '{title}' by {author}.{genre_hint} "
f"Artistic cover, no text, no letters, no words, "
f"vertical book cover proportions, high quality, "
f"minimalist design suitable for 180px thumbnail"
)
response = await self.client.images.generate(
model="dall-e-3", prompt=prompt,
size="1024x1024", quality="standard", n=1
)
# → PIL resize do 180×270px → zapis do /covers/{isbn}.jpg
05WebSocket — real-time w dwóch miejscach
Backend ma pełny WebSocketManager z trzema komponentami:
ConnectionRegistry (aktywne połączenia + ping/pong),
SubscriptionManager (subskrypcje book_id → lista socketów)
i Broadcaster (send personal + broadcast to book).
- Cover live preview — po wysłaniu ISBN do API frontend subskrybuje WebSocket na
book_id. Gdy serwer skończy generować okładkę, broadcastuje{type: "cover_ready", url: "..."}do wszystkich subskrybentów. HookuseBookCoverWSodbiera wiadomość i aktualizuje UI bez odświeżania strony. - Chat wypożyczenia — wiadomości między użytkownikiem a właścicielem w czasie rzeczywistym,
z obsługą statusu przeczytana/nieprzeczytana i typami wiadomości (
text, zdarzenia systemowe).
Wzorzec subskrypcji book_id: jeden WebSocket może subskrybować wiele ID jednocześnie.
subscribe(book_id, websocket) i unsubscribe(book_id, websocket) zarządzają mapą
Dict[str, Set[WebSocket]] po stronie serwera.
06Autentykacja — triple-cookie JWT
Zamiast przechowywania tokenów w localStorage (podatne na XSS),
ShareBook używa trzech cookies ustawianych przez serwer:
- access_token — JWT (30 minut),
httpOnly=True,SameSite=Lax— niedostępny dla JS - refresh_token — JWT (7 dni),
httpOnly=True— używany do cichego odświeżenia access tokena - csrf_token —
httpOnly=False, odczytywany przez JS i dołączany do nagłówka każdego żądania modyfikującego dane
Dodatkowe zabezpieczenia: rate limiting przez slowapi (limit na endpoint logowania),
bcrypt z 12 rundami (BCRYPT_ROUNDS), walidacja siły hasła przez bibliotekę zxcvbn
i token blacklist dla wylogowanych tokenów.
def verify_csrf_token(token: str, cookie_token: str) -> bool:
if not token or not cookie_token:
return False
if len(token.encode('utf-8')) < 32:
return False
return secrets.compare_digest(token, cookie_token)
# ↑ compare_digest zapobiega timing attacks
07Stack technologiczny
Frontend
- React + TypeScriptpełne typowanie, zero any
- TanStack React Query v5cache serwera, optimistic updates, refetch on focus
- Zustandstore dla stanu biblioteki użytkownika
- Vitedev server, HMR, build
- Tailwind CSS + shadcn/uiPlayfair Display · Inter, warm palette
- React Hook Form + Zodwalidacja formularzy
- Vitesttesty jednostkowe (
npm test) - @clerk/clerk-reactdependency w package.json (auth UI)
Backend
- FastAPIasync, WebSocket, auto-docs, lifespan
- SQLAlchemy 2.0 asyncMapped[] typed ORM
- PostgreSQL + pgvectorrelacje + wektory w jednej bazie
- python-jose + bcryptJWT HS256, bcrypt 12 rounds
- slowapirate limiting na endpointach
- zxcvbnwalidacja siły hasła
- Pillow (PIL)resize okładek do 180×270px
- mypy + rufftypowanie statyczne, linter
- pytest + httpxtesty backendowe
- tenacityretry z exponential backoff dla OpenAI
08Struktura projektu
Wzorce architektoniczne
- Interface Segregation (Protocol) — każdy serwis ma swój interface ABC (
IBookService,IAIService…). Endpointy zależą od abstrakcji, nie od konkretnych implementacji - Repository Pattern — warstwa dostępu do danych oddzielona od logiki biznesowej (6 repozytoriów)
- Service Factory —
factories.pytworzy i wstrzykuje serwisy, odwrócenie zależności przez FastAPIDepends() - Domain Exceptions — hierarchia
ShareBookExceptionmapowana na kody HTTP w globalnym handlerze
09Model danych
Sześć encji w PostgreSQL, w tym BookChunk z kolumną Vector(1536) dla RAG:
10REST API — wybrane endpointy
rejestracja + walidacja siły hasła (zxcvbn)
logowanie → 3 cookies (access, refresh, csrf) · rate limited
cichy refresh access tokena przez refresh cookie
książki społeczności z filtrowaniem i paginacją
dodanie książki przez ISBN → BookSearchAgent → cover
toggle: czy egzemplarz jest dostępny do wypożyczenia
nowa prośba o wypożyczenie od właściciela
akceptacja / odrzucenie prośby (z powodem)
zapytanie do chatbota-bibliotekarza → RAG po pgvector
re-embedding całego katalogu książek
statystyki: użytkownicy, książki, wypożyczenia, aktywność
usunięcie użytkownika (cascade na wszystkie jego dane)
11Testowanie
Projekt ma skonfigurowane środowiska testowe zarówno po stronie backendowej jak i frontendowej:
- Backend —
pytest+pytest-asyncio+httpxjako async test client dla FastAPI. Konfiguracja mypy (mypy.ini) + ruff jako linter - Frontend —
vitestz konfiguracją wvitest.config.ts, uruchamiany przeznpm test, z opcją--coverage - Seed danych —
scripts/seed_database.pyz 50+ książkami (dane JSON) do natychmiastowego załadowania środowiska dev
12Czego dowodzi ten projekt
Pełny product cycle
Od modelu bazy, przez serwisy i API, po animowane karty książek w przeglądarce — samodzielnie.
Nowoczesny AI stack
RAG (pgvector + embeddingi), web_search agent, DALL-E 3, retry z tenacity — trzy odrębne wzorce użycia LLM.
WebSocket
Real-time z subskrypcją per book_id, ping/pong heartbeat, connection registry — nie tylko echo server.
Interface Segregation
Pełna warstwa protokołów ABC dla serwisów i repozytoriów — kod zależy od abstrakcji, nie implementacji.
Bezpieczeństwo auth
Triple-cookie JWT, CSRF, bcrypt 12 rounds, zxcvbn, token blacklist, rate limiting — wielowarstwowa ochrona.
TanStack Query
Cache serwera, stale-while-revalidate, optimistic updates — nie useState + useEffect do każdego fetcha.
Testy + jakość kodu
pytest async, vitest, mypy, ruff — projekt z dbałością o jakość, nie tylko “działa na moim komputerze”.
Realne use case
Platforma z logiką biznesową: wypożyczenie ma właściciela i pożyczającego, stanową maszyną statusów i historią.
Zobacz pełny kod na GitHubie
Repozytorium zawiera pełny frontend, backend, seed danych z 50+ książkami i wszystkie szczegóły implementacji — od WebSocket managera po DALL-E cover generator.
Zobacz kod na GitHub13Aplikacja w akcji
Poniżej wideo pokazujące ShareBook w działaniu — dodawanie książki przez ISBN, generowanie okładki przez DALL-E, prośba o wypożyczenie i chat.