An AI-assisted full-stack eBook platform for writing, organizing, publishing, reading, and exporting books. The project is split into three workspaces:
frontend/for the React + Vite web appbackend/for the Express + MongoDB APItests/for Playwright end-to-end coverage
- Chapter 1. Project Snapshot
- Chapter 2. Product Journey
- Chapter 3. Core Features
- Chapter 4. Tech Stack
- Chapter 5. System Architecture
- Chapter 6. Folder Structure
- Chapter 7. Local Setup and Run Guide
- Chapter 8. Environment Variables
- Chapter 9. API Overview
- Chapter 10. Testing Strategy
- Chapter 11. Data Model
- Chapter 12. Migration Details
- Chapter 13. Screenshot Placeholder Map
eBook Creator helps a user move from idea to publishable digital book inside one app. It combines authentication, a personal dashboard, a chapter-based editor, AI writing assistance, public publishing, profile management, cover uploads, and export flows for PDF, DOCX, and Markdown.
- Public landing page with published book discovery
- User signup, login, logout, and protected routes
- Dashboard for creating, listing, and deleting personal books
- Chapter-based editing with Markdown content
- AI-assisted chapter generation and content improvement
- Book cover upload support via
/uploads - Draft or published status management
- Reader view for navigating chapter-by-chapter content
- Profile editing for name, avatar, and password
- Export to PDF, DOCX, and Markdown
The landing page introduces the platform, highlights major capabilities, and shows published books pulled from the public books endpoint. This is the public entry point for unauthenticated visitors.
Users can register, log in, and then access protected routes through JWT-based authentication. The frontend stores the token and user in localStorage, while axiosInstance injects the Authorization header automatically.
The dashboard is the writer's home screen. It loads the authenticated user's books, supports creating a new book from a modal, and lets the user delete an existing book.
The editor is the core authoring experience. A user can:
- add and remove chapters
- edit chapter titles, descriptions, and Markdown content
- save book updates
- upload a cover image
- publish or unpublish a book
- open AI actions for content generation or improvement
- export the book into supported file formats
The reader view renders a book with its cover, chapter navigation, chapter list, and Markdown-rendered content. It is useful both as a preview and as an in-app reading experience for the owner.
The profile page allows the authenticated user to update their display name, avatar URL, and password while keeping email read-only.
- create a new book with title, subtitle, and author
- manage a chapter list inside a dedicated editor
- write in Markdown using
@uiw/react-md-editor - preview chapter content in the reader experience
The backend exposes four AI endpoints:
POST /api/ai/generate-chapterPOST /api/ai/improve-contentPOST /api/ai/generate-outlinePOST /api/ai/generate-title
The current frontend actively uses:
- chapter generation
- content improvement for grammar, clarity, and expansion
The outline and title endpoints already exist in the backend and can be wired into the UI later.
- books can be stored as
draftorpublished - published books appear in the public landing page catalog
- private dashboard routes remain protected behind authentication
- PDF export using
pdfkit - DOCX export using
docx - Markdown export using plain text response generation
- cover images are uploaded through
multer - backend serves static assets from
/uploads - frontend resolves backend asset URLs through
toBackendAssetUrl
| Layer | Stack |
|---|---|
| Frontend | React 19, TypeScript, Vite, React Router, Tailwind CSS, Axios, React Hot Toast, UIW Markdown Editor |
| Backend | Node.js, Express 5, TypeScript, Mongoose, JWT, Bcrypt, Multer |
| AI | Google Generative AI SDK |
| Export | PDFKit, docx, Markdown |
| Unit/Integration Testing | Jest, Supertest, MongoMemoryServer |
| E2E Testing | Playwright |
flowchart LR
U["User"] --> F["Frontend (React + Vite)"]
F --> A["Axios Client + Auth Context"]
A --> B["Backend API (Express)"]
B --> D["MongoDB"]
B --> G["Gemini API"]
B --> X["Export Generators (PDF/DOCX/Markdown)"]
B --> S["Uploads Directory"]
frontend/src/main.tsxbootstrapsBrowserRouter,AuthProvider, and the toast systemfrontend/src/App.tsxdefines public and protected routesfrontend/src/context/AuthContext.tsxmanages session state and profile refresh/update actionsfrontend/src/utils/axiosInstance.tsinjects JWT headers and clears invalid sessions on401
backend/src/app.tswires middleware, static uploads, and route groupsbackend/src/server.tsloads environment config, connects to MongoDB, and starts the HTTP server- route groups are split into auth, books, AI, and export APIs
- controllers hold business logic for each route group
backend/src/e2eServer.tsis a dedicated in-memory API server for deterministic browser teststests/playwright.config.tsboots the e2e backend and the frontend dev server togethertests/globalSetup.tsresets state before the browser suite starts
/api/authfor registration, login, and profile/api/booksfor CRUD, publishing, public listing, and cover uploads/api/aifor AI-assisted writing helpers/api/exportfor file generation/uploadsfor serving uploaded cover images
ebook/
├── README.md
├── backend/
│ ├── src/
│ │ ├── app.ts
│ │ ├── server.ts
│ │ ├── config/
│ │ ├── controllers/
│ │ ├── middlewares/
│ │ ├── models/
│ │ ├── routes/
│ │ ├── types/
│ │ └── e2eServer.ts
│ └── tests/
│ ├── integration/
│ ├── unit/
│ └── helpers/
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── context/
│ │ ├── pages/
│ │ ├── types/
│ │ └── utils/
│ └── public/
├── tests/
│ ├── e2e/
│ ├── globalSetup.ts
│ └── playwright.config.ts
└── docs/
└── screenshots/
- Node.js 18+
- npm
- MongoDB running locally or a reachable MongoDB connection string
- Gemini API key for real AI generation
Run these once:
cd backend && npm install
cd ../frontend && npm install
cd ../tests && npm installcd backend
npm run devcd frontend
npm run devThe frontend fallback API URL is http://localhost:8000/api, while the backend dev server defaults to port 5000 unless PORT is set.
Use one of these two approaches:
- Set backend
PORT=8000 - Or set frontend
VITE_API_BASE_URL=http://127.0.0.1:5000/api
Recommended local alignment:
# backend/.env
PORT=8000# frontend/.env
VITE_API_BASE_URL=http://127.0.0.1:8000/apiCreate backend/.env with:
MONGO_URI=mongodb://127.0.0.1:27017/ebook-db
JWT_SECRET=replace-with-a-secure-secret
GEMINI_API_KEY=your-gemini-api-key
PORT=8000Create frontend/.env with:
VITE_API_BASE_URL=http://127.0.0.1:8000/api- backend unit/integration tests set fallback
JWT_SECRETandGEMINI_API_KEYautomatically inbackend/tests/setup.ts - Playwright uses the dedicated e2e backend and injects
VITE_API_BASE_URL=http://127.0.0.1:8000/api - the e2e server provides its own fallback values and does not depend on MongoDB
| Method | Route | Access | Purpose |
|---|---|---|---|
POST |
/api/auth/register |
Public | Register a new user and return a JWT |
POST |
/api/auth/login |
Public | Login and return token plus user payload |
GET |
/api/auth/profile |
Private | Fetch the current user profile |
PUT |
/api/auth/profile |
Private | Update name, avatar, and optionally password |
| Method | Route | Access | Purpose |
|---|---|---|---|
GET |
/api/books/public |
Public | List published books for the landing page |
POST |
/api/books |
Private | Create a book |
GET |
/api/books |
Private | List books owned by the authenticated user |
GET |
/api/books/:id |
Private | Fetch one owned book |
PUT |
/api/books/:id |
Private | Update book metadata, chapters, or status |
DELETE |
/api/books/:id |
Private | Delete a book |
PUT |
/api/books/cover/:id |
Private | Upload or replace a cover image |
| Method | Route | Access | Purpose |
|---|---|---|---|
POST |
/api/ai/generate-chapter |
Private | Generate chapter content from chapter metadata |
POST |
/api/ai/improve-content |
Private | Improve existing content |
POST |
/api/ai/generate-outline |
Private | Generate a structured book outline |
POST |
/api/ai/generate-title |
Private | Generate possible titles and subtitles |
| Method | Route | Access | Purpose |
|---|---|---|---|
GET |
/api/export/pdf/:id |
Private | Download the book as PDF |
GET |
/api/export/docx/:id |
Private | Download the book as DOCX |
GET |
/api/export/markdown/:id |
Private | Download the book as Markdown |
Authenticated endpoints expect:
Authorization: Bearer <jwt_token>Example book payload:
{
"title": "My First Book",
"subtitle": "A Practical Writing Journey",
"author": "Jane Doe",
"status": "draft",
"chapters": [
{
"title": "Chapter 1",
"description": "Opening context",
"content": "## Intro\n\nThis is chapter content."
}
]
}Example AI generation payload:
{
"title": "Chapter 3: The Breakthrough",
"description": "The turning point in the story",
"bookContext": "A startup founder's journey from idea to launch"
}Cover upload details:
- route:
PUT /api/books/cover/:id - content type:
multipart/form-data - form field:
coverImage
Backend tests live in backend/tests and cover:
- auth middleware
- upload middleware
Usermodel behaviorBookmodel behavior- auth API integration
- app-level integration such as CORS and static uploads
Key implementation details:
- Jest runs in-band with
ts-jest - Supertest exercises Express routes directly
mongodb-memory-serverprovides an isolated in-memory MongoDB for tests- Gemini calls are mocked through
backend/tests/helpers/mockGemini.ts
Commands:
cd backend
npm test
npm run test:unit
npm run test:integration
npm run test:coverageCoverage thresholds are configured globally in backend/jest.config.js at:
- branches:
80 - functions:
80 - lines:
80 - statements:
80
E2E tests live in tests/ and validate real browser flows against a dedicated in-memory backend.
Covered areas include:
- auth smoke flows
- public landing page behavior
- dashboard book lifecycle
- editor, AI, upload, and export flows
- reader and profile settings flows
How the suite works:
- starts
backend/src/e2eServer.tson127.0.0.1:8000 - starts the frontend dev server with
VITE_API_BASE_URL=http://127.0.0.1:8000/api - runs a reset step in
tests/globalSetup.ts - uses Chromium for full coverage and Firefox/WebKit for smoke coverage
Commands:
cd tests
npm run test:e2e
npm run test:e2e -- --project=chromium
npm run test:e2e -- --reporter=line
npm run test:e2e -- --listDefined in backend/src/models/User.ts.
| Field | Type | Notes |
|---|---|---|
name |
string |
required |
email |
string |
required, unique, lowercase |
password |
string |
required, hashed before save, not selected by default |
avatar |
string |
optional URL-like string |
isPro |
boolean |
defaults to false |
Defined in backend/src/models/Book.ts.
| Field | Type | Notes |
|---|---|---|
userId |
ObjectId |
owner reference |
title |
string |
required |
subtitle |
string |
optional |
author |
string |
required |
coverImage |
string |
upload path or external URL |
chapters |
Chapter[] |
nested chapter list |
status |
"draft" | "published" |
defaults to draft |
createdAt |
Date |
auto-generated |
updatedAt |
Date |
auto-generated |
Nested inside the book schema.
| Field | Type | Notes |
|---|---|---|
title |
string |
required |
description |
string |
optional summary |
content |
string |
Markdown content |
This repository has already started a TypeScript hardening migration across key frontend flows. The most visible migrated areas are:
frontend/src/context/AuthContext.tsxfrontend/src/pages/EditorPage.tsxfrontend/src/pages/ViewBookPage.tsx- shared types in
frontend/src/typesandbackend/src/types
AuthContextnow uses an explicitAuthContextValue | nulltypeuseAuthreturns a typed context contract- provider
childrenare typed withReactNode userandtokenstate are explicitly typed- auth methods now expose explicit async return contracts
- error handling uses safer
unknowncasting and nullish coalescing
- route params are typed
- editor state is typed as
Book | null - chapter update helpers use typed
Chapterkeys - export format handling is narrowed to
"pdf" | "docx" | "markdown" - AI improvement handling is narrowed to supported action types
- null guards were added before book-dependent operations
- route params are typed
- book state is typed as
Book | null - null guards prevent invalid access before the API payload loads
There is currently no dedicated migration framework or versioned migration folder in the repository. Schema evolution is code-first through Mongoose models and application updates.
That means a schema change usually requires updates in more than one place:
- Mongoose schema in
backend/src/models - shared backend types in
backend/src/types/index.ts - frontend types in
frontend/src/types - controllers, validation logic, and route payload handling
- e2e server contract in
backend/src/e2eServer.ts - backend unit/integration tests and Playwright helpers
- existing MongoDB documents if a new required field is introduced
If you add a new field or change a contract, follow this order:
- Update the schema and TypeScript types first
- Update controller logic and request/response payloads
- Update frontend forms, pages, and API types
- Update the in-memory e2e server so browser tests stay aligned
- Update unit, integration, and e2e tests
- Backfill old data in MongoDB before making a new field mandatory in production
Use the docs/screenshots/ folder for project images. Suggested filenames:
| Section | Suggested file |
|---|---|
| Landing page | docs/screenshots/chapter-02-landing-page.png |
| Auth flow | docs/screenshots/chapter-02-auth-flow.png |
| Dashboard | docs/screenshots/chapter-02-dashboard.png |
| Editor | docs/screenshots/chapter-02-editor.png |
| Reader view | docs/screenshots/chapter-02-reader-view.png |
| Profile page | docs/screenshots/chapter-02-profile.png |
| Architecture diagram screenshot, if needed | docs/screenshots/chapter-05-architecture.png |
| Test report screenshot | docs/screenshots/chapter-10-test-report.png |






