diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7d1b302 --- /dev/null +++ b/.gitignore @@ -0,0 +1,44 @@ +# Dependencies +node_modules/ +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +env/ +venv/ +.venv/ + +# Database +*.db +*.sqlite +*.sqlite3 + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log +logs/ + +# Environment variables +.env +.env.local +.env.production.local +.env.test.local + +# Build outputs +.next/ +dist/ +build/ + +# Cache +.cache/ +.parcel-cache/ \ No newline at end of file diff --git a/README.md b/README.md index 7ec6e9a..695e223 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,130 @@ -# ai-book-reader -AI assisted book reader +# AI Book Reader + +AI assisted book reader with smart notes and bookmarks management. + +## Features + +- 📝 **Smart Notes**: Take AI-powered notes while reading +- 🔖 **Bookmarks**: Save important passages and references +- 🤖 **AI Insights**: Get AI-generated summaries and insights (coming soon) +- 💾 **SQLite Database**: Local storage for notes and bookmarks + +## Tech Stack + +### Frontend +- **Next.js 15** with TypeScript +- **React 19** +- **Tailwind CSS** for styling +- **App Router** for navigation + +### Backend +- **FastAPI** with Python 3.12 +- **SQLAlchemy** for database ORM +- **SQLite** for data storage +- **Pydantic** for data validation +- **CORS** enabled for frontend integration + +## Project Structure + +``` +ai-book-reader/ +├── frontend/ # Next.js 15 frontend application +│ ├── app/ # App router pages and components +│ │ ├── notes/ # Notes management page +│ │ ├── bookmarks/# Bookmarks management page +│ │ └── ... +│ └── package.json # Frontend dependencies +├── backend/ # FastAPI backend application +│ ├── app/ # Main application code +│ │ ├── main.py # FastAPI app and routes +│ │ ├── models.py# SQLAlchemy database models +│ │ ├── schemas.py# Pydantic schemas +│ │ └── database.py# Database configuration +│ └── requirements.txt# Backend dependencies +├── vercel.json # Vercel deployment configuration +└── start.sh # Local development script +``` + +## Getting Started + +### Prerequisites +- Node.js 20+ +- Python 3.12+ +- npm or yarn + +### Installation + +1. **Install Frontend Dependencies** + ```bash + cd frontend + npm install + ``` + +2. **Install Backend Dependencies** + ```bash + cd backend + pip install -r requirements.txt + ``` + +### Development + +#### Option 1: Start Both Servers with Script +```bash +chmod +x start.sh +./start.sh +``` + +#### Option 2: Start Servers Individually + +**Backend (Terminal 1):** +```bash +cd backend +python3 -m uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 +``` + +**Frontend (Terminal 2):** +```bash +cd frontend +npm run dev +``` + +### Access the Application + +- **Frontend**: http://localhost:3000 +- **Backend API**: http://localhost:8000 +- **API Documentation**: http://localhost:8000/docs + +## API Endpoints + +### Notes +- `GET /notes/` - List all notes +- `POST /notes/` - Create a new note +- `GET /notes/{id}` - Get specific note +- `PUT /notes/{id}` - Update note +- `DELETE /notes/{id}` - Delete note + +### Bookmarks +- `GET /bookmarks/` - List all bookmarks +- `POST /bookmarks/` - Create a new bookmark +- `GET /bookmarks/{id}` - Get specific bookmark +- `DELETE /bookmarks/{id}` - Delete bookmark + +## Deployment + +This project is configured for deployment on **Vercel**: + +1. Connect your GitHub repository to Vercel +2. Vercel will automatically detect the configuration from `vercel.json` +3. Both frontend and backend will be deployed as serverless functions + +## Contributing + +1. Fork the repository +2. Create a feature branch +3. Make your changes +4. Test locally +5. Submit a pull request + +## License + +This project is open source and available under the MIT License. diff --git a/api/index.py b/api/index.py new file mode 100644 index 0000000..ff7e8b0 --- /dev/null +++ b/api/index.py @@ -0,0 +1,28 @@ +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +app = FastAPI( + title="AI Book Reader API", + description="API for managing notes and bookmarks in the AI Book Reader", + version="1.0.0" +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.get("/") +def read_root(): + return {"message": "AI Book Reader API", "version": "1.0.0"} + +@app.get("/health") +def health_check(): + return {"status": "healthy"} + +# Export the app for Vercel +handler = app \ No newline at end of file diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000..a22886d --- /dev/null +++ b/backend/__init__.py @@ -0,0 +1 @@ +# AI Book Reader Backend Package \ No newline at end of file diff --git a/backend/api/main.py b/backend/api/main.py new file mode 100644 index 0000000..24c71d9 --- /dev/null +++ b/backend/api/main.py @@ -0,0 +1,5 @@ +from app.main import app + +# This is the Vercel serverless function handler +def handler(request, response): + return app(request, response) \ No newline at end of file diff --git a/backend/app/__init__.py b/backend/app/__init__.py new file mode 100644 index 0000000..ea03d0e --- /dev/null +++ b/backend/app/__init__.py @@ -0,0 +1 @@ +# AI Book Reader Backend \ No newline at end of file diff --git a/backend/app/database.py b/backend/app/database.py new file mode 100644 index 0000000..f217f1d --- /dev/null +++ b/backend/app/database.py @@ -0,0 +1,24 @@ +from sqlalchemy import create_engine +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import sessionmaker +import os + +# SQLite database URL +SQLALCHEMY_DATABASE_URL = "sqlite:///./ai_book_reader.db" + +# Create SQLite engine +engine = create_engine( + SQLALCHEMY_DATABASE_URL, + connect_args={"check_same_thread": False} +) + +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) + +Base = declarative_base() + +def get_db(): + db = SessionLocal() + try: + yield db + finally: + db.close() \ No newline at end of file diff --git a/backend/app/main.py b/backend/app/main.py new file mode 100644 index 0000000..b5c4b79 --- /dev/null +++ b/backend/app/main.py @@ -0,0 +1,108 @@ +from fastapi import FastAPI, HTTPException, Depends +from fastapi.middleware.cors import CORSMiddleware +from sqlalchemy.orm import Session +from typing import List, Optional + +from . import models, schemas, database +from .database import get_db + +# Create database tables +models.Base.metadata.create_all(bind=database.engine) + +app = FastAPI( + title="AI Book Reader API", + description="API for managing notes and bookmarks in the AI Book Reader", + version="1.0.0" +) + +# Add CORS middleware +app.add_middleware( + CORSMiddleware, + allow_origins=["http://localhost:3000", "https://*.vercel.app"], + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + +@app.get("/") +def read_root(): + return {"message": "AI Book Reader API", "version": "1.0.0"} + +@app.get("/health") +def health_check(): + return {"status": "healthy"} + +# Notes endpoints +@app.post("/notes/", response_model=schemas.Note) +def create_note(note: schemas.NoteCreate, db: Session = Depends(get_db)): + db_note = models.Note(**note.dict()) + db.add(db_note) + db.commit() + db.refresh(db_note) + return db_note + +@app.get("/notes/", response_model=List[schemas.Note]) +def read_notes(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + notes = db.query(models.Note).offset(skip).limit(limit).all() + return notes + +@app.get("/notes/{note_id}", response_model=schemas.Note) +def read_note(note_id: int, db: Session = Depends(get_db)): + note = db.query(models.Note).filter(models.Note.id == note_id).first() + if note is None: + raise HTTPException(status_code=404, detail="Note not found") + return note + +@app.put("/notes/{note_id}", response_model=schemas.Note) +def update_note(note_id: int, note: schemas.NoteUpdate, db: Session = Depends(get_db)): + db_note = db.query(models.Note).filter(models.Note.id == note_id).first() + if db_note is None: + raise HTTPException(status_code=404, detail="Note not found") + + for key, value in note.dict(exclude_unset=True).items(): + setattr(db_note, key, value) + + db.commit() + db.refresh(db_note) + return db_note + +@app.delete("/notes/{note_id}") +def delete_note(note_id: int, db: Session = Depends(get_db)): + db_note = db.query(models.Note).filter(models.Note.id == note_id).first() + if db_note is None: + raise HTTPException(status_code=404, detail="Note not found") + + db.delete(db_note) + db.commit() + return {"message": "Note deleted"} + +# Bookmarks endpoints +@app.post("/bookmarks/", response_model=schemas.Bookmark) +def create_bookmark(bookmark: schemas.BookmarkCreate, db: Session = Depends(get_db)): + db_bookmark = models.Bookmark(**bookmark.dict()) + db.add(db_bookmark) + db.commit() + db.refresh(db_bookmark) + return db_bookmark + +@app.get("/bookmarks/", response_model=List[schemas.Bookmark]) +def read_bookmarks(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)): + bookmarks = db.query(models.Bookmark).offset(skip).limit(limit).all() + return bookmarks + +@app.get("/bookmarks/{bookmark_id}", response_model=schemas.Bookmark) +def read_bookmark(bookmark_id: int, db: Session = Depends(get_db)): + bookmark = db.query(models.Bookmark).filter(models.Bookmark.id == bookmark_id).first() + if bookmark is None: + raise HTTPException(status_code=404, detail="Bookmark not found") + return bookmark + +@app.delete("/bookmarks/{bookmark_id}") +def delete_bookmark(bookmark_id: int, db: Session = Depends(get_db)): + db_bookmark = db.query(models.Bookmark).filter(models.Bookmark.id == bookmark_id).first() + if db_bookmark is None: + raise HTTPException(status_code=404, detail="Bookmark not found") + + db.delete(db_bookmark) + db.commit() + return {"message": "Bookmark deleted"} \ No newline at end of file diff --git a/backend/app/models.py b/backend/app/models.py new file mode 100644 index 0000000..8f4c8b2 --- /dev/null +++ b/backend/app/models.py @@ -0,0 +1,27 @@ +from sqlalchemy import Column, Integer, String, Text, DateTime, Boolean +from sqlalchemy.sql import func +from .database import Base + +class Note(Base): + __tablename__ = "notes" + + id = Column(Integer, primary_key=True, index=True) + title = Column(String(255), nullable=False, index=True) + content = Column(Text, nullable=False) + book_title = Column(String(255), nullable=False) + page_number = Column(Integer, nullable=True) + chapter = Column(String(255), nullable=True) + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column(DateTime(timezone=True), onupdate=func.now()) + +class Bookmark(Base): + __tablename__ = "bookmarks" + + id = Column(Integer, primary_key=True, index=True) + book_title = Column(String(255), nullable=False, index=True) + page_number = Column(Integer, nullable=False) + chapter = Column(String(255), nullable=True) + quote = Column(Text, nullable=True) + notes = Column(Text, nullable=True) + is_favorite = Column(Boolean, default=False) + created_at = Column(DateTime(timezone=True), server_default=func.now()) \ No newline at end of file diff --git a/backend/app/schemas.py b/backend/app/schemas.py new file mode 100644 index 0000000..1fe85ac --- /dev/null +++ b/backend/app/schemas.py @@ -0,0 +1,48 @@ +from pydantic import BaseModel +from datetime import datetime +from typing import Optional + +# Note schemas +class NoteBase(BaseModel): + title: str + content: str + book_title: str + page_number: Optional[int] = None + chapter: Optional[str] = None + +class NoteCreate(NoteBase): + pass + +class NoteUpdate(BaseModel): + title: Optional[str] = None + content: Optional[str] = None + book_title: Optional[str] = None + page_number: Optional[int] = None + chapter: Optional[str] = None + +class Note(NoteBase): + id: int + created_at: datetime + updated_at: Optional[datetime] = None + + class Config: + from_attributes = True + +# Bookmark schemas +class BookmarkBase(BaseModel): + book_title: str + page_number: int + chapter: Optional[str] = None + quote: Optional[str] = None + notes: Optional[str] = None + is_favorite: bool = False + +class BookmarkCreate(BookmarkBase): + pass + +class Bookmark(BookmarkBase): + id: int + created_at: datetime + + class Config: + from_attributes = True \ No newline at end of file diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..154cc5a --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,6 @@ +fastapi==0.104.1 +uvicorn[standard]==0.24.0 +sqlalchemy==2.0.23 +pydantic==2.5.0 +python-multipart==0.0.6 +httpx==0.25.2 \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,36 @@ +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/frontend/app/bookmarks/page.tsx b/frontend/app/bookmarks/page.tsx new file mode 100644 index 0000000..5380f68 --- /dev/null +++ b/frontend/app/bookmarks/page.tsx @@ -0,0 +1,45 @@ +export default function BookmarksPage() { + return ( +
+
+

🔖 My Bookmarks

+ +
+

Add New Bookmark

+
+ + + +