Archived
feat: uv config other feats
- add uv configuration for the backend - update frontend to make auth work - add new auth endpoints - add bookmars feat - add reader feat
This commit is contained in:
@@ -1,96 +0,0 @@
|
||||
# 009 — Expo Mobile Application Integration
|
||||
|
||||
**Issue:** #16
|
||||
**Status:** Draft
|
||||
**Created:** 2026-05-29
|
||||
|
||||
## Objective
|
||||
|
||||
Integrate an Expo-based React Native mobile application into the `cloud-reader` monorepo, sharing types, API client patterns, and configuration with the existing web frontend.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
cloud-reader/
|
||||
├── mobile/ # Expo React Native app
|
||||
│ ├── package.json
|
||||
│ ├── app.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── babel.config.js
|
||||
│ ├── App.tsx # Root component
|
||||
│ ├── src/
|
||||
│ │ ├── api/ # API client (mirrors frontend/src/api/ pattern)
|
||||
│ │ │ ├── client.ts # Axios instance + JWT interceptor
|
||||
│ │ │ ├── books.ts # Book API calls
|
||||
│ │ │ └── annotations.ts
|
||||
│ │ ├── screens/ # Screen-level components
|
||||
│ │ ├── components/ # Reusable UI components
|
||||
│ │ ├── navigation/ # React Navigation setup
|
||||
│ │ ├── context/ # Auth context, etc.
|
||||
│ │ ├── hooks/ # Custom hooks
|
||||
│ │ └── types/ # Mobile-specific types
|
||||
│ └── assets/
|
||||
├── packages/
|
||||
│ └── shared/
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ └── src/
|
||||
│ ├── types.ts # Shared domain types (Book, User, Bookmark, Note)
|
||||
│ └── utils.ts # Shared utility functions
|
||||
└── package.json # Root — updated workspace config
|
||||
```
|
||||
|
||||
## Monorepo Workspace Config
|
||||
|
||||
Root `package.json` workspaces array updated to include `"mobile"`, `"packages/shared"` alongside existing `"frontend"` and `"backend"`.
|
||||
|
||||
## Shared `packages/shared`
|
||||
|
||||
- `@cloud-reader/shared` package published within the monorepo
|
||||
- Exports:
|
||||
- All domain types (`Book`, `BookSummary`, `Bookmark`, `Note`, `User`, `AnnotationEntry`, `PaginatedResponse`, `TokenResponse`)
|
||||
- API endpoint constants
|
||||
- Date formatting helpers
|
||||
- Validation utilities (email regex, password strength check)
|
||||
|
||||
## Mobile App Structure
|
||||
|
||||
### API Client (`mobile/src/api/client.ts`)
|
||||
- Axios instance configured with:
|
||||
- Base URL from environment variable (`EXPO_PUBLIC_API_URL`)
|
||||
- JWT token attachment via request interceptor
|
||||
- Token refresh response interceptor on 401
|
||||
- Uses `AsyncStorage` for token persistence (instead of `localStorage`)
|
||||
|
||||
### Navigation (`mobile/src/navigation/`)
|
||||
- React Navigation stack:
|
||||
1. `AuthStack` — Login, Register screens
|
||||
2. `MainTabs` — Library, Search, Settings tabs
|
||||
3. `BookReader` — Full-screen reading view
|
||||
|
||||
### Key Screens
|
||||
| Screen | Route | Purpose |
|
||||
|--------|-------|---------|
|
||||
| Login | `Auth/Login` | Email/password login |
|
||||
| Register | `Auth/Register` | User registration |
|
||||
| Library | `Main/Library` | Book list with filtering |
|
||||
| BookDetail | `Main/BookDetail` | Book metadata + actions |
|
||||
| Reader | `Reader/View` | EPUB/PDF rendering |
|
||||
| Search | `Main/Search` | Book discovery |
|
||||
| Settings | `Main/Settings` | Profile, theme, download mgmt |
|
||||
|
||||
## Backend Changes Required
|
||||
|
||||
None. The existing Django REST API already serves all endpoints needed by the mobile app. The mobile app communicates with the same backend via the shared API base URL.
|
||||
|
||||
## Docker
|
||||
|
||||
No changes to `docker-compose.yml` needed — the mobile app runs on-device or via Expo Go, not inside Docker.
|
||||
|
||||
## CI/CD Considerations
|
||||
|
||||
The monorepo structure supports a single pipeline that can:
|
||||
- `yarn install` at root (installs all workspaces)
|
||||
- `yarn workspace @cloud-reader/shared build`
|
||||
- `yarn workspace @cloud-reader/mobile build` (Expo EAS for mobile builds)
|
||||
- `yarn workspace @cloud-reader/frontend build` (Vite for web builds)
|
||||
@@ -0,0 +1,117 @@
|
||||
# 010 — Open Library metadata on import
|
||||
|
||||
**Status:** Implemented
|
||||
**Created:** 2026-06-03
|
||||
|
||||
## Objective
|
||||
|
||||
After a user uploads an EPUB/PDF (`POST /api/books/ebooks/`), enrich the `EBook` with metadata and a cover from [Open Library](https://openlibrary.org/developers/api), using only **title** and **author** from the upload form. Prefer **Spanish** editions when available; fall back to English/any.
|
||||
|
||||
Upload must **never fail** if Open Library is down or no match is found.
|
||||
|
||||
## Trigger
|
||||
|
||||
- **Automatic:** `EBookUploadSerializer.create()` calls `enrich_ebook_metadata(ebook)` after file save.
|
||||
- **Manual:** `POST /api/books/ebooks/{id}/enrich-metadata/` re-runs enrichment (owner only).
|
||||
|
||||
## Open Library usage
|
||||
|
||||
### Search
|
||||
|
||||
`GET https://openlibrary.org/search.json`
|
||||
|
||||
| Param | Value |
|
||||
|-------|--------|
|
||||
| `title` | User-provided title |
|
||||
| `author` | User-provided author |
|
||||
| `lang` | `es` (primary) or `en` (fallback) |
|
||||
| `limit` | `5` |
|
||||
| `fields` | `key,title,author_name,cover_i,first_publish_year,subject,language,edition_key,number_of_pages_median,publisher` |
|
||||
|
||||
Primary pass also uses query filter `language:spa`. Fallback omits language filter.
|
||||
|
||||
### Covers
|
||||
|
||||
`GET https://covers.openlibrary.org/b/id/{cover_i}-L.jpg` — downloaded and stored on `EBook.cover_image`.
|
||||
|
||||
## Match scoring
|
||||
|
||||
| Score | Behavior |
|
||||
|-------|----------|
|
||||
| ≥ 0.8 | Apply OL title/author + cover + full metadata |
|
||||
| 0.6 – 0.8 | Metadata + cover only; keep user title/author |
|
||||
| < 0.6 | `match_status: not_found`; no field changes except metadata stub |
|
||||
|
||||
Author overlap + title similarity (normalized strings, `difflib.SequenceMatcher`). Prefer hits with `cover_i`.
|
||||
|
||||
## Data model
|
||||
|
||||
No migration. Uses existing fields on `EBook`:
|
||||
|
||||
- `metadata_json` — full enrichment payload (see below)
|
||||
- `cover_image` — downloaded cover file
|
||||
- `title` / `author` — updated when match score ≥ 0.8
|
||||
|
||||
### `metadata_json` shape
|
||||
|
||||
```json
|
||||
{
|
||||
"source": "openlibrary",
|
||||
"matched_at": "2026-06-03T12:00:00+00:00",
|
||||
"match_language": "es",
|
||||
"match_score": 0.92,
|
||||
"match_status": "matched",
|
||||
"user_input": { "title": "...", "author": "..." },
|
||||
"openlibrary": {
|
||||
"work_key": "/works/OL...",
|
||||
"edition_key": "...",
|
||||
"title": "...",
|
||||
"authors": ["..."],
|
||||
"cover_id": 12345,
|
||||
"cover_url": "https://covers.openlibrary.org/b/id/12345-L.jpg",
|
||||
"first_publish_year": 1605,
|
||||
"subjects": ["..."],
|
||||
"languages": ["spa"],
|
||||
"publishers": ["..."],
|
||||
"number_of_pages_median": 320
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API changes
|
||||
|
||||
### Upload response (unchanged path)
|
||||
|
||||
`POST /api/books/ebooks/` — response may include populated `cover_image` and updated `title`/`author` after sync enrichment.
|
||||
|
||||
### Detail
|
||||
|
||||
`GET /api/books/ebooks/{id}/` — adds read-only `metadata` (alias of `metadata_json`).
|
||||
|
||||
### Manual refresh
|
||||
|
||||
`POST /api/books/ebooks/{id}/enrich-metadata/` — returns updated `EBookDetailSerializer` payload.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Env var | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `OPENLIBRARY_ENABLED` | `true` | Kill switch |
|
||||
| `OPENLIBRARY_PREFERRED_LANG` | `es` | Primary `lang` param |
|
||||
| `OPENLIBRARY_FALLBACK_LANG` | `en` | Fallback `lang` param |
|
||||
| `OPENLIBRARY_TIMEOUT_SECONDS` | `5` | HTTP timeout |
|
||||
| `OPENLIBRARY_USER_AGENT` | `CloudReader/1.0` | User-Agent header |
|
||||
|
||||
## Code layout
|
||||
|
||||
```
|
||||
backend/apps/books/services/
|
||||
├── openlibrary.py # Search, scoring, cover download
|
||||
└── metadata.py # enrich_ebook_metadata orchestrator
|
||||
```
|
||||
|
||||
## Verification
|
||||
|
||||
1. Upload with title `Don Quijote`, author `Cervantes` → cover + Spanish-friendly metadata.
|
||||
2. Upload with nonsense title/author → 201, no cover, `match_status: not_found`.
|
||||
3. `POST .../enrich-metadata/` on existing ebook refreshes metadata.
|
||||
Reference in New Issue
Block a user