Commit Graph

27 Commits

Author SHA1 Message Date
opencode d7c845266c feat(assets): optimasi serving gambar (WebP on-the-fly + cache disk)
Arsitektur: upload tetap menyimpan original apa adanya; optimasi terjadi
saat serving — gambar raster di-resize (maks 1600px) + encode WebP
(quality 80), di-generate on-the-fly lalu di-cache di disk.

- config.CACHE_DIR = sibling ASSETS_DIR ('assets-cache'), WAJIB di luar
  ASSETS_DIR supaya export_bundle (os.walk tanpa filter) tetap lossless
- services/image_cache.py: varian WebP, cache key path+mtime+size+width,
  invalidasi otomatis via mtime, atomic write, fallback original bila gagal
- routes/lessons.py::send_assets: handler (bukan static serve) —
  .png/.jpg/.jpeg/.webp/.gif -> varian WebP; .svg & GIF animasi pass-through;
  ?orig=1 untuk original; ?w= untuk lebar alternatif; ETag + Cache-Control
- Dockerfile: buat /app/assets-cache writable

Test (services/tests/test_asset_cache.py, 20 test): lokasi cache di luar
ASSETS_DIR, cache reuse, invalidasi mtime, passthrough SVG/GIF animasi,
fallback file korup, ETag/304, dan acceptance test — upload -> request ->
export bundle berisi HANYA original tanpa .webp turunan.
2026-10-25 19:19:07 +07:00
a2nr 087d338477 feat(quiz): izinkan guru melanjutkan attempt terminated dari jawaban tersimpan
Kebijakan termination tetap strict, tapi guru pegang override untuk kendala
tak terduga (false-positive blur dari dialog OS, browser crash, refresh
tak sengaja). Selain reset (siswa mengulang dari nol), guru kini bisa
memberi izin resume — siswa melanjutkan dari jawaban yang sudah tersimpan.

- Tambah kolom quiz_attempts.resumed_at (migrasi 0006) sebagai penanda izin
  guru melanjutkan attempt terminated
- Tambah endpoint teacher-only POST /resume-quiz-attempt — kontrak keamanan
  sama dengan /reset-progress (guru cukup student_id anonim, tanpa token siswa)
- Syarat: hanya attempt ber-status terminated yang bisa di-resume; submitted
  ditolak karena kuis sudah selesai normal
- has_quiz_attempt meloloskan attempt ber-izin sehingga gate one-attempt
  terbuka selama sesi lanjutan
- finalize_quiz_attempt menimpa row attempt sekali (status/skor/answers baru)
  lalu clear resumed_at; retry berikutnya kembali ke jalur idempotent normal
- Sinyal ke frontend siswa lewat quiz_resumable pada data lesson; laporan guru
  menampilkan badge "Dilanjutkan" dan tombol "Lanjutkan" untuk attempt
  terminated
- Frontend: restoreQuizSession() merekonstruksi sesi dari jawaban tersimpan per
  question_id, is_correct dihitung ulang dari source sebagai guard bila
  konten soal berubah; option_id tak dikenal dianggap belum dijawab
- Frontend: firstUnansweredIndex() menentukan soal awal sesi lanjutan;
  resolveQuizTabView() memusatkan keputusan view agar transisi resume dapat
  diuji Vitest
- UI siswa: panel "Lanjutkan Kuis" + snackbar mode lanjutan (sekali muncul).
  Attempt di-refetch segar sebelum sesi dimulai agar izin yang sudah dicabut
  guru tidak memulai sesi yang jawabannya akan terbuang
- Test: 16 integrasi backend (services/tests/test_resume_quiz.py), 9 unit
  quiz-session, 10 unit quiz-view
- Docs: alur resume + keterbatasan di 12-quiz-integrity.md, ringkasan di
  07-quiz-authoring.md, kontrak endpoint di 02-backend-flask.md
2026-10-20 19:24:40 +07:00
a2nr bb8ca50650 fix(upload): review — CSP SVG, boundary 5 MiB, regression guard 502/413
Hasil review pasca-fix upload asset:

