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
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 structure — Routes grouped by layout: (main) for single-column pages, (books) for the sidebar layout. Each group has its own layout.tsx.
Authentication flow — HTTP-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 utilities — lib/permissions.ts exposes canAddBooks, canEditBooks, canDeleteBooks, canViewBookings — each returning true for admin always, or checking the librarian's permissions array.
UX details — Debounced search (400 ms), skeleton card placeholders during fetch, fade-in animations on content arrival, portal-based modals immune to stacking-context issues.
Code quality — ESLint + 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 design — All 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 security — Tokens stored in HttpOnly; Secure; SameSite=Lax cookies — inaccessible to JavaScript. Rotation on every refresh ensures a stolen refresh token is single-use.
Input validation — Every 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 guards — IsAuthenticated 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 config — SECRET_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 setup — Custom 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 flow — Browser → 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 storage — Uploaded 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 management — Gunicorn 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 workflow — Code 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.