Skip to content

Repository files navigation

BookShare

Java Spring Boot Gradle PostgreSQL Docker Build Coverage Gate License: GPL v2 Status

BookShare is a social reading platform where users can track books, log reading progress, write reviews, organize personal shelves, and follow friends' reading activity — all in one place.


Table of Contents


Features

Books & Reading

  • Browse, search, and filter the book catalog by genre, tag, and average rating
  • Track reading progress per book (pages read and completion percentage)
  • Mark books as complete — progress is auto-finalized
  • Write and like book reviews (star ratings 0–5, rich-text content via TinyMCE)
  • Organize books into built-in shelves (Currently Reading, Want to Read, Read) and custom shelves
  • View your full collection organized by shelf
  • Reading Statistics dashboard — per-year stats: books read, pages read, average pages/day, average time-to-finish, longest/shortest/recently-finished books, a monthly completion bar chart (Chart.js), and your personal rating distribution, tracked against your annual Reading Challenge goal

Social

  • Send, accept, decline, and revoke friend requests
  • Remove existing connections
  • Browse any user's public profile: shelves, reading progress, and bio
  • Real-time activity feed showing friends' reviews, progress updates, and new connections (30-day window, paginated)

Authors

  • Any registered user can apply to become an author
  • Authors can submit book addition requests for admin review
  • Author profiles optionally link to a registered user account

Authentication & Account Management

  • Standard email/password registration and login
  • Google OAuth2 / OIDC login (auto-provisions new accounts)
  • Password reset via tokenized email link (Gmail API, 10-minute expiry)

Admin Panel

  • Full CRUD for users, books, authors, genres, and tags
  • Review and approve/reject author applications
  • Review and approve author-submitted book requests
  • System metrics dashboard: live JVM heap, CPU, thread and disk metrics, Caffeine cache statistics, health component status

REST API

  • Full JWT-authenticated REST API under /api/v1 for mobile and external clients
  • Covers books, shelves, feed, profiles, reading statistics, reading challenges, author applications, and account management
  • OpenAPI 3.0 specification served at /api-docs/openapi.yaml

General

  • Server-side rendering with Thymeleaf; AJAX-powered fragments for feeds, shelves, and search
  • Internationalization (i18n): English, French, German, Spanish, Bengali
  • Dark / light mode toggle
  • Async activity event system with transactional outbox pattern
  • Structured JSON (ECS) logging to rolling files (human-readable console)

Architecture Overview

BookShare is a monolithic Spring Boot application with a server-rendered Thymeleaf frontend and a parallel stateless REST API. AJAX is used selectively in the MVC layer to update page fragments without full reloads (book grid, activity feed, shelf tabs, profile connection state).

Browser (Thymeleaf + Bootstrap)     Mobile / API clients
        │                                   │
        ▼                                   ▼
