This repository contains the current Laravel backend for the Fahrenheit reading application. In its present state, the project is a Laravel 10 API that combines authentication, book storage, Project Gutenberg scraping, reading progress tracking, streaks, notes, a personal Secret Attic, and a purchasable Secret Vault system backed by Laravel Sanctum authentication.1 2 3
The codebase is not a generic Laravel starter anymore, even though the existing README was still the default framework boilerplate. The implemented backend is clearly oriented around a gamified reading experience in which users read books, earn rank through reading activity, preserve streaks, collect books, and spend accumulated reading rank on premium or hidden content.2 4 5 6
The API surface is organized around several user-facing systems rather than a single CRUD domain. Authentication endpoints manage account access, book endpoints manage the shared book catalog, reading-progress endpoints track user activity, note endpoints support annotations, and the attic/vault endpoints add collection and economy mechanics on top of the base reading flow.2
| Domain | Purpose | Main sources |
|---|---|---|
| Authentication | User registration, login, logout, and Sanctum token access | 2 4 |
| Users and profile | Profile retrieval, profile updates, username/profile picture lookup, deletion, and reading-rank retrieval | 2 5 |
| Books | Store imported books, retrieve catalog, fetch random books, and increment ratings | 2 6 |
| Scraping and reading content | Scrape Gutenberg book metadata or page contents for in-app reading | 2 7 |
| Reading progress and streaks | Start reading, finish reading, mark daily reading, and retrieve streak/progress state | 2 8 9 |
| Notes | Create, update, list, filter, and delete user notes attached to books | 2 10 |
| Secret Attic | Personal saved-book area for unread or selected books | 2 11 |
| Secret Books and Secret Vault | Purchasable premium books using reading rank as currency | 2 12 |
The repository is a standard Laravel application using PHP 8.1+, Laravel 10.8, Sanctum, and a minimal Vite asset pipeline.1 13 The backend also includes Goutte and Symfony DomCrawler, which are central to the scraping and page-extraction behavior used against Project Gutenberg content.1 7
| Layer | Current implementation |
|---|---|
| Framework | Laravel 10 |
| Language | PHP 8.1+ |
| Auth | Laravel Sanctum personal access tokens |
| Scraping | Goutte + Symfony DomCrawler |
| Database support | Laravel DB layer with MySQL defaults in .env.example |
| Frontend tooling in repo | Vite, Axios bootstrap |
Authentication is implemented through Laravel Sanctum. The route file exposes login, register, and logout endpoints through the API auth controller, while most other endpoints are protected with the auth:sanctum middleware.2 Login validates a submitted username and password, and on success returns the authenticated user together with a newly created API token under an authorization object.4
Registration currently validates username, password, email, and an optional uploaded profile_picture image.4 During registration, the backend also creates an initial ReadingStreak record for the new user with a starting streak of 1, which shows that streak mechanics are part of the onboarding flow rather than an optional add-on.4
| Auth endpoint | Method | Access | Current behavior |
|---|---|---|---|
/api/login |
POST |
Public | Authenticate by username/password and return token |
/api/register |
POST |
Public | Create user and initialize reading streak |
/api/logout |
POST |
Public in route file, but expects authenticated user | Delete current user's tokens |
/api/user |
GET |
Authenticated | Return authenticated user |
/api/tokens/create |
POST |
Token-oriented route | Create token from current user context |
One practical implementation note is that /api/logout is placed inside the auth-controller group without explicit auth:sanctum middleware at the route level, even though the controller expects an authenticated user to exist.2 That is worth knowing when integrating clients against the current backend.
The route file documents a fairly broad application backend. Almost every non-auth action requires Sanctum authentication, reflecting the product's user-account-centric design.2
| Base path | Endpoint | Access | Purpose |
|---|---|---|---|
/api |
POST /login |
Public | Login by username |
/api |
POST /register |
Public | Register account |
/api |
POST /logout |
Controller expects auth | Logout current user |
/api |
GET /users |
Authenticated | List users |
/api |
DELETE /user |
Authenticated | Permanently delete selected users |
/api |
DELETE /user/hibernate |
Authenticated | Intended soft-delete route |
/api |
GET /user/username |
Authenticated | Get current username |
/api |
GET /user/profile-picture |
Authenticated | Get profile picture path |
/api |
PUT /user |
Authenticated | Update current user |
/api |
GET /user/readingrank |
Authenticated | Get reading rank |
/api |
GET /profile |
Authenticated | Get current user with reading streak |
/api |
POST /books |
Authenticated | Bulk store books |
/api |
GET /books |
Authenticated | List all books |
/api |
GET /books/random |
Authenticated | Fetch one random book |
/api |
GET /books/randomCollection |
Authenticated | Fetch six random books |
/api |
PUT /books/increment-rating |
Authenticated | Increase book rating |
/api |
GET /scrape-books |
Authenticated | Scraping/import-related route |
/api |
GET /secret-attic/scrape-page-contents |
Authenticated | Scrape readable page content from a saved attic book |
/api |
POST /books/browse |
Authenticated | Scrape readable page content for a specific book |
/api |
POST /readingprogress/start-reading |
Authenticated | Start reading a book |
/api |
PUT /readingprogress/finish-reading |
Authenticated | Finish reading a book |
/api |
GET /reading-progress |
Authenticated | Retrieve current user's reading progress |
/api |
POST /read-today |
Authenticated | Update daily reading streak |
/api |
GET /reading-streak |
Authenticated | Retrieve current and longest streak |
/api |
POST /notes |
Authenticated | Add note |
/api |
PUT /notes/{note} |
Authenticated | Update note |
/api |
GET /notes |
Authenticated | View notes |
/api |
GET /notes/book/{book} |
Authenticated | View notes by book |
/api |
DELETE /notes/{id} |
Authenticated | Delete note |
/api |
POST /secret-attic/add-book |
Authenticated | Add book to Secret Attic |
/api |
GET /secret-attic/books |
Authenticated | View Secret Attic books |
/api |
GET /secret-attic/book-urls |
Authenticated | View Secret Attic URLs |
/api |
GET /secret-attic/count |
Authenticated | Count Secret Attic books |
/api |
PUT /secret-attic/burn |
Authenticated | Mark Secret Attic book as received |
/api |
POST /secret-books |
Authenticated | Create secret book |
/api |
POST /secret-books/multiple |
Authenticated | Bulk create secret books |
/api |
PUT /secret-books/price |
Authenticated | Update secret book price |
/api |
POST /secret-books/purchase-book |
Authenticated | Purchase secret book with rank |
/api |
GET /secret-books |
Authenticated | List secret books |
/api |
GET /user/secret-vault/books |
Authenticated | Get purchased vault books |
/api |
DELETE /secret-books |
Authenticated | Delete all secret books |
The most distinctive part of this backend is that it treats reading as a progression system. When a user starts reading a book, the backend records a started_reading_at timestamp in the reading_progress pivot table and increases the user's reading_rank by 1.8 When the user finishes a book, the backend writes finished_reading_at and increases reading_rank by 2.8 Notes also contribute to progression, because adding a note grants additional reading rank.10
Daily engagement is handled separately through readToday(), which updates or creates a ReadingStreak, checks whether the user already read on the current date, increments the streak if yesterday was also a reading day, or resets the streak if continuity was lost.8 The resulting streak is written both to the separate streak table and back onto the user record via current_reading_streak and longest_reading_streak fields.5 8
| Reading action | Current reward or effect |
|---|---|
| Start reading | Creates/updates reading-progress row and adds +1 reading rank |
| Finish reading | Updates reading-progress row and adds +2 reading rank |
| Read today | Extends or resets daily streak depending on last reading date |
| Add note | Increases reading rank further |
| Reach rank threshold | Enables secret-book purchasing if rank is high enough |
The User model makes it clear that the application stores more than basic auth data. In addition to username, password, and email, the model persists reading_rank, current_reading_streak, and longest_reading_streak as first-class user fields.5 It also defines relationships to notes, reading streaks, reading progress, regular books, the secret attic, and the secret vault.5
| User-related capability | Current implementation |
|---|---|
| Profile retrieval | /api/profile returns authenticated user with readingStreak relation |
| Username lookup | /api/user/username returns current username |
| Profile picture lookup | /api/user/profile-picture returns stored file name/path |
| Profile update | /api/user updates username, password, email, and optional profile picture |
| Reading rank lookup | /api/user/readingrank returns current rank |
| Account deletion | Permanent delete via /api/user; intended soft delete route also exists |
Profile picture uploads are handled by moving the uploaded file into public/profile_picture during both registration and update flows.4 5 The model also uses soft deletes, which fits the existence of the hibernation-style user route, even though the route and controller method names do not perfectly match in the current code.5
The standard books domain stores Project Gutenberg book metadata such as pg_id, title, author, language, url, and ratings.6 Books can be inserted in bulk through the store() method, retrieved as a full catalog, or sampled randomly for recommendation-like behavior.6
The backend also includes a scraping controller built with Goutte and DomCrawler. In its current active form, BrowseBook() loads a specific book URL from the database and extracts the HTML contents after Gutenberg's boilerplate header, while scrapePageContents() performs a similar extraction using a random book from the authenticated user's Secret Attic.7 This indicates the repository is designed not only to store book metadata but also to serve readable book content inside the app experience.7
The Secret Attic is a personal saved-book space tied to the authenticated user.11 If a user does not yet have one, the backend creates it automatically when they first add a book.11 Books can then be attached to that attic, listed, counted, or marked as received in the pivot table, which suggests a collectible or progress-gated reading queue rather than a plain wishlist.11
The Secret Vault and Secret Books layer adds an economy mechanic on top of that system.12 Secret books can be created individually or in bulk, priced, listed, and then purchased by users using their accumulated reading_rank instead of traditional payment.12 If the user has enough rank, the backend deducts the price, creates a vault if one does not exist yet, and attaches the purchased book to that vault.12
| System | Current purpose |
|---|---|
| Secret Attic | Personal collection of saved books before or during reading |
| Secret Vault | Personal storage of purchased secret books |
| Secret Books | Purchasable premium books with mutable prices |
| Reading rank | In-app currency earned by reading actions |
The notes subsystem lets authenticated users create annotations tied to books.2 10 Creating a note requires text and book_id, and the note is saved against the authenticated user.10 The controller also supports updating existing notes, listing all notes, filtering notes by book, and deleting notes subject to policy checks.10
One important implementation detail is that viewAllNotes() returns all notes with eager-loaded user and book relations, not just the current user's notes.10 That is useful to document because it reflects the actual repository behavior rather than an assumed per-user privacy model.
The data model supports both standard reading and gamified progression. The migration set and model layout show dedicated tables for categories, books, notes, reading progress, reading streaks, secret attics, secret books, secret vaults, and the corresponding pivot tables that connect them.5 14
| Model | Role in the system |
|---|---|
User |
Authenticated reader account with rank and streak fields |
Book |
Standard book catalog entry |
ReadingProgress |
Reading timeline between users and books |
ReadingStreak |
Daily streak tracking |
Note |
User-created annotation attached to a book |
SecretAttic |
User's saved-book collection |
SecretBook |
Purchasable premium/hidden book |
SecretVault |
User-owned purchased-book vault |
This is a standard Laravel repository from a setup perspective, even though the application logic is highly customized. The example environment file uses MySQL defaults and the usual Laravel application settings, so local development follows the normal Laravel workflow.13 15
| Step | Command |
|---|---|
| Install PHP dependencies | composer install |
| Install JS dependencies | npm install |
| Copy environment file | cp .env.example .env |
| Generate app key | php artisan key:generate |
| Configure database | Edit .env with your MySQL credentials |
| Run migrations | php artisan migrate |
| Start backend server | php artisan serve |
| Start Vite dev server | npm run dev |
The example environment file expects values such as APP_NAME, APP_ENV, APP_KEY, APP_URL, DB_CONNECTION, DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, and DB_PASSWORD for local development.15 The broader Laravel config also supports standard optional mail, Redis, AWS, queue, and broadcasting variables, but the repository's feature-specific logic does not currently introduce separate custom environment variables beyond those framework settings.15
This README is intentionally written to reflect the repository as it exists now, including details maintainers may want to revisit later.
| Topic | Current state |
|---|---|
| Documentation status | The previous README was still default Laravel boilerplate rather than project-specific documentation. |
| Auth credentials | Login currently validates username and password, not email/password. |
| Logout route | Logout is exposed without explicit route middleware even though the controller expects an authenticated user. |
| Soft delete route | The route references softdeleteUser, while the controller defines softDelete, indicating a naming mismatch in current code. |
| Reading rewards | Reading rank is incremented on start, finish, and note creation, making rank a central progression currency. |
| Content scraping | The active scraping logic targets Project Gutenberg HTML pages and strips content after the boilerplate header. |
| Privacy quirks | The notes listing endpoint currently returns all notes with related users and books. |
| Economy system | Secret-book purchasing uses reading rank rather than external payment. |