About This Project

A full-stack library management platform built with modern web technologies, covering the entire lifecycle from book browsing to borrowing, with role-based access control at every layer.

General Information

City Library is a web platform that digitises the workflow of a public library. Its goal is to let community members discover, reserve, and borrow books online — eliminating paper-based queues and manual record-keeping. Library staff get a dedicated management interface, and the administrator retains full control over who can do what.

Roles & Permissions

Regular User

  • Browse and search books
  • Filter by genre, author, year
  • Add books to cart
  • Submit borrow requests
  • Track personal bookings
  • Rate books (1–5 stars)
  • Manage profile & password
  • Browse library events

Librarian

  • All browsing capabilities
  • Add books (if permitted)
  • Edit book details (if permitted)
  • Delete books (if permitted)
  • View all bookings (if permitted)
  • Approve / reject requests
  • Permissions set by admin

Administrator

  • Full book management
  • Full booking management
  • Manage librarian accounts
  • Grant / revoke permissions
  • Access to all data
  • Librarian registration
  • Librarian deletion

Key Features

Real-time book search with debounce
Multi-filter sidebar (genre, author, year, status)
Sort by newest, oldest, most / least popular
Paginated book catalogue
Shopping-cart borrow flow
Star rating system per book
Library events with images
Admin-controlled librarian permissions
HTTP-only JWT cookie authentication
Auto token refresh (5-minute access token)

Frontend

Framework

Next.js 15 (App Router)

File-system routing, React Server Components, layout nesting, Suspense boundaries for async data.

Language

TypeScript — strict mode

All components, hooks, and utilities are fully typed. No implicit any. Shared types in /types/index.ts.

Styling

Tailwind CSS v4

Utility-first with a custom colour palette (@theme inline). Animations and skeleton screens via CSS keyframes.

State Management

Redux Toolkit

Slices for auth, cart. Async thunks for all API calls. AppInitializer restores session on page load.

HTTP Layer

Custom Fetch wrapper

Built on the native Fetch API — no Axios. Handles JSON and FormData, credentials: include, unified error shape.

Image Handling

Next.js Image component

Automatic WebP conversion, lazy loading, and size-aware srcset. Remote patterns configured per environment.

Architecture & Patterns

App Router structureRoutes grouped by layout: (main) for single-column pages, (books) for the sidebar layout. Each group has its own layout.tsx.

Authentication flowHTTP-only JWT cookies set by the backend. CookieJWTAuthentication reads the token on every request. AppInitializer calls /auth/me/ on mount to rehydrate Redux from the cookie.

Permission utilitieslib/permissions.ts exposes canAddBooks, canEditBooks, canDeleteBooks, canViewBookings — each returning true for admin always, or checking the librarian's permissions array.

UX detailsDebounced search (400 ms), skeleton card placeholders during fetch, fade-in animations on content arrival, portal-based modals immune to stacking-context issues.

Code qualityESLint + TypeScript strict mode enforced. Functional components only. No class components, no any types, no direct DOM manipulation outside of refs.

Backend

Framework

Django 5 + Django REST Framework

Class-based APIViews for every endpoint. DRF serializers handle validation, nested relations, and field-level read_only.

Database

PostgreSQL

Relational schema with Django ORM. Migrations track every schema change. JSONField stores librarian permission arrays.

Authentication

SimpleJWT — HTTP-only cookies

Access token (5 min) + refresh token (1 day). CookieJWTAuthentication reads token from cookie, never from Authorization header.

RBAC

Role field + LibrarianProfile

CustomUser.role ∈ {user, librarian, admin}. Librarians get a LibrarianProfile with a JSON permissions list managed by admins.

Media Files

Django FileField / ImageField

Book covers and user avatars stored under MEDIA_ROOT. Full URLs built with request.build_absolute_uri in serializers.

CORS & CSRF

django-cors-headers

CORS_ALLOW_CREDENTIALS=True with explicit origin allowlist. CSRF_TRUSTED_ORIGINS matches CORS origins. DRF APIView is csrf_exempt.

API & Security

Endpoint designAll routes under /api/v1/. Books, events, bookings, cart, and auth each live in their own Django app with separate urls.py. No raw SQL — exclusively Django ORM.

Token securityTokens stored in HttpOnly; Secure; SameSite=Lax cookies — inaccessible to JavaScript. Rotation on every refresh ensures a stolen refresh token is single-use.

Input validationEvery write endpoint goes through a DRF serializer with explicit field types, validators, and partial=True for PATCH. Django password validators enforce strength on registration.

Permission guardsIsAuthenticated as default. Librarian/admin-only views use custom IsAdmin and IsAdminOrLibrarian permission classes. Role check happens at the view layer before any DB write.

Sensitive configSECRET_KEY, DB credentials, and all environment-specific values live in a .env file loaded by python-dotenv. The file is excluded from version control via .gitignore.

Deployment & Server

Server

VPS — Hostinger, Ubuntu 22.04 LTS

A dedicated virtual private server with full root access. No container orchestration — services run directly as systemd units.

Reverse Proxy

Nginx

Terminates TLS, routes /api/ traffic to Gunicorn via Unix socket, serves Next.js via PM2 proxy, and delivers static/media files directly from disk.

Backend Process

Gunicorn (3 workers)

Managed by systemd. Binds to a Unix socket under /run/citylibrary-backend/ with correct ownership so Nginx can communicate without TCP overhead.

Frontend Process

PM2 + Next.js standalone

PM2 keeps the Node.js process alive and restarts it on crash. next build produces an optimised standalone output served on port 3000.

SSL / TLS

Let's Encrypt — Certbot

Free certificates for citylibrary.denyslab.com and api-citylibrary.denyslab.com. Auto-renewal via cron. HTTPS enforced on all routes.

Database

PostgreSQL on the same VPS

Listens on localhost only — never exposed to the internet. Managed by the deploy user with a dedicated DB user and password stored in .env.

Infrastructure Details

Domain setupCustom domain denyslab.com with two subdomains: citylibrary.denyslab.com (frontend) and api-citylibrary.denyslab.com (backend). DNS A records point both to the same VPS IP.

Traffic flowBrowser → Nginx (443 TLS) → PM2/Next.js for page requests. Browser → Nginx (443 TLS) → Gunicorn Unix socket → Django for API requests. Media files served directly by Nginx from /media/.

Media storageUploaded book covers and avatars saved to MEDIA_ROOT on disk. Nginx serves them under /media/ with the alias directive. The deploy user owns the directory; www-data has read access.

Process managementGunicorn is a systemd service (citylibrary-backend) that starts on boot, restarts on failure, and loads environment variables from .env via EnvironmentFile. PM2 handles the Node.js side with pm2 startup.

Deployment workflowCode is pushed to GitHub. On the server: git pull, pip install (backend) or npm install + npm run build (frontend), followed by systemctl restart or pm2 restart. No downtime tooling yet — CI/CD pipeline planned.

Next.js 15
Frontend framework
Django 5
Backend framework
PostgreSQL
Database
Nginx + PM2
Server layer