Spring MVC Controllers          REST API Controllers (/api/v1/**)
        │                        JWT filter + stateless auth
        └───────────┬────────────────────────┘
                    ▼
        Service Layer (business logic, caching, event publishing)
                    │
           ┌────────┴────────────┐
           │                     │
     JPA/Hibernate     Activity Outbox (transactional writes)
           │                     │
      PostgreSQL        Scheduled Processor (every 15 s)
                                 │
                      Activity + FeedEntry tables

Activity & Feed pipeline:

  1. User actions (review, progress update, friend connection) write to activity_outbox within the same transaction.
  2. ActivityOutboxProcessor (runs every 15 seconds) picks up PENDING outbox rows, persists Activity records, and fans out FeedEntry rows to the actor's connections.
  3. Feed reads are served from the feed Caffeine cache (60-second TTL).

Caching (Caffeine):

Cache TTL Notes
books / book-lists 12 h / 30 min Individual books and paginated list results
authors / author-lists 12 h / 30 min
genres / genre-lists 24 h / 24 h
tags / tag-lists 24 h / 24 h
shelves / shelf-lists 10 min / 5 min Short TTL; invalidated on shelf writes
logins 30 min Keyed by email and handle
feed 60 s Per-user, refreshed frequently

Cache statistics are visible on the admin Actuator dashboard.


Technology Stack

Layer Technology
Backend Java 25 (virtual threads), Spring Boot 3.5.5, Spring MVC, Spring Data JPA / Hibernate
Security Spring Security, Spring Security OAuth2 Client (Google OIDC), JJWT 0.13 (JWT for REST API)
Frontend Thymeleaf + Layout Dialect, Bootstrap 5, jQuery 3.7.1, FontAwesome
Rich UI TinyMCE (description editor), FilePond (image upload), Select2 (multi-select), DataTables, Chart.js (reading-stats charts)
Database PostgreSQL 17, Flyway (migrations V1–V21)
Caching Caffeine (managed via Spring Cache abstraction)
Email Spring Mail + Gmail API (Google OAuth2 UserCredentials)
Observability Spring Boot Actuator, Micrometer, structured JSON (ECS) logging
Build & QA Gradle (version catalog), Lombok, SpotBugs (MAX effort, build-failing), JaCoCo (enforced 90% line / 80% branch gate)
Testing JUnit 5, AssertJ, Mockito, Testcontainers (PostgreSQL 17)
Infrastructure Docker (multi-stage build, eclipse-temurin:25-jre-alpine), Docker Compose

Screenshots

Homepage — recently-read shelf, reading progress, friends' activity feed, and featured books

Homepage

Book Catalog — browse and filter books by name, rating, genre, and tag

Book list

Profile — shelves, reading progress, connections, and activity for a user

Profile

Collection — personal shelf view with per-book reading progress

Collection

Reading Statistics — per-year dashboard: books/pages read, average pace and time-to-finish, monthly completion chart, rating distribution, and Reading Challenge progress

Reading statistics dashboard


Getting Started

Prerequisites

  • Java 25+
  • Gradle 8+
  • PostgreSQL (or Docker — no local DB needed when using Compose)
  • Git

Local Setup

# Clone the repository
git clone https://github.com/smzoha/bookshare.git
cd bookshare

Database connection defaults to localhost:5432/bookshare. Override in src/main/resources/application-dev.properties:

spring.datasource.url=jdbc:postgresql://localhost:5432/bookshare
spring.datasource.username=your_db_user
spring.datasource.password=your_db_password

Start the application:

./gradlew bootRun

Access at: http://localhost:6001

Flyway runs all migrations automatically on startup. No manual schema setup is required.

Running Tests

./gradlew test

Repository-layer tests use Testcontainers to spin up a real PostgreSQL 17 instance — Docker must be running. All other tests (service, controller, helper, filter, validator, utility) use Mockito and run without any infrastructure.

The build enforces quality gates via ./gradlew check: JaCoCo coverage verification (minimum 90% line / 80% branch on business logic — config, entities, and DTOs are excluded) and SpotBugs static analysis (MAX effort; the build fails on any finding). The suite spans repository, service, controller, helper, filter, validator, and utility layers and sits comfortably above the enforced thresholds. Both gates run in CI on every push and pull request.

To run a specific test class:

./gradlew test --tests "com.zedapps.bookshare.service.book.BookServiceTest"

Running with Docker

Docker Compose starts both the application and a PostgreSQL 17 instance. No local database required.

# 1. Create your environment file
cp .env.example .env

Edit .env:

DATABASE_NAME=bookshare
DATABASE_USER=your_db_user
DATABASE_PASSWORD=your_db_password

# REST API — JWT signing key (Base64-encoded) and token expiry
APP_JWT_SECRET=your_base64_encoded_secret
APP_JWT_EXPIRY_MS=1800000
# 2. Start all services
./deploy.sh start

# 3. Stop all services
./deploy.sh stop
  • App port: 6001 (host)
  • PostgreSQL port: 5433 (host, mapped to 5432 inside the container)
  • Logs are written to ./logs/ on the host

Seed Data

The project ships with sample data covering 10 authors, 10 books, 8 tags, and 5 genres. This script is not run automatically by Flyway — load it manually into your database after first startup:

psql -U your_db_user -d bookshare -f src/main/resources/seed/seed_data.sql

Gmail API & Google OAuth2 Setup

Two Google Cloud credentials are needed:

  • Gmail API — for sending password-reset emails
  • Google OAuth2 Login — for Sign in with Google

Step 1 — Create OAuth2 Credentials in Google Cloud Console:

  1. Go to Google Cloud Console and create or select a project.
  2. Enable the Gmail API: APIs & Services → Library → Gmail API → Enable.
  3. Configure the OAuth Consent Screen: External, fill in app name and support email, add scopes:
    • https://www.googleapis.com/auth/gmail.send
    • openid
    • https://www.googleapis.com/auth/userinfo.email
    • https://www.googleapis.com/auth/userinfo.profile
  4. Create an OAuth Client ID (Web application) with these Authorized Redirect URIs:
    • http://localhost:6001
    • http://localhost:6001/login/oauth2/code/google
    • http://localhost:6001/oauth2/authorization/google
  5. Note the client_id and client_secret.

Step 2 — Generate a Gmail Refresh Token:

Open in your browser (replace YOUR_CLIENT_ID):

https://accounts.google.com/o/oauth2/v2/auth?client_id=YOUR_CLIENT_ID&redirect_uri=http://localhost:6001&response_type=code&scope=https://www.googleapis.com/auth/gmail.send&access_type=offline&prompt=consent

After granting access, copy the code from the redirect URL. Exchange it for tokens:

POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded

code=AUTH_CODE&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&redirect_uri=http://localhost:6001&grant_type=authorization_code

Copy refresh_token from the response.

Step 3 — Add credentials to your config:

For local Spring Boot (secret-dev.properties, see secret-dev.properties.example):

app.gmail.client.id=your_client_id
app.gmail.client.secret=your_client_secret
app.gmail.refresh.token=your_refresh_token

spring.security.oauth2.client.registration.google.client-id=${app.gmail.client.id}
spring.security.oauth2.client.registration.google.client-secret=${app.gmail.client.secret}

For Docker deployment (.env):

GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
GOOGLE_REFRESH_TOKEN=your_refresh_token

The Google Login flow is handled automatically by Spring Security OAuth2. The manual token exchange above is only needed for the Gmail sending credential.

REST API

The API base URL is /api/v1. It uses stateless JWT authentication — no cookies or sessions.

Getting a token:

POST /api/v1/auth/token
Content-Type: application/json

{"email": "user@example.com", "password": "yourpassword"}

The response body contains {"token": "<JWT>"}. Include it in subsequent requests:

Authorization: Bearer <token>

Tokens expire after 30 minutes by default (APP_JWT_EXPIRY_MS).

The full OpenAPI 3.0 specification is available at /api-docs/openapi.yaml once the application is running. When running locally with the dev profile, Swagger UI is also available at /swagger-ui/index.html. It is disabled in production.


Application Roles

Role Description
USER Default role. Can browse, review, shelve books, track progress, and manage social connections. Can apply to become an author.
AUTHOR Elevated user. Can submit new book requests for admin review. Cannot re-apply for authorship.
MODERATOR Access to /manage/** routes (subset of admin).
ADMIN Full access including admin panel, user management, and Actuator dashboard.

Role assignment: new registrations and Google logins default to USER. Admins upgrade roles via the user management panel. Author applications go through an admin-approval flow.


Key Workflows

Registering and logging in: /login → register tab or Google button → redirected to / (authenticated home with feed).

Browsing and shelving a book: /book/list → filter/search → /book/{id} → add to shelf via dropdown → reading progress form.

Social connections: /profile/{handle} → Add Friend → friend receives request → accepts → both appear in each other's connections → activity events fan out to their mutual friends' feeds.

Author application: Home page → "Apply to be an Author" → request saved → admin reviews at /admin/author/request → on approval, user role upgraded to AUTHOR and an Author entity is created.

Book submission (authors): /author/bookRequest → fill form (title, ISBN, authors, genres, tags, description, cover image) → submitted with PENDING status → admin reviews at /admin/book.

Password reset: /resetPasswordRequest → enter email → tokenized link sent via Gmail → /resetPassword?token=... → set new password (token expires in 10 minutes).


Contributing

  1. Fork the repository
  2. Create a branch: feat/my-feature for features, bugfix/my-fix for bugfixes
  3. Commit your changes with a clear message
  4. Push and open a Pull Request

Guidelines:

  • Follow existing code style and package structure
  • Flyway migration files must follow the naming convention V{N}__{description}.sql and be placed in the appropriate db/migration/{month_year}/ subdirectory
  • New i18n strings belong in all five locale files under src/main/resources/locale/
  • Keep controller methods thin — business logic lives in the service layer
  • Cache eviction must be added alongside any writes that affect cached data
  • Run ./gradlew test before submitting a PR; repository-layer tests require Docker (Testcontainers) — all other tests use Mockito and run without it

License

This project is licensed under the GNU General Public License v2 (GPL-2.0). See the LICENSE file for the full text.


Contact

BookShare — Powered by ZedApps

GitHub: smzoha

Email: shamah.zoha@gmail.com

About

A webapp that helps users to track books, their reading progress, while keeping up with the updates of their connections

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages