ShareBook — platforma wymiany książek między użytkownikami
Projekt Full-Stack · Python · React · AI · WebSocket

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.

React + TypeScript TanStack Query FastAPI PostgreSQL + pgvector JWT + CSRF WebSocket OpenAI · DALL-E 3 Vitest

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

Frontend React TanStack Query · Zustand Playfair + Inter · shadcn/ui WebSocket chat · cover live preview useBookCoverWS hook Panel Admin JWT role: admin users · books · stats FastAPI · REST API v1 + WebSocket slowapi rate limiting · CORS · JWT auth middleware PostgreSQL + pgvector async SQLAlchemy 2.0 Services Layer books · loans · auth messages · admin AI Layer BookSearchAgent AIService · CoverAI OpenAI API · DALL-E 3 StaticFiles /covers (PIL)

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. Hook useBookCoverWS odbiera 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_tokenhttpOnly=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

sharebook/ ├── backend/ │ ├── src/ │ │ ├── api/v1/endpoints/ # endpointy REST + WebSocket │ │ ├── services/ │ │ │ ├── interfaces/ # ★ protokoły ABC dla wszystkich serwisów │ │ │ │ ├── books.py # IBookService, IUserBookService │ │ │ │ ├── loans.py # ILoanService, ILoanRequestService │ │ │ │ ├── auth.py # IAuthService │ │ │ │ ├── ai.py # IAIService, IVectorService │ │ │ │ ├── cover.py # ICoverService │ │ │ │ └── factory.py # IServiceFactory │ │ │ ├── ai/ # RAG chatbot (AIService + VectorService) │ │ │ ├── book_discovery/ # BookSearchAgent + QueryBuilder │ │ │ ├── cover/ # CoverAIGenerator (DALL-E 3) + PIL resize │ │ │ ├── websocket/ # Manager + Registry + Subscriptions │ │ │ ├── auth/ # AuthService + TokenService + CookieService │ │ │ ├── books/ # BookService + UserBookService │ │ │ ├── loans/ # LoanService + LoanRequestService │ │ │ ├── messages/ # MessageService │ │ │ └── factories.py # fabryka serwisów (DI) │ │ ├── core/ │ │ │ ├── security.py # JWT create/decode, bcrypt, CSRF │ │ │ ├── exceptions.py # hierarchia ShareBookException │ │ │ ├── token_blacklist.py # blacklista wylogowanych tokenów │ │ │ └── constants.py # stałe (tokeny, bcrypt rounds) │ │ ├── schemas/ # Pydantic v2 models │ │ └── main.py # FastAPI app + slowapi + lifespan │ └── database/ │ ├── models.py # User, Book, UserBook, Loan, LoanRequest, │ │ # Message, BookChunk (pgvector) │ ├── repositories/ # 6 repozytoriów (Repository Pattern) │ ├── interfaces.py # IUserRepository, IBookRepository… │ └── covers/ # okładki (StaticFiles) │ ├── frontend/ │ └── src/ │ ├── api/ # klienty API: auth, books, loans, admin, ai │ ├── components/ # books, loan-requests, auth, ai, layout │ ├── hooks/ # useBookCoverWS, useBooks, useUserBooks… │ ├── pages/ # reader, admin, auth, browse, books, ai │ ├── store/ # Zustand: userBooksStore │ └── types/ # TypeScript interfaces │ ├── backend/scripts/ │ └── seed_database.py # seed 50+ książek z covers └── requirements.txt

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 Factoryfactories.py tworzy i wstrzykuje serwisy, odwrócenie zależności przez FastAPI Depends()
  • Domain Exceptions — hierarchia ShareBookException mapowana na kody HTTP w globalnym handlerze

09Model danych

Sześć encji w PostgreSQL, w tym BookChunk z kolumną Vector(1536) dla RAG:

User ──────────────── UserBook ─────────── Book id, email, role, id, user_id, id, isbn (unique), location, is_active book_id, title, author, status, description, is_lendable, cover_url, genre condition │ ├── LoanRequest ──── Message │ id, user_book_id id, loan_request_id, │ requester_id sender_id, content, │ owner_id is_read, message_type │ status, message │ └── Loan id, user_book_id, borrower_id, lender_id, loan_date, due_date, return_date Book ──── BookChunk ← pgvector id, book_id, content, embedding Vector(1536), chunk_index

10REST API — wybrane endpointy

POST
/api/v1/auth/register
rejestracja + walidacja siły hasła (zxcvbn)
POST
/api/v1/auth/login
logowanie → 3 cookies (access, refresh, csrf) · rate limited
POST
/api/v1/auth/refresh
cichy refresh access tokena przez refresh cookie
GET
/api/v1/community/books
książki społeczności z filtrowaniem i paginacją
POST
/api/v1/my-books
dodanie książki przez ISBN → BookSearchAgent → cover
PATCH
/api/v1/my-books/{id}/lendable
toggle: czy egzemplarz jest dostępny do wypożyczenia
POST
/api/v1/loan-requests
nowa prośba o wypożyczenie od właściciela
PATCH
/api/v1/loan-requests/{id}
akceptacja / odrzucenie prośby (z powodem)
POST
/api/v1/ai/chat
zapytanie do chatbota-bibliotekarza → RAG po pgvector
POST
/api/v1/ai/sync
re-embedding całego katalogu książek
GET
/api/v1/admin/dashboard
statystyki: użytkownicy, książki, wypożyczenia, aktywność
DELETE
/api/v1/admin/users/{id}
usunięcie użytkownika (cascade na wszystkie jego dane)

11Testowanie

Projekt ma skonfigurowane środowiska testowe zarówno po stronie backendowej jak i frontendowej:

  • Backendpytest + pytest-asyncio + httpx jako async test client dla FastAPI. Konfiguracja mypy (mypy.ini) + ruff jako linter
  • Frontendvitest z konfiguracją w vitest.config.ts, uruchamiany przez npm test, z opcją --coverage
  • Seed danychscripts/seed_database.py z 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 GitHub

13Aplikacja 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.

· · ·
Udostępnij jeśli spodobał Ci się mój projekt