mirror of
https://git.victorphan.net/basketballcantho/ten-project.git
synced 2026-08-05 10:43:11 +07:00
152 lines
5.5 KiB
Markdown
152 lines
5.5 KiB
Markdown
# Project Specification: Personal PDF Learning Management System (LMS)
|
|
|
|
## 1. Project Overview
|
|
A web application that allows users to securely log in, upload PDF files to a personal gallery, and interact with those PDFs in a dedicated "Workbook" interface. The core feature is the ability to draw, highlight, and add text directly onto the PDF pages from any device (Desktop & Mobile), saving these annotations for future review.
|
|
|
|
## 2. Tech Stack Definition
|
|
Please use the following technologies for this project:
|
|
* **Frontend:** React.js (or Next.js) + Tailwind CSS.
|
|
* **PDF Rendering:** PDF.js (Mozilla) to render PDF pages to canvas/DOM.
|
|
* **Annotation Layer:** Fabric.js (overlaying the PDF to handle drawing, shapes, and text).
|
|
* **Backend:** FastAPI (Python) OR Express (Node.js) - *Copilot: please ask me which one to start with.*
|
|
* **Database:** PostgreSQL (using Prisma or SQLAlchemy for ORM).
|
|
* **Authentication:** JWT (JSON Web Tokens) with HttpOnly cookies.
|
|
* **Storage:** Local file system (Docker volume) for storing uploaded PDFs.
|
|
|
|
## 3. Core Features & Requirements
|
|
|
|
### 3.1. Authentication (JWT)
|
|
* User Registration and Login.
|
|
* Protect all API routes.
|
|
* Each user can only see and interact with their own uploaded PDFs.
|
|
|
|
### 3.2. PDF Gallery (Dashboard)
|
|
* **Upload:** Multi-file drag-and-drop upload.
|
|
* **Display:** Responsive CSS Grid displaying uploaded PDFs as cards. Include the document title and upload date.
|
|
* **Action:** Clicking a card opens the PDF in the "Workbook" view.
|
|
|
|
### 3.3. Interactive Workbook (The Core Viewer)
|
|
* **Responsive UI:** Must work seamlessly on mobile and desktop.
|
|
* **PDF Viewer:** Render the selected PDF file. Support pagination (Next/Prev page).
|
|
* **Annotation Tools (Fabric.js):**
|
|
* Pen tool (freehand drawing/circling).
|
|
* Highlighter tool (semi-transparent freehand or straight line).
|
|
* Text tool (click to type text).
|
|
* **Mobile Touch UX (Crucial):** Implement a toggle switch between two modes:
|
|
1. *Pan Mode:* Touch and swipe will scroll/zoom the page. Canvas drawing is disabled.
|
|
2. *Draw/Edit Mode:* Scrolling is locked. Touch events are passed to Fabric.js for drawing/annotating.
|
|
* **Save Mechanism:** A "Save" button that serializes the current Fabric.js canvas state into JSON and POSTs it to the backend.
|
|
* **Load Mechanism:** When opening a PDF, fetch the saved JSON annotation data and load it onto the Fabric.js canvas over the PDF.
|
|
|
|
## 4. Database Schema (Draft)
|
|
|
|
* `users`: id, username, password_hash, created_at
|
|
* `pdfs`: id, user_id (FK), title, file_path, created_at
|
|
* `annotations`: id, pdf_id (FK), user_id (FK), page_number, canvas_data (JSON), updated_at
|
|
|
|
## 5. Deployment strategy
|
|
* Provide a `docker-compose.yml` to spin up the Frontend, Backend, and PostgreSQL database together.
|
|
|
|
## 6. Instructions for Copilot
|
|
Let's build this step-by-step to avoid context limits.
|
|
1. First, analyze this spec and confirm you understand.
|
|
2. Provide the database schema models (SQLAlchemy or Prisma).
|
|
3. Wait for my confirmation before writing the Backend API routes.
|
|
|
|
---
|
|
|
|
## 7. CI/CD — Push lên GitHub Container Registry (GHCR)
|
|
|
|
### 7.1. Yêu cầu
|
|
|
|
- **Docker** đã cài và đang chạy
|
|
- **GitHub Personal Access Token (PAT)** với quyền `write:packages` và `read:packages`
|
|
- Tạo tại: https://github.com/settings/tokens → *Generate new token (classic)*
|
|
- File `.env` đã được cấu hình (xem `.env.example`)
|
|
|
|
### 7.2. Cấu hình `.env`
|
|
|
|
Thêm các dòng sau vào file `.env` (file này đã được gitignore, **không commit**):
|
|
|
|
```env
|
|
GITHUB_USER=<github_username_của_bạn>
|
|
GITHUB_TOKEN=<personal_access_token>
|
|
BACKEND_IMAGE=ghcr.io/<github_username>/lms-backend:latest
|
|
FRONTEND_IMAGE=ghcr.io/<github_username>/lms-frontend:latest
|
|
```
|
|
|
|
### 7.3. Build & Push lên GHCR
|
|
|
|
```bash
|
|
chmod +x push-ghcr.sh
|
|
./push-ghcr.sh
|
|
```
|
|
|
|
Script sẽ tự động:
|
|
1. Đọc `GITHUB_USER` và `GITHUB_TOKEN` từ `.env`
|
|
2. Đăng nhập vào `ghcr.io`
|
|
3. Build cả hai image (`lms-backend`, `lms-frontend`) bằng `docker compose build`
|
|
4. Tag và push lên GHCR
|
|
|
|
Để push với tag cụ thể (ví dụ: version):
|
|
```bash
|
|
./push-ghcr.sh v1.0.0
|
|
```
|
|
|
|
### 7.4. Deploy trên máy khác
|
|
|
|
Trên máy đích (server, VPS, máy tính khác):
|
|
|
|
```bash
|
|
# 1. Copy các file cần thiết
|
|
scp docker-compose.yml .env.example deploy.sh user@server:/opt/lms/
|
|
ssh user@server
|
|
|
|
# 2. Tạo .env từ example
|
|
cd /opt/lms
|
|
cp .env.example .env
|
|
# Điền các giá trị: POSTGRES_PASSWORD, JWT_SECRET_KEY, GITHUB_USER, GITHUB_TOKEN,
|
|
# BACKEND_IMAGE, FRONTEND_IMAGE
|
|
|
|
# 3. Chạy deploy
|
|
chmod +x deploy.sh
|
|
./deploy.sh
|
|
```
|
|
|
|
Script `deploy.sh` sẽ:
|
|
1. Kiểm tra `.env` hợp lệ
|
|
2. Đăng nhập GHCR (nếu có token)
|
|
3. Pull image từ GHCR
|
|
4. Khởi động toàn bộ stack: `db`, `backend`, `frontend`
|
|
|
|
### 7.5. Kiểm tra packages trên GitHub
|
|
|
|
Sau khi push, image sẽ xuất hiện tại:
|
|
```
|
|
https://github.com/<github_username>?tab=packages
|
|
```
|
|
|
|
> **Lưu ý:** Mặc định packages ở chế độ **Private**. Để máy khác pull mà không cần token, vào
|
|
> GitHub → Packages → tên package → *Package settings* → đổi visibility sang **Public**.
|
|
|
|
### 7.6. Sinh JWT Secret Key
|
|
|
|
```bash
|
|
python3 -c "import secrets; print(secrets.token_hex(32))"
|
|
```
|
|
|
|
|
|
Field Value
|
|
username admin
|
|
password Admin@12345
|
|
role admin
|
|
status approved
|
|
|
|
|
|
Lệnh Kết quả tag
|
|
./push-ghcr.sh 1.0.0 → 1.0.1 (tự động tăng patch)
|
|
./push-ghcr.sh minor 1.0.0 → 1.1.0
|
|
./push-ghcr.sh major 1.0.0 → 2.0.0
|
|
./push-ghcr.sh 1.5.0 đúng 1.5.0, không auto-bump
|
|
./push-ghcr.sh --no-cache rebuild + auto-bump patch
|
|
./push-ghcr.sh minor --no-cache bump minor + rebuild từ đầu |