Keamanan:
- Mitigasi stored-XSS via SVG (route /assets/* publik, same-origin):
  CSP 'default-src none; sandbox' saat serve SVG — di backend
  (send_assets) DAN di proxy SvelteKit (proxy hanya meneruskan
  content-type/set-cookie, jadi CSP backend tak sampai ke browser).
- Deteksi 413 proxy kini ber-test status code + pesan.

Testing:
- Regression guard: error jaringan biasa tetap 502, bukan 413.
- 413 via status code saja (tanpa regex pesan) tetap 413.
- Boundary 5 MiB: file TEPAT 5 MiB lolos; 5 MiB+1 ditolak (cabang
  len(raw) — SVG dipakai karena dikecualikan validasi PIL).
- CSP: SVG dapat header, raster tidak.
- E2E script (verify_asset_upload.py) memakai file asli
  mr-edu/assets/ilustrasi_beban.jpeg + test 413 terintegrasi.

Reuseability:
- Komentar silang-referensi _MAX_ASSET_BYTES (sumber) dengan pesan 413
  proxy dan BODY_SIZE_LIMIT compose.
2026-10-03 08:06:29 +07:00
a2nr 1f4217be76 fix(quiz): enforce 75% passing threshold, sync server progress, and persist attempt review
- Only show celebration overlay and top bar completed checkmark if quiz score meets 75% threshold (>= 0.75) and exercises are done
- Return is_done, state, and composite_percent in POST /lesson-progress for exercise and quiz submissions
- Synchronize frontend LessonManager state with authoritative server is_done response
- Add has_quiz_attempt check in routes/lessons.py and token_service to recognize existing attempts even when composite state is in_progress
- Update QuizTab to display persistent attempt review, score status, and explanation instead of retake prompt on page load/refresh
- Make QuizQuestionView finish handler async, await finishQuiz(), and disable submission buttons to prevent navigation race conditions
- Update local ordered_lessons completion state dynamically upon passing
- Add unit and integration tests covering response payload attributes and has_quiz_attempt API field
- Bump APP_VERSION to 30
2026-09-09 07:33:52 +07:00
a2nr a157a31b78 fix(lesson): no-store cache header pada response lesson JSON
Mencegah browser/SW/proxy menyajikan lesson (termasuk quiz_data) dari
cache stale, terutama saat file lesson diubah.
2026-08-30 10:55:22 +07:00
a2nr ac10692b61 feat(prereq): dukung ambang minimum (min: N%) pada prasyarat materi
Tambahkan logika ambang minimum skor (opsional) di daftar prasyarat.
Tanpa ambang, status scored berapa pun (termasuk 0/x) memenuhi syarat
(backward compatible). Dengan ambang `min: N%`, earned/total harus
melebihi ambang untuk membuka kunci materi berikutnya.

Perubahan:
- Parser bullet prasyarat mendukung suffix `min: N%` (contoh: min: 75%)
- is_prerequisite_met: completed → selalu terpenuhi; scored → terpenuhi
  kecuali di bawah ambang; not_started → belum terpenuhi
- routes/lessons.py: 3 titik kunci sekarang pakai get_missing_prerequisites
- latex_test.md: tambahkan prereq quiz_test min: 75% (demo nyata)
- examples/content disinkronkan dengan content
- Tambah test_prerequisite_logic.py (28 unit test) + 3 integration test
2026-08-17 21:57:10 +07:00
a2nr 432a8c2eca fix: show lesson progress on sub-home /bab pages
The /bab/<folder> endpoint returned raw get_sub_home_data() output, so
lessons had no completed/locked fields and green checkmarks never
appeared. api_bab now reads the student token, injects progress via
get_ordered_lessons_with_learning_objectives (scoped to the folder's
sub-home.md), and computes lock status from prerequisites — matching
/lessons. The bab page load also forwards the student token, and tests
cover progress injection and prerequisite locking.
2026-08-14 05:23:50 +07:00
a2nr 67ed58a327 feat: scope sub-home lesson lists & make sub-home cache mtime-aware
Lengkapi rencana sub-home (fitur bab sudah ada sejak 2f689af) dengan
perilaku sidebar/navigasi yang akurat per sub-folder:

- get_lessons()/get_ordered_lessons_with_learning_objectives() menerima
  source_path opsional sehingga daftar materi dapat diambil dari
  sub-home.md alih-alih home.md root
- api_lesson: ordered_lessons + prev/next di-scope ke sub-home.md bila
  folder memilikinya, fallback ke daftar global home.md
- get_sub_home_data kini mtime-cached (bukan lru_cache) sehingga edit
  sub-home.md langsung terlihat tanpa restart; edit juga meng-invalidate
  find_lesson_file agar materi baru terdeteksi

Testing & docs:
- test_sub_home.py (unit): _read_md_cached mtime, find_sub_home_for_lesson,
  get_sub_home_data refresh, ordered_lessons via source_path
- test_sub_home_api.py (integration): GET /bab/<folder> + scoped
  ordered_lessons di /lesson/<slug>.json
- docs/13-content-sub-home.md (baru), README + docs/02-backend-flask.md
  diperbarui dengan catatan fallback
- elemes.sh: perintah `test` + verifikasi tes sub-home di runclearbuild
2026-08-13 20:08:50 +07:00
a2nr 2f689afc27 feat: implement sub-home/bab feature
- Generalize _read_md_cached() for generic file caching
- Add find_sub_home_for_lesson() and get_sub_home_data() helpers
- Add /api/bab/<folder> endpoint
- Detect sub-home in api_lesson for nav context
- Skip sub-home.md in find_lesson_file() and _parse_lesson_links()
- Add title to bab frontend page data
- Update generate_tokens.py & content_parser.py to skip sub-home.md
2026-07-27 22:01:01 +07:00
a2nr 8e89fde3c1 fix: docked editor panel height + quiz image parser + assets dir config
- fix(layout): editor-area docked mode sekarang height:70vh (sebelumnya
  max-height:85vh tanpa height, menyebabkan tab Arduino/velxio menciut
  ke 150px karena iframe tidak punya intrinsic height)
- fix(layout): tambah overflow:hidden pada velxio/flowchart/quiz panel
  untuk mencegah nested scrollable flex collapse
- feat(quiz): parser image dari 'image:' directive dan markdown ![alt](path)
  di question flashcard; konversi bare filename ke /assets/ path
- feat(assets): ASSETS_DIR derived dari CONTENT_DIR untuk support
  custom content directory (bukan hardcoded 'assets')
- fix(quiz): QuizTab container gunakan flex:1+min-height:0 (bukan height:100%)
- chore: hapus placeholder examples/assets/put_your_image.here
2026-07-21 20:50:01 +07:00
a2nr bc49877123 feat: update to support folder inside content folder to easy to manage 2026-05-14 19:45:10 +07:00
a2nr 58e897f743 feat: add markdown-based interactive slide carousel with fullscreen mode 2026-05-14 18:53:46 +07:00
a2nr ea3556d05f feat: implement interactive quiz with secure content protection, scoring, and teacher reset 2026-05-09 13:04:42 +07:00
a2nr 7e77748b6b feat: add quiz feature 2026-05-08 09:50:07 +07:00
a2nr 9ed207d2fa feat: implement secured lesson prerequisite system with interactive links and auto-docking UI 2026-05-07 10:54:42 +07:00
a2nr 433c095394 feat(flowchart): implement text-based logic with auto-layout and migrate evaluation to frontend 2026-05-02 18:47:46 +07:00
a2nr 0ff56ed9d2 bug fix : leak content python_expected_output 2026-05-01 15:32:55 +07:00
a2nr c5348a9166 add feature: flowchart maker 2026-05-01 09:09:45 +07:00
a2nr a41711fb54 security: implement gVisor sandboxing and harden API endpoints
- Menambahkan service 'compiler-worker' terpisah untuk isolasi eksekusi kode C/Python.
   - Mengintegrasikan gVisor (runsc) pada worker untuk mencegah RCE pada level kernel.
   - Menggunakan Gunicorn (4 workers) pada compiler-worker untuk mendukung concurrency.
   - Menambahkan otentikasi token wajib pada endpoint /compile dan laporan progres.
   - Memperketat CORS policy menggunakan environment variable ORIGIN.
   - Menerapkan secure_filename pada rute pelajaran untuk mencegah Path Traversal.
   - Mengubah volume mounting backend utama menjadi Read-Only (:ro) untuk perlindungan data.
   - Memperbarui proposal.md dan .env.example dengan standar keamanan terbaru.
2026-04-21 15:13:16 +07:00
a2nr 7c069660f6 feat: add evaluation configuration support in lesson content and API 2026-04-16 11:12:23 +07:00
a2nr 997ab78f56 feat: improve evaluation system and update circuit editor to use localStorage for persistence 2026-04-10 14:00:35 +07:00
a2nr 74a8d87853 add velxio as submodule 2026-04-09 10:51:36 +07:00
a2nr 09827bf3ff fix python code 2026-04-01 12:56:27 +07:00
a2nr 3771f01110 feat: Add initial configuration files, example content, and lesson materials for programming and circuit in Elemes LMS 2026-04-01 09:37:09 +07:00
a2nr 1e6c6a884c feat: Enhance lesson functionality with circuit output and key text support, update token generation script for better CSV handling 2026-03-31 14:31:15 +07:00
a2nr 39d1b18c2a feat: integrate circuitjs1 simulator into frontend with new editor component and service worker support 2026-03-29 17:25:09 +07:00
a2nr d2b6d62b28 refactor to make it better 2026-03-25 09:39:51 +07:00