TutorLoom
The learning platform for companies, colleges and training institutes. Free and open source, with an Enterprise edition.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Website luyện thi IELTS đầy đủ 4 kỹ năng, giao diện tiếng Việt, chấm điểm bằng AI. Toàn bộ dịch vụ dùng trong dự án đều có gói miễn phí, không cần thẻ tín dụng.
Công nghệ: Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Firebase Auth + Firestore · Gemini API · Groq Whisper.
npm install
npm run dev
Mở http://localhost:3000 — website chạy được ngay mà không cần cấu hình gì:
Ba mục dưới đây sẽ bật đầy đủ tính năng.
Bước 1 — tạo project
Bước 2 — bật đăng nhập
Bước 3 — tạo database
asia-southeast1 — Singapore)firestore.rules trong dự án này vào, bấm PublishQuy tắc trong
firestore.rulesđảm bảo mỗi người chỉ đọc và ghi được dữ liệu của chính mình. Đừng để database ở chế độ test mode — ai cũng đọc được dữ liệu của người khác.
Bước 4 — lấy khoá
</>firebaseConfigBước 5 — điền vào dự án
cp .env.local.example .env.local
Mở .env.local và điền:
NEXT_PUBLIC_FIREBASE_API_KEY=AIza...
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=ten-project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=ten-project
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=ten-project.firebasestorage.app
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=123456789
NEXT_PUBLIC_FIREBASE_APP_ID=1:123456789:web:abc...
Khởi động lại npm run dev.
Bước 6 — Firestore index (tuỳ chọn)
Trang Dashboard hiện đọc lịch sử theo người dùng rồi sắp xếp phía trình duyệt, nên web chạy được ngay cả khi bạn chưa tạo composite index.
Nếu sau này bạn muốn tối ưu truy vấn lịch sử bằng orderBy("createdAt", "desc")
trực tiếp trên Firestore, dự án đã có sẵn định nghĩa trong firestore.indexes.json.
Dùng Firebase CLI để deploy index:
npm i -g firebase-tools
firebase login
firebase deploy --only firestore:indexes
Hạn mức miễn phí (gói Spark): 50.000 lượt đọc và 20.000 lượt ghi mỗi ngày. Với cách lưu của dự án này (1 lượt ghi cho mỗi bài làm), bạn phải có khoảng vài nghìn người dùng hoạt động mỗi ngày mới chạm trần.
.env.local:GEMINI_API_KEY=AIza...
GEMINI_MODEL=gemini-flash-latest
Sau khi có khoá này:
/api/analyze-progress để phân tích các bài gần đây và gợi ý kỹ năng / đề nên làm tiếp. Nếu Gemini hết quota, hệ thống tự chuyển sang gợi ý theo điểm thấp nhất.Lưu ý về free tier: Google có giới hạn số lượt mỗi phút và mỗi ngày, và dùng prompt của bạn để cải thiện model ở gói miễn phí (ngoài khu vực EU/UK). Nếu bạn định thu tiền người dùng, hãy chuyển sang gói trả phí để tắt việc này.
Dùng cho phần Speaking.
.env.local:GROQ_API_KEY=gsk_...
Hạn mức free: khoảng 30 request/phút, 1.000 request/ngày. Groq không dùng dữ liệu của bạn để train.
Nếu không cấu hình, phần Speaking vẫn ghi âm được — bạn chỉ cần tự gõ lại nội dung mình vừa nói vào ô transcript trước khi bấm chấm.
Mặc định bài nghe được đọc bằng giọng có sẵn của trình duyệt. Để có audio giọng bản xứ (Anh / Úc / Mỹ / Canada) như đề thi thật:
pip install edge-tts
brew install ffmpeg # macOS — để chèn khoảng lặng giữa các lượt nói
npm run gen:audio
Script đọc data/listening/*.json, tổng hợp từng lượt nói bằng đúng giọng đã khai
trong trường voices, rồi ghép thành public/audio/lt-01-s1.mp3, lt-01-s2.mp3...
Chạy một lần là xong; file MP3 nằm trong public/ nên deploy lên Vercel là có luôn.
npm run gen:audio -- --force # tạo lại tất cả
npm run gen:audio -- --test lt-01 # chỉ tạo cho một đề
edge-tts dùng giọng neural của Microsoft Edge, miễn phí và không giới hạn.
Sau khi nộp bài Listening và bấm Xem lại từng câu, mỗi câu có nút Nghe đoạn này để phát lại đúng đoạn ghi âm chứa đáp án của câu đó.
Web tự dò đáp án nằm ở lượt nói nào trong transcript (hiểu được cả khi số được đọc
bằng chữ, ví dụ đáp án 550 trong câu "five hundred and fifty"), rồi quy ra mốc thời
gian trong file MP3. Nếu chưa có file mốc thời gian, mốc được ước lượng theo độ dài
từng lượt nói — vẫn dùng được, chỉ lệch vài giây nên đoạn phát được nới rộng ra hai đầu.
Muốn chính xác tuyệt đối thì chạy lại một lần:
npm run gen:audio -- --force
Lần chạy này ghi thêm public/audio/<đề>-s<n>.timings.json chứa mốc bắt đầu và kết
thúc của từng lượt nói. Có file đó rồi thì đoạn phát lại khớp chính xác. Các đề tạo
mới sau này sẽ tự có file mốc thời gian ngay từ đầu.
Ngoài bốn kỹ năng IELTS, trang còn một mục riêng ở /grammar dành cho người mới bắt
đầu hoặc mất gốc: 30 bài, 300 câu, đi từ động từ to be tới câu điều kiện.
Lộ trình chia ba mức:
| Mức | Số bài | Nội dung |
|---|---|---|
| Mới bắt đầu | 12 | to be, đại từ, mạo từ a/an, số nhiều, this/that, hiện tại đơn, there is/are, can, giới từ, have got, số và giờ |
| Sơ cấp | 11 | hiện tại tiếp diễn, trạng từ tần suất, quá khứ đơn, mạo từ the, so sánh hơn/nhất, will vs be going to, lượng từ |
| Trung cấp | 7 | hiện tại hoàn thành, quá khứ tiếp diễn, must/should/have to, câu điều kiện, mệnh đề quan hệ, câu bị động |
Mỗi bài gồm:
Kết quả được lưu cùng hệ thống tiến độ với các kỹ năng IELTS, nhưng không tính vào band tổng — mục này chấm theo phần trăm đúng, không phải band score. Dashboard có một thẻ riêng theo dõi đã học bao nhiêu trong 30 bài.
Dữ liệu nằm ở data/grammar/g1.json … g4.json. Muốn thêm bài thì viết thêm vào một
trong bốn file đó theo đúng kiểu GrammarLesson trong lib/types.ts; trang danh sách
tự sắp xếp theo trường order.
Cách 1 — sinh bằng AI (nhanh nhất)
node scripts/generate-tests.mjs reading "Năng lượng gió ngoài khơi"
node scripts/generate-tests.mjs listening "Đặt vé tàu"
node scripts/generate-tests.mjs writing "Làm việc từ xa"
node scripts/generate-tests.mjs speaking "Thể thao"
Script dùng chính GEMINI_API_KEY ở trên, tự đánh id tiếp theo, tự kiểm tra số thứ
tự câu hỏi và tính hợp lệ của biểu đồ.
data/writing/tests.json / data/speaking/tests.json, dùng được ngaydata/reading/rt-03.json hoặc data/listening/lt-03.json; registry hiện đã ghép các đề sinh ngắn thành đúng 3 passage / 4 section cho các đề mới.import rt03 from "@/data/reading/rt-03.json";
export const READING_TESTS = [rt01, rt02, rt03] as unknown as ReadingTest[];
Với Listening, chạy thêm npm run gen:audio để tạo file MP3 cho đề mới.
Luôn đọc lại đề AI sinh ra trước khi cho học viên dùng — AI thỉnh thoảng tạo câu hỏi mà đáp án không thực sự có trong bài.
Cách 2 — tự viết tay
Mở một file trong data/ làm mẫu và copy cấu trúc. Toàn bộ kiểu dữ liệu được mô tả
trong lib/types.ts.
Repo GitHub của dự án: https://github.com/HoangKha1810/English-AI
Vercel tự nhận diện Next.js từ package.json, nên không cần thêm adapter hoặc
vercel.json. Dự án yêu cầu Node.js 20.9+.
HoangKha1810/English-AI.env.local.exampleKhông commit .env.local. File này chứa API key và đã được .gitignore loại khỏi
repo. Dùng .env.local.example làm danh sách biến cần nhập vào Vercel:
NEXT_PUBLIC_FIREBASE_API_KEYNEXT_PUBLIC_FIREBASE_AUTH_DOMAINNEXT_PUBLIC_FIREBASE_PROJECT_IDNEXT_PUBLIC_FIREBASE_STORAGE_BUCKETNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_IDNEXT_PUBLIC_FIREBASE_APP_IDGEMINI_API_KEYGEMINI_MODELGROQ_API_KEYGOOGLE_SITE_VERIFICATION (chỉ cần nếu xác minh Search Console bằng HTML tag)Nên thêm các biến cho cả Production, Preview và Development nếu bạn muốn các branch/preview của Vercel có đầy đủ chức năng.
git add .
git commit -m "Mô tả thay đổi"
git push
Bước bắt buộc sau khi deploy: quay lại Firebase Console →
Authentication → Settings → Authorized domains → Add domain → dán tên miền
Vercel (ten-du-an.vercel.app). Không làm bước này thì nút đăng nhập Google sẽ báo
lỗi auth/unauthorized-domain.
Mỗi lần git push sau đó Vercel sẽ tự deploy lại.
Các route SEO đã có sẵn:
https://english.khanhportfolio.info/robots.txthttps://english.khanhportfolio.info/sitemap.xmlhttps://english.khanhportfolio.info/og-image.pngSau khi deploy:
https://english.khanhportfolio.info/content trong thẻ google-site-verificationGOOGLE_SITE_VERIFICATION trên Vercelsitemap.xmlNếu chọn xác minh bằng DNS, không cần biến GOOGLE_SITE_VERIFICATION; chỉ cần
thêm bản ghi TXT mà Google cung cấp vào nơi quản lý DNS của domain.
Preview khi gửi link dùng Open Graph title, description và ảnh thương hiệu. Các ứng dụng chat có thể lưu cache link cũ; sau khi deploy hãy gửi lại link hoặc dùng trình debug chia sẻ của nền tảng đó để yêu cầu đọc lại metadata.
app/
page.tsx trang chủ và workspace tiến độ
ielts/[id]/ đề IELTS full gồm đủ 4 kỹ năng
ielts/ danh sách 10 đề full
login/ đăng nhập / đăng ký
dashboard/ tiến độ, biểu đồ, lịch sử
reading/[id]/ giao diện làm bài Reading
listening/[id]/ giao diện làm bài Listening
writing/[id]/ giao diện viết bài
speaking/[id]/ giao diện ghi âm
api/
score-writing/ gọi Gemini chấm Writing
score-speaking/ gọi Gemini chấm Speaking
transcribe/ gọi Groq Whisper chuyển giọng nói thành văn bản
components/
ui/ Button, GlassCard, Badge, BandRing, ProgressBar
exam/ engine làm bài dùng chung cho Reading và Listening
ChartRenderer.tsx vẽ biểu đồ Writing Task 1 (cột / đường / tròn / bảng)
ProgressChart.tsx biểu đồ band theo thời gian
lib/
types.ts toàn bộ kiểu dữ liệu đề thi
band.ts bảng quy đổi điểm thô sang band IELTS
grade.ts chấm Reading / Listening
firebase.ts, storage.ts Firebase + dự phòng localStorage
gemini.ts gọi Gemini ở chế độ JSON có schema
progress.ts lưu bài dở và khôi phục trạng thái làm bài
data/ toàn bộ đề thi ở dạng JSON
scripts/ sinh đề bằng AI, tạo audio bằng edge-tts
firestore.rules quy tắc bảo mật Firestore
Về bản quyền. Toàn bộ bài đọc, transcript, đề viết và câu hỏi speaking trong
data/ đều là nội dung gốc được biên soạn riêng cho dự án này. Đừng chép đề từ
bộ Cambridge IELTS vào — đó là vi phạm bản quyền và là lý do nhiều website luyện thi
bị gỡ. IELTS là nhãn hiệu của British Council, IDP và Cambridge; dự án này không
liên kết với các tổ chức đó.
Về độ chính xác của điểm. Bảng quy đổi band trong lib/band.ts lấy theo bảng
công bố trong bộ Cambridge IELTS, đề thi thật có thể xê dịch ±0.5. Điểm Writing và
Speaking do AI chấm chỉ mang tính tham khảo để biết mình đang yếu ở tiêu chí nào —
không thay thế được giám khảo thật. Riêng Pronunciation, AI chỉ đọc transcript chứ
không nghe được âm thanh, nên phần đó là ước lượng gián tiếp.
Muốn chấm phát âm chính xác hơn? Dùng Azure Pronunciation Assessment — chấm đến từng âm tiết (Accuracy, Fluency, Prosody), có tier miễn phí F0 vĩnh viễn (đăng ký Azure cần thẻ nhưng F0 không bị tính tiền).
Chi phí thực tế. Với vài trăm người dùng hoạt động, toàn bộ dự án chạy ở mức
0 đồng. Mẹo để giữ như vậy: sinh sẵn đề bằng script rồi lưu vào data/, đừng gọi
LLM real-time mỗi lần có người vào làm bài — chỉ dùng AI cho khâu chấm Writing và
Speaking.