2026-03-31 14:15:32 +07:00
# 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).
2026-03-31 16:29:03 +07:00
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))"
2026-04-01 19:26:47 +07:00
```
Field Value
username admin
password Admin@12345
role admin
status approved