Book Social Network (BSN) is a platform where book lovers can share, borrow, and discuss books within a community. Users can register their personal book collections, share them with others, and manage a complete borrow-return lifecycle with owner approval.
Getting Started | API Documentation | Architecture | Contributing
- About The Project
- Key Features
- Architecture
- Tech Stack
- Prerequisites
- Getting Started
- Environment Variables
- Default Accounts
- API Documentation
- Role & Permission System
- Project Structure
- Database Schema
- Contributing
- License
Book Social Network addresses a simple problem: sharing books should be easy. Instead of buying books that sit on shelves, this platform connects readers who want to share their collections.
Register -> Verify via Email -> Login -> Share Your Books -> Others Borrow -> Return & Review
- A user registers and receives an activation email (via MailDev in development)
- After activation, they login and receive JWT tokens
- They can create book listings with details and cover images
- Other users can borrow available (shareable, non-archived) books
- The borrower returns the book, and the owner approves the return
- Borrowers can leave feedback and ratings on books they've read
- Secure registration with email-based account activation (6-digit OTP)
- JWT-based authentication with Access Token (24h) and Refresh Token (7d)
- Role-based access control (RBAC) with
ADMINandUSERroles - Stateless session management — no server-side sessions
- Automatic token refresh via HTTP interceptor on the frontend
- Create, read, update, and delete book listings
- Upload book cover images (stored locally, up to 50MB)
- Toggle shareable status (allow others to borrow)
- Toggle archived status (hide from listings)
- Ownership enforcement — only the owner can modify their books
- Full borrow-return lifecycle with owner approval
- Prevents duplicate borrows and self-borrowing
- Tracks borrow status: borrowed -> returned -> return approved
- Separate views for borrowed books and returned books
- Rate books on a 0-5 scale with comments
- Own-feedback detection — users see their own feedback flagged
- Paginated feedback listings per book
- View all registered users
- Lock/Unlock user accounts
- Enable/Disable user accounts
- Role-based dashboard routing (ADMIN sees user management, USER sees books)
The system follows a 3-tier architecture with clear separation between presentation, business logic, and data access layers.
┌─────────────────────────────────────────────────────────────────────────┐
│ PRESENTATION TIER │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Angular Frontend (:4200) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Login │ │ Register│ │ Book │ │ Admin Panel │ │ │
│ │ │ Page │ │ Page │ │ List │ │ (USER Mgmt) │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ └──────┬───────┘ │ │
│ │ │ │ │ │ │ │
│ │ ┌────▼──────────────▼──────────────▼────────────────▼───────┐ │ │
│ │ │ HTTP Token Interceptor │ │ │
│ │ │ (Injects Bearer token to every request) │ │ │
│ │ └──────────────────────────┬────────────────────────────────┘ │ │
│ └──────────────────────────────┼──────────────────────────────────┘ │
│ │ │
└──────────────────────────────────┼──────────────────────────────────────┘
│ HTTP/REST (JSON)
│ Authorization: Bearer <jwt>
┌──────────────────────────────────┼──────────────────────────────────────┐
│ APPLICATION TIER │
│ │ │
│ ┌──────────────────────────────▼──────────────────────────────────┐ │
│ │ Spring Boot API (:8088/api/v1) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ SECURITY LAYER │ │ │
│ │ │ │ │ │
│ │ │ ┌────────────┐ ┌──────────────┐ ┌───────────────┐ │ │ │
│ │ │ │ CorsFilter │─>│ JwtFilter │─>│ Authentication│ │ │ │
│ │ │ │ (CORS) │ │ (validate & │ │ Provider │ │ │ │
│ │ │ │ │ │ set auth) │ │ (verify user) │ │ │ │
│ │ │ └────────────┘ └──────────────┘ └───────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────────────▼──────────────────────────────┐ │ │
│ │ │ CONTROLLER LAYER │ │ │
│ │ │ │ │ │
│ │ │ ┌───────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ Authentication│ │ Book │ │ Feedback │ │ │ │
│ │ │ │ Controller │ │ Controller │ │ Controller │ │ │ │
│ │ │ │ /auth/* │ │ /books/* │ │ /feedbacks │ │ │ │
│ │ │ └───────┬───────┘ └──────┬───────┘ └──────┬──────┘ │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ ┌───────┴──────────────────┴──────────────────┴──────┐ │ │ │
│ │ │ │ Validation (@Valid) │ │ │ │
│ │ │ │ Request DTO ──> Validate ──> Forward to Service │ │ │ │
│ │ │ └────────────────────────┬───────────────────────────┘ │ │ │
│ │ └────────────────────────────┼──────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌────────────────────────────▼──────────────────────────────┐ │ │
│ │ │ SERVICE LAYER │ │ │
│ │ │ (Interface + Impl) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
│ │ │ │ AuthService │ │ BookService │ │ EmailService │ │ │ │
│ │ │ │ Impl │ │ Impl │ │ Impl │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ • register │ │ • CRUD │ │ • sendEmail │ │ │ │
│ │ │ │ • login │ │ • borrow │ │ • templates │ │ │ │
│ │ │ │ • activate │ │ • return │ │ │ │ │ │
│ │ │ │ • refresh │ │ • approve │ │ │ │ │ │
│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │
│ │ │ │UserService │ │FeedbackSvc │ │FileStorage │ │ │ │
│ │ │ │ Impl │ │ Impl │ │ Impl │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ • list users │ │ • save │ │ • save file │ │ │ │
│ │ │ │ • lock/unlock│ │ • list │ │ • read file │ │ │ │
│ │ │ │ • profile │ │ │ │ │ │ │ │
│ │ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────────────▼───────────────────────────────┐ │ │
│ │ │ REPOSITORY LAYER │ │ │
│ │ │ (Spring Data JPA + Hibernate) │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ ┌────────┐ ┌──────────────┐ │ │ │
│ │ │ │ BookRepo │ │ UserRepo │ │TokenRepo│ │FeedbackRepo │ │ │ │
│ │ │ └──────────┘ └──────────┘ └────────┘ └──────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌──────────┐ ┌──────────┐ │ │ │
│ │ │ │ RoleRepo │ │ HistoryRepo│ │ │ │
│ │ │ └──────────┘ └──────────┘ │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────┬───────────────────────────────────────┘
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌──────────────────────┐ ┌─────────────┐ ┌──────────────────┐
│ PostgreSQL (:5432) │ │ MailDev │ │ Local File │
│ │ │ │ │ System │
│ ┌────────────────┐ │ │ SMTP :1025 │ │ │
│ │ book_social_ │ │ │ Web :1080 │ │ book-network/ │
│ │ network │ │ │ │ │ uploads/ │
│ │ │ │ │ Captures │ │ users/{id}/ │
│ │ Tables: │ │ │ all emails │ │ {cover}.png │
│ │ • _user │ │ │ during dev │ │ │
│ │ • role │ │ │ │ │ │
│ │ • user_roles │ │ └─────────────┘ └──────────────────┘
│ │ • book │ │
│ │ • feedback │ │
│ │ • token │ │
│ │ • book_trans.. │ │
│ └────────────────┘ │
└──────────────────────┘
This diagram shows exactly what happens from the moment a user clicks a button to the final response.
USER ACTION BACKEND PROCESSING
─────────── ──────────────────
┌──────────┐
│ User │ Click "Borrow Book"
│ Browser │─────────────────────────────────────────────────────────┐
└──────────┘ │
▼
┌─────────────────┐
│ Angular HTTP │
│ Interceptor │
│ Adds: │
│ Authorization: │
│ Bearer <jwt> │
└────────┬────────┘
│
══════════════════════════════════════════════════════
SPRING BOOT API
══════════════════════════════════════════════════════
│
▼
┌─────────────────┐
│ CorsFilter │
│ Validates │
│ Origin header │
└────────┬────────┘
│
▼
┌─────────────────┐
│ JwtFilter │
│ │
│ 1. Extract JWT │
│ from header │
│ 2. Parse email │
│ 3. Load User │
│ 4. Check token │
│ in DB │
│ 5. Set Security │
│ Context │
└────────┬────────┘
│
┌──────────────┴──────────────┐
│ Token valid? │
│ │
┌─────▼─────┐ ┌──────▼──────┐
│ YES │ │ NO │
│ Continue │ │ 401 UNAUTH │
└─────┬─────┘ └─────────────┘
│
▼
┌─────────────────┐
│ BookController │
│ │
│ @PreAuthorize │
│ hasAnyAuthority │
│ (USER,ADMIN) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ BookServiceImpl│
│ │
│ 1. Get User │
│ from Auth │
│ 2. Find Book │
│ by ID │
│ 3. Check: │
│ • not archived│
│ • shareable │
│ • not own book│
│ • not borrowed│
│ 4. Create │
│ Transaction │
└────────┬────────┘
│
▼
┌─────────────────┐
│ BookTransaction │
│ HistoryRepository│
│ │
│ INSERT INTO │
│ book_transaction│
│ _history (...) │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Return HTTP │
│ 200 OK │
│ Body: {id: 42} │
└────────┬────────┘
│
══════════════════════════════════════════════════════
│
▼
┌─────────────────┐
│ Angular HTTP │
│ Interceptor │
│ Stores new JWT │
│ if refreshed │
└────────┬────────┘
│
▼
┌─────────────────┐
│ UI Updates: │
│ "Book borrowed │
│ successfully" │
└─────────────────┘
This diagram shows the complete authentication lifecycle including registration, login, token refresh, and protected requests.
REGISTRATION FLOW LOGIN FLOW
───────────────── ──────────
POST /auth/register POST /auth/authenticate
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Create User │ │ AuthManager │
│ (enabled=F) │ │ .authenticate│
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Generate 6 │ │ Validates │
│ digit OTP │ │ email+pass │
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Save Token │ │ Revoke old │
│ in DB │ │ tokens │
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Send Email │ │ Generate │
│ via Thymeleaf│ │ JWT + Refresh│
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Return 202 │ │ Save tokens │
│ Accepted │ │ in DB │
└─────────────┘ └──────┬───────┘
│
▼
┌──────────────┐
│ Return 200 │
│ {accessToken,│
│ refreshToken,│
│ roles} │
└──────────────┘
TOKEN REFRESH FLOW PROTECTED REQUEST
───────────────── ─────────────────
POST /auth/refresh-token GET /books/owner
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Extract │ │ JwtFilter │
│ refresh JWT │ │ Extract + │
│ from header │ │ Validate JWT │
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Lookup in │ │ Check token │
│ token table │ │ not expired │
│ (not expired│ │ & not revoked│
│ & revoked) │ └──────┬───────┘
└──────┬──────┘ │
│ ▼
▼ ┌──────────────┐
┌─────────────┐ │ @PreAuthorize│
│ Revoke all │ │ Check role │
│ old tokens │ │ (USER/ADMIN) │
└──────┬──────┘ └──────┬───────┘
│ │
▼ ▼
┌─────────────┐ ┌──────────────┐
│ Generate new│ │ Controller │
│ access + │ │ -> Service │
│ refresh JWT │ │ -> Repository│
└──────┬──────┘ │ -> Database │
│ └──────┬───────┘
▼ │
┌─────────────┐ ▼
│ Return new │ ┌──────────────┐
│ tokens │ │ Return 200 │
└─────────────┘ │ + Response │
└──────────────┘
Each layer has a single responsibility and only communicates with the layer directly below it.
| Layer | Package | Responsibility | Knows About |
|---|---|---|---|
| Controller | controller/ |
HTTP routing, request validation, response formatting | Service interfaces |
| Service | service/ + service/impl/ |
Business logic, transaction management, authorization rules | Repository, other Services |
| Repository | repository/ |
Database queries, CRUD operations, JPA specifications | Entity classes |
| Entity | entity/ |
Database table mappings, relationships, computed fields | Only JPA annotations |
| DTO | dto/request/ + dto/response/ |
Data transfer between layers, validation rules | Nothing (pure data) |
| Security | security/ |
JWT generation/parsing, authentication filter, user lookup | Token repository, User entity |
| Common | common/ |
Shared utilities, base classes, constants | Nothing (pure utilities) |
| Decision | Rationale |
|---|---|
| Interface + Impl for Services | Enables mocking in unit tests, decouples controller from implementation, allows swapping implementations |
| DTO separation from Entity | Prevents exposing database internals, allows different shapes for API input/output |
| JWT in database (token table) | Enables token revocation, prevents reuse of expired tokens, supports logout functionality |
| Stateless sessions | No server-side session storage, enables horizontal scaling, each request is self-contained |
| Ownership enforcement in Service | Business rules (who can edit what) live in service layer, not in controller or database |
| Layer-based package structure | Standard Spring Boot convention, easy for new developers, clear separation of concerns |
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Language | Java | 17+ | Core runtime |
| Framework | Spring Boot | 3.3.1 | Application framework |
| Security | Spring Security | 6.x | Authentication & authorization |
| JWT | jjwt | 0.11.5 | Token generation & validation |
| ORM | Spring Data JPA / Hibernate | 6.5.2 | Database access |
| Database | PostgreSQL | 14+ | Primary data store |
| Spring Mail + Thymeleaf | - | Activation email templates | |
| Validation | Jakarta Bean Validation | - | Request validation |
| API Docs | springdoc-openapi | 2.6.0 | Swagger UI |
| Build | Maven | 3.9+ | Dependency management |
| Code Gen | Lombok | - | Boilerplate reduction |
| Component | Technology | Version | Purpose |
|---|---|---|---|
| Framework | Angular | 16.1.x | SPA framework |
| CSS | Bootstrap | 5.3.3 | UI components |
| Icons | Font Awesome | 6.6.0 | Icon library |
| JWT | @auth0/angular-jwt | 5.2.0 | Client-side JWT handling |
| OTP Input | angular-code-input | 2.0.0 | Activation code input |
| API Gen | ng-openapi-gen | 0.51.0 | OpenAPI client generation |
| Language | TypeScript | 5.1.3 | Type-safe JavaScript |
| Build | Angular CLI | 16.1.4 | Build tooling |
| Component | Technology | Purpose |
|---|---|---|
| Containerization | Docker Compose | Service orchestration |
| Database | PostgreSQL 14 | Persistent data storage |
| Email Server | MailDev | Development email capture |
Before running this project, make sure you have the following installed:
| Software | Minimum Version | Check Command | Download |
|---|---|---|---|
| Java (JDK) | 17+ | java -version |
Oracle / Adoptium |
| Node.js | 18+ | node -v |
nodejs.org |
| npm | 9+ | npm -v |
Comes with Node.js |
| Docker | 20.10+ | docker -v |
docker.com |
| Docker Compose | 2.0+ | docker compose version |
Comes with Docker Desktop |
| Git | 2.0+ | git --version |
git-scm.com |
git clone https://github.com/hendrowunga/SpringBoot-Book-Social-Networking.git
cd SpringBoot-Book-Social-Networkingdocker compose up -dThis starts:
- PostgreSQL on port
5432 - MailDev Web UI on port
1080, SMTP on port1025
Verify they're running:
docker compose pscp .env.example .envEdit .env with your own values if needed. See Environment Variables for details.
Option A: Using the run script (recommended)
chmod +x run.sh
./run.shOption B: Manual
export $(cat .env | grep -v '^#' | xargs)
cd book-network
./mvnw spring-boot:runOption C: Using IntelliJ IDEA
- Import the
book-networkfolder as a Maven project - Set Environment Variables in Run Configuration (copy from
.env) - Run
BookNetworkApiApplication.java
The API will start at http://localhost:8088/api/v1
cd book-network-frontend
npm install
ng serveThe frontend will start at http://localhost:4200
| Service | URL | Expected |
|---|---|---|
| Backend API | http://localhost:8088/api/v1 | Application starts |
| Swagger UI | http://localhost:8088/api/v1/swagger-ui/index.html | API docs visible |
| Frontend | http://localhost:4200 | Login page visible |
| MailDev | http://localhost:1080 | Email inbox visible |
| pgAdmin / DBeaver | localhost:5432 | Database accessible |
All configuration is managed through environment variables in the .env file. Never commit .env to version control.
| Variable | Default Value | Description |
|---|---|---|
DB_URL |
jdbc:postgresql://localhost:5432/book_social_network |
PostgreSQL JDBC connection URL |
DB_USERNAME |
username |
PostgreSQL database username |
DB_PASSWORD |
password |
PostgreSQL database password |
JWT_SECRET_KEY |
404E63... |
Secret key for signing JWT tokens (Base64 encoded) |
JWT_EXPIRATION |
86400000 |
Access token expiration in milliseconds (24 hours) |
JWT_REFRESH_EXPIRATION |
604800000 |
Refresh token expiration in milliseconds (7 days) |
MAIL_HOST |
localhost |
SMTP server hostname |
MAIL_PORT |
1025 |
SMTP server port |
MAIL_USERNAME |
endos |
SMTP authentication username |
MAIL_PASSWORD |
endos |
SMTP authentication password |
ACTIVATION_URL |
http://localhost:4200/activate-account |
Frontend activation URL sent in emails |
Production Note: For production deployment, generate a new
JWT_SECRET_KEYusing:openssl rand -base64 512
These accounts are pre-configured for testing:
| Password | Role | Status | Purpose | |
|---|---|---|---|---|
john.owner@test.com |
password123 |
ADMIN + USER | Active | Admin panel + book management |
jane.borrower@test.com |
password123 |
USER | Active | Borrow/return workflow |
hendrowunga@test.com |
password123 |
USER | Active | General testing |
There is no admin registration endpoint (by design). To make a user admin:
-- Connect to PostgreSQL
psql -U username -d book_social_network
-- Find the user ID
SELECT id, email FROM _user WHERE email = 'newuser@test.com';
-- Get ADMIN role ID
SELECT id FROM role WHERE name = 'ADMIN';
-- Assign ADMIN role
INSERT INTO user_roles (user_id, role_id) VALUES (<user_id>, <role_id>);All endpoints are prefixed with /api/v1. Interactive documentation is available at:
Swagger UI: http://localhost:8088/api/v1/swagger-ui/index.html
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
POST |
/auth/register |
Register a new user (sends activation email) | No |
POST |
/auth/authenticate |
Login and receive JWT tokens | No |
GET |
/auth/activate-account?token={code} |
Activate account with 6-digit code | No |
POST |
/auth/refresh-token |
Get new access token using refresh token | Refresh Token |
Request/Response Examples
POST /auth/register
// Request
{
"firstname": "John",
"lastname": "Doe",
"email": "john@example.com",
"password": "securePassword123"
}
// Response: 202 Accepted (empty body)POST /auth/authenticate
// Request
{
"email": "john@example.com",
"password": "securePassword123"
}
// Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzM4NCJ9...",
"refreshToken": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"roles": ["USER"]
}POST /auth/refresh-token
# Header: Authorization: Bearer <refresh_token>
// Response: 200 OK
{
"accessToken": "eyJhbGciOiJIUzM4NCJ9...",
"refreshToken": "new-refresh-token-uuid",
"roles": ["USER"]
}| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
POST |
/books |
Create a new book listing | Yes |
GET |
/books |
Get all shareable books (paginated) | Yes |
GET |
/books/{book-id} |
Get book details by ID | Yes |
GET |
/books/owner |
Get current user's books (paginated) | Yes |
GET |
/books/borrowed |
Get books borrowed by current user | Yes |
GET |
/books/returned |
Get books returned by current user | Yes |
PATCH |
/books/shareable/{book-id} |
Toggle shareable status | Yes (Owner) |
PATCH |
/books/archived/{book-id} |
Toggle archived status | Yes (Owner) |
POST |
/books/borrow/{book-id} |
Borrow a book | Yes |
PATCH |
/books/borrow/return/{book-id} |
Return a borrowed book | Yes (Borrower) |
PATCH |
/books/borrow/return/approve/{book-id} |
Approve book return | Yes (Owner) |
POST |
/books/cover/{book-id} |
Upload book cover image | Yes (Owner) |
Request/Response Examples
POST /books
// Request
{
"title": "Effective Java",
"authorName": "Joshua Bloch",
"isbn": "978-0134685991",
"synopsis": "A must-read for every Java programmer.",
"shareable": true
}
// Response: 200 OK
42GET /books?page=0&size=5
// Response: 200 OK
{
"content": [
{
"id": 1,
"title": "The Great Gatsby",
"authorName": "F. Scott Fitzgerald",
"isbn": "978-0743273565",
"synopsis": "A story of the mysteriously wealthy Jay Gatsby...",
"owner": "John Doe",
"cover": "base64-encoded-image...",
"rate": 4.5,
"archived": false,
"shareable": true
}
],
"number": 0,
"size": 5,
"totalElements": 12,
"totalPages": 3,
"first": true,
"last": false
}POST /books/cover/{book-id}
# Content-Type: multipart/form-data
# Body: file=<binary image data>
// Response: 202 Accepted| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
POST |
/feedbacks |
Submit feedback for a book | Yes |
GET |
/feedbacks/book/{book-id} |
Get all feedback for a book (paginated) | Yes |
Request/Response Examples
POST /feedbacks
// Request
{
"note": 4.5,
"comment": "Amazing book! Great story about the American dream.",
"bookId": 1
}
// Response: 200 OK
1GET /feedbacks/book/1?page=0&size=5
// Response: 200 OK
{
"content": [
{
"note": 4.5,
"comment": "Amazing book! Great story about the American dream.",
"ownFeedback": true
}
],
"number": 0,
"size": 5,
"totalElements": 1,
"totalPages": 1,
"first": true,
"last": true
}| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
GET |
/users |
Get all users (paginated) | ADMIN |
GET |
/users/{user-id} |
Get user details by ID | ADMIN |
PATCH |
/users/{user-id}/lock |
Toggle user lock status | ADMIN |
PATCH |
/users/{user-id}/enable |
Toggle user enabled status | ADMIN |
GET |
/users/profile |
Get own profile | Any User |
- Register a new account via
POST /auth/register - Check MailDev at http://localhost:1080 for the activation email
- Activate via
GET /auth/activate-account?token={6-digit-code} - Login via
POST /auth/authenticateto get JWT tokens - Use the Access Token as
Authorization: Bearer {accessToken}header - Test protected endpoints via Swagger UI or your preferred HTTP client
| Role | Description | How to Assign |
|---|---|---|
USER |
Default role for all registered users | Automatically assigned on registration |
ADMIN |
Platform administrator | Manually assigned via database |
| Action | USER | ADMIN |
|---|---|---|
| Register | Yes | - |
| Login | Yes | Yes |
| Create books | Yes (own) | Yes (own) |
| Edit books | Yes (own) | Yes (own) |
| Delete/archive books | Yes (own) | Yes (own) |
| Toggle shareable | Yes (own) | Yes (own) |
| Upload book cover | Yes (own) | Yes (own) |
| Browse all books | Yes | Yes |
| Borrow books | Yes | Yes |
| Return books | Yes | Yes |
| Approve returns | Yes (owner) | Yes (owner) |
| Give feedback | Yes | Yes |
| View all users | No | Yes |
| Lock/Unlock users | No | Yes |
| Enable/Disable users | No | Yes |
The system enforces strict ownership rules:
- Users can only edit, archive, toggle shareable, and upload covers for books they own
- Users can only approve returns for books they own
- Users cannot borrow their own books
- ADMIN cannot modify other users' books — admin manages users, not content
This design follows the principle: "Admin is a platform manager, not a content censor."
The backend follows a layer-based package structure with Interface + Impl pattern for the service layer, making the codebase clean, consistent, and easy to maintain.
book-network/src/main/java/com/endos/book/
│
├── BookNetworkApiApplication.java # Entry point + role initialization
│
├── controller/ # REST API Endpoints
│ ├── AuthenticationController.java # POST /auth/register, /auth/authenticate
│ ├── BookController.java # CRUD /books, borrow, return
│ ├── FeedbackController.java # CRUD /feedbacks
│ └── UserController.java # Admin user management
│
├── dto/ # Data Transfer Objects
│ ├── request/ # Incoming request models
│ │ ├── AuthenticationRequest.java
│ │ ├── RegistrationRequest.java
│ │ ├── BookRequest.java
│ │ └── FeedbackRequest.java
│ └── response/ # Outgoing response models
│ ├── AuthenticationResponse.java
│ ├── BookResponse.java
│ ├── BorrowedBookResponse.java
│ ├── FeedbackResponse.java
│ └── UserResponse.java
│
├── entity/ # JPA Entity / Database Table Mappings
│ ├── BaseEntity.java # Abstract base (id, createdDate, auditing)
│ ├── Book.java # book table
│ ├── BookTransactionHistory.java # book_transaction_history table
│ ├── Feedback.java # feedback table
│ ├── Role.java # role table (USER, ADMIN)
│ ├── Token.java # token table (JWT storage)
│ └── User.java # _user table (implements UserDetails)
│
├── repository/ # Spring Data JPA Repositories
│ ├── BookRepository.java # Book queries + JpaSpecificationExecutor
│ ├── BookTransactionHistoryRepository.java # Borrow/return queries
│ ├── FeedbackRepository.java # Feedback queries
│ ├── RoleRepository.java # Role lookups
│ ├── TokenRepository.java # JWT token queries
│ └── UserRepository.java # User lookups
│
├── service/ # Business Logic (Interface + Impl)
│ ├── AuthService.java # Interface
│ ├── BookService.java # Interface
│ ├── EmailService.java # Interface
│ ├── FeedbackService.java # Interface
│ ├── FileStorageService.java # Interface
│ ├── UserService.java # Interface
│ ├── BookMapper.java # Book entity <-> DTO mapper
│ ├── FeedbackMapper.java # Feedback entity <-> DTO mapper
│ └── impl/ # Implementations
│ ├── AuthServiceImpl.java # Register, login, activate, refresh
│ ├── BookServiceImpl.java # Book CRUD, borrow/return logic
│ ├── EmailServiceImpl.java # Thymeleaf email sending
│ ├── FeedbackServiceImpl.java # Feedback CRUD
│ ├── FileStorageServiceImpl.java # Local file storage
│ └── UserServiceImpl.java # Admin user management
│
├── security/ # JWT & Security
│ ├── SecurityConfig.java # CORS, filter chain, stateless sessions
│ ├── JwtFilter.java # Token validation filter
│ ├── JwtService.java # Token generation & parsing
│ └── UserDetailsServiceImpl.java # User authentication lookup
│
├── config/ # Application Configuration
│ ├── BeansConfig.java # PasswordEncoder, AuthenticationManager, CORS
│ ├── ApplicationAuditAware.java # JPA auditing (createdBy/modifiedBy)
│ └── OpenApiConfig.java # Swagger UI / OpenAPI 3 config
│
├── exception/ # Error Handling
│ ├── GlobalExceptionHandler.java # @RestControllerAdvice
│ ├── BusinessErrorCodes.java # Custom error codes (300-306)
│ ├── ExceptionResponse.java # Error response DTO
│ └── OperationNotPermittedException.java # Ownership violation exception
│
└── common/ # Shared Utilities
├── PageResponse.java # Generic paginated response wrapper
├── BookSpecification.java # JPA Specification for book queries
├── EmailTemplateName.java # Email template enum
└── FileUtils.java # File read utility
controller → service (interface) → service.impl (implementation)
↓
repository → entity
↓
common/dto
book-network-frontend/src/app/
├── app.module.ts
├── app-routing.module.ts
│
├── pages/ # Public pages
│ ├── login/
│ ├── register/
│ └── activate-account/
│
├── modules/book/ # Book module (lazy-loaded)
│ ├── book.module.ts
│ ├── book-routing.module.ts
│ │
│ ├── pages/
│ │ ├── main/
│ │ ├── book-list/
│ │ ├── book-details/
│ │ ├── my-books/
│ │ ├── manage-book/
│ │ ├── borrowed-book-list/
│ │ ├── return-books/
│ │ └── manage-users/ # Admin panel
│ │
│ └── components/
│ ├── menu/ # Role-aware navbar
│ ├── book-card/
│ └── rating/
│
└── services/
├── token/token.service.ts # JWT + role management
├── interceptor/http-token.interceptor.ts
├── guard/auth.guard.ts
├── models/ # TypeScript interfaces
├── services/ # API services
│ ├── authentication.service.ts
│ ├── book.service.ts
│ ├── feedback.service.ts
│ └── user.service.ts
└── fn/ # Generated API functions
┌──────────────┐ ┌──────────────────┐ ┌──────────────┐
│ role │ │ user_roles │ │ _user │
├──────────────┤ ├──────────────────┤ ├──────────────┤
│ id (PK) │◄──────│ role_id (FK) │ │ id (PK) │
│ name (UNIQUE)│ │ user_id (FK) │──────►│ email (UNIQUE)│
│ created_date │ └──────────────────┘ │ firstname │
│ last_mod_date│ │ lastname │
└──────────────┘ │ password │
│ enabled │
│ account_locked│
│ created_date │
└──────┬───────┘
│
┌────────────────────────────────┤
│ │
┌─────────v──────────┐ ┌──────────v──────────┐
│ book │ │ book_transaction_ │
├────────────────────┤ │ history │
│ id (PK) │◄─────────├─────────────────────┤
│ title │ │ id (PK) │
│ author_name │ │ book_id (FK) │
│ isbn │ │ user_id (FK) │
│ synopsis │ │ returned │
│ owner_id (FK) │──┐ │ return_approved │
│ book_cover │ │ │ created_date │
│ archived │ │ └─────────────────────┘
│ shareable │ │
│ rate │ │ ┌─────────────────────┐
│ created_date │ │ │ feedback │
└────────────────────┘ │ ├─────────────────────┤
│ │ id (PK) │
└───────│ book_id (FK) │
│ user_id (FK) │
│ note (0-5) │
│ comment │
│ created_date │
└─────────────────────┘
| Table | Records | Purpose |
|---|---|---|
_user |
User accounts | Stores all registered users |
role |
USER, ADMIN | Role definitions |
user_roles |
M:N junction | Maps users to their roles |
token |
JWT tokens | Stores access & refresh tokens |
book |
Book listings | All books created by users |
book_transaction_history |
Borrow records | Tracks borrow/return lifecycle |
feedback |
Ratings & comments | User feedback on books |
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project follows Conventional Commits:
| Prefix | Description |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
docs: |
Documentation changes |
style: |
Code style changes (formatting, etc.) |
refactor: |
Code refactoring |
test: |
Adding or updating tests |
chore: |
Build process or tooling changes |
This project is licensed under the MIT License. See the LICENSE file for details.
Built with Spring Boot + Angular + PostgreSQL
Book Social Network - Share knowledge, one book at a time.