20 KiB
Proposal Refactoring: LMS Belajar Pemrograman C
Tanggal: 24 Maret 2026
Project: LMS-C (Learning Management System untuk Pemrograman C)
Workspace: elemes/
1. Ringkasan Eksekutif
Proposal ini mengusulkan refactoring LMS-C dari arsitektur Flask monolith + Jinja2 templates menjadi arsitektur Flask API + SvelteKit PWA. Tujuannya adalah memberikan pengalaman belajar yang optimal di desktop dan mobile dengan code editor yang proper, performa cepat, dan kemampuan instalasi sebagai aplikasi (PWA).
2. Kondisi Saat Ini
Arsitektur
- Backend: Flask monolith (
app.py, 982 baris) — semua logic dalam satu file - Frontend: Jinja2 templates + vanilla JavaScript + Bootstrap 5
- Code Editor: Plain textarea dengan line numbers manual (bukan code editor sesungguhnya)
- Compiler: Factory pattern — CCompiler (gcc) dan PythonCompiler
- Auth: Token-based via CSV file (
tokens_siswa.csv) - Deploy: Podman container + Tailscale sidecar untuk HTTPS
- Flutter app: 0% — hanya folder kosong, tidak ada kode sama sekali
Masalah Utama
- Monolith 982 baris — sulit di-maintain dan di-test
- Bukan code editor — textarea biasa, tidak ada syntax highlighting saat mengetik, bracket matching, atau auto-indent
- Tidak mobile-friendly — code editor tidak bisa dipakai di mobile
- Tidak bisa offline — tidak ada caching mekanisme
- Vanilla JS — duplikasi kode di setiap template (login logic di-copy paste)
- Tidak installable — harus buka browser setiap kali
3. Evaluasi Framework
3.1 Flutter Web + Mobile
| Aspek | Evaluasi |
|---|---|
| Rendering | CanvasKit — render ke HTML canvas, BUKAN native DOM |
| Code Editor | Tidak ada widget setara CodeMirror/Monaco. code_text_field sangat terbatas |
| Bundle Size | 2-4 MB initial download (CanvasKit engine) |
| Mobile | Excellent untuk native app, tapi web version bermasalah |
| Accessibility | Buruk — canvas rendering, tidak bisa "Find in Page", screen reader tidak jalan |
| SEO | Tidak ada — semua di-render di canvas |
| Markdown | Ecosystem terbatas untuk rendering markdown |
Verdict: ❌ Tidak cocok untuk LMS yang content-heavy dengan code editor sebagai fitur utama.
Alasan utama penolakan: Flutter Web me-render SELURUH UI ke HTML <canvas>. Ini berarti:
- Siswa tidak bisa copy-paste text dari lesson content secara natural
- Browser "Find in Page" (Ctrl+F) tidak berfungsi
- Code editor di Flutter tidak bisa match fitur CodeMirror 6 (syntax highlighting, bracket matching, multiple cursors, mobile keyboard support)
- Bundle 2-4 MB sangat berat untuk siswa dengan koneksi mobile
3.2 Next.js (React) PWA
| Aspek | Evaluasi |
|---|---|
| Rendering | Native DOM + SSR/SSG |
| Code Editor | Monaco Editor (VS Code engine) — sangat powerful tapi 5MB+ |
| Bundle Size | 200-400 KB base, 5MB+ dengan Monaco |
| Mobile | Baik, tapi Monaco Editor tidak optimal di mobile |
| Ecosystem | Sangat besar, banyak library |
| Complexity | Lebih kompleks dari Svelte (virtual DOM, hooks, etc.) |
Verdict: ⚠️ Bisa dipakai, tapi overkill dan Monaco terlalu berat.
3.3 SvelteKit PWA (REKOMENDASI)
| Aspek | Evaluasi |
|---|---|
| Rendering | Native DOM + SSR, compile-time (no virtual DOM) |
| Code Editor | CodeMirror 6 — production-grade, mobile-friendly, 150KB |
| Bundle Size | 50-100 KB base + 150KB CodeMirror = ~250KB total |
| Mobile | Excellent — native HTML/CSS responsive, CodeMirror 6 support touch |
| PWA | Built-in via @vite-pwa/sveltekit |
| SSR | Built-in — fast first paint untuk konten markdown |
| Learning Curve | Minimal — Svelte lebih sederhana dari React |
| TypeScript | Native support |
Verdict: ✅ Pilihan terbaik untuk project ini.
3.4 Flask + HTMX/Alpine.js
| Aspek | Evaluasi |
|---|---|
| Perubahan | Minimal dari arsitektur sekarang |
| Code Editor | Bisa pakai CodeMirror, tapi integrasi manual |
| Mobile | Terbatas — tetap server-rendered, tidak ada PWA |
| Offline | Tidak ada |
Verdict: ⚠️ Perbaikan inkremental, bukan refactor sebenarnya.
Tabel Perbandingan
| Kriteria | Flutter Web | Next.js | SvelteKit | Flask+HTMX |
|---|---|---|---|---|
| Code editor quality | 2/10 | 9/10 | 9/10 | 5/10 |
| Bundle size | 2/10 | 5/10 | 9/10 | 10/10 |
| Mobile experience | 4/10 | 7/10 | 9/10 | 4/10 |
| PWA support | 3/10 | 7/10 | 9/10 | 2/10 |
| SSR / first paint | 2/10 | 9/10 | 9/10 | 8/10 |
| Complexity | 6/10 | 5/10 | 8/10 | 9/10 |
| Markdown rendering | 4/10 | 8/10 | 9/10 | 8/10 |
| Total | 23/70 | 50/70 | 62/70 | 46/70 |
4. Arsitektur yang Diusulkan
4.1 Overview
┌─────────────────────────────┐
│ Internet (HTTPS) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ Tailscale Funnel (:443) │
│ (elemes-ts container) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
│ SvelteKit Frontend (:3000) │
│ - SSR pages │
│ - CodeMirror 6 editor │
│ - PWA + Service Worker │
│ - Responsive UI │
└──────────────┬──────────────┘
│ /api/*
┌──────────────▼──────────────┐
│ Flask API Backend (:5000) │
│ - Code compilation (gcc) │
│ - Token authentication │
│ - Progress tracking (CSV) │
│ - Lesson content parsing │
└─────────────────────────────┘
4.2 Tiga Container
| Container | Base Image | Port | Fungsi |
|---|---|---|---|
elemes-frontend |
Node 20 slim | 3000 | SvelteKit SSR, static assets, PWA |
elemes-api |
Python 3.11 slim + gcc | 5000 | Flask JSON API, compilation, auth |
elemes-ts |
Tailscale | 443 | HTTPS termination, Funnel |
4.3 Alur Data
Siswa buka browser
→ GET /lesson/hello_world
→ SvelteKit SSR: fetch /api/lesson/hello_world dari Flask
→ Flask: parse markdown, return JSON {lesson_html, initial_code, ...}
→ SvelteKit: render HTML + hydrate CodeMirror editor
→ Siswa tulis kode C di CodeMirror
→ Klik "Run"
→ POST /api/compile {code: "..."}
→ Flask: write temp file → gcc compile → run → return output
→ SvelteKit: tampilkan output di OutputPanel
→ Jika benar → POST /api/track-progress
→ Flask: update CSV file
5. Struktur Direktori
lms-c/ # Root — konten saja
├── content/ # 25 lesson markdown
│ ├── home.md
│ ├── hello_world.md
│ ├── variables.md
│ └── ... (25 file)
├── assets/ # Gambar untuk lesson
├── tokens_siswa.csv # Data siswa & progress
├── config/
│ └── sinau-c-tail.json # Tailscale config
├── state/ # Runtime state (certs, logs)
└── .env # Environment variables
elemes/ # Semua kode aplikasi
├── documentation.md # Dokumen ini
│
├── api/ # ── Flask API Backend ──
│ ├── app.py # create_app() factory
│ ├── config.py # Environment config
│ ├── requirements.txt # Python dependencies
│ ├── Dockerfile
│ ├── gunicorn.conf.py
│ ├── compiler/ # Compiler module (tidak berubah)
│ │ ├── __init__.py # CompilerFactory
│ │ ├── base_compiler.py # Abstract base
│ │ ├── c_compiler.py # gcc wrapper
│ │ └── python_compiler.py # python wrapper
│ ├── routes/ # Flask Blueprints (JSON only)
│ │ ├── auth.py # /api/login, logout, validate-token
│ │ ├── compile.py # /api/compile
│ │ ├── lessons.py # /api/lessons, /api/lesson/<slug>
│ │ └── progress.py # /api/track-progress, progress-report
│ ├── services/ # Business logic
│ │ ├── token_service.py # CSV token operations
│ │ └── lesson_service.py # Markdown parsing
│ └── tests/ # pytest
│ ├── test_auth.py
│ ├── test_compile.py
│ ├── test_lessons.py
│ └── test_progress.py
│
├── frontend/ # ── SvelteKit PWA Frontend ──
│ ├── package.json
│ ├── svelte.config.js # adapter-node
│ ├── vite.config.ts # PWA plugin + API proxy
│ ├── Dockerfile
│ └── src/
│ ├── app.html # Root HTML shell
│ ├── app.css # Global styles
│ ├── lib/
│ │ ├── components/
│ │ │ ├── CodeEditor.svelte # CodeMirror 6 (KRITIS)
│ │ │ ├── Navbar.svelte # Navigation + auth
│ │ │ ├── LessonCard.svelte # Card lesson
│ │ │ ├── OutputPanel.svelte # Output kompilasi
│ │ │ ├── ProgressBadge.svelte # Badge status
│ │ │ └── Footer.svelte
│ │ ├── stores/
│ │ │ ├── auth.ts # Token state
│ │ │ └── theme.ts # Dark/light
│ │ ├── services/
│ │ │ └── api.ts # Flask API client
│ │ └── types/
│ │ ├── lesson.ts
│ │ ├── auth.ts
│ │ └── compiler.ts
│ ├── routes/
│ │ ├── +layout.svelte # Navbar + Footer
│ │ ├── +page.svelte # Home (lesson grid)
│ │ ├── lesson/[slug]/
│ │ │ └── +page.svelte # Lesson viewer
│ │ └── progress/
│ │ └── +page.svelte # Teacher dashboard
│ └── static/
│ ├── manifest.json # PWA manifest
│ └── icons/ # App icons
│
└── podman-compose.yml # 3 services
6. Komponen Kritis: Code Editor
CodeMirror 6 adalah code editor web paling mature yang support mobile. Berikut fitur yang akan diimplementasi:
Fitur Editor
- Syntax highlighting — C language mode (
@codemirror/lang-cpp) - Line numbers — gutter di sisi kiri
- Bracket matching — highlight bracket pasangan
- Auto-close brackets — otomatis tutup
{,(,[ - Dark/Light theme — toggle dengan
@codemirror/theme-one-dark - Undo/Redo — Ctrl+Z / Ctrl+Shift+Z
- Tab handling — insert 4 spasi (bukan tab karakter)
- Ctrl+Enter — shortcut untuk Run
- Copy-paste prevention — bisa diaktifkan per-lesson (configurable)
- Mobile keyboard support — CodeMirror 6 handle virtual keyboard natively
Perbandingan dengan Editor Saat Ini
| Fitur | Saat Ini (textarea) | SvelteKit (CodeMirror 6) |
|---|---|---|
| Syntax highlighting saat mengetik | ❌ | ✅ |
| Auto-indent | ❌ | ✅ |
| Bracket matching | ❌ | ✅ |
| Auto-close brackets | ❌ | ✅ |
| Undo/Redo | Basic browser | ✅ Full history |
| Mobile keyboard | Problematic | ✅ Native support |
| Dark/Light theme | Partial | ✅ Full themes |
| Find & Replace | ❌ | ✅ Ctrl+F |
| Multiple cursors | ❌ | ✅ |
7. Mobile Strategy: PWA
Apa itu PWA?
Progressive Web App memungkinkan web app di-install seperti native app di mobile dan desktop, tanpa harus publish ke App Store / Play Store.
Fitur PWA yang diimplementasi
- Add to Home Screen — siswa bisa install dari browser
- Standalone mode — berjalan tanpa browser chrome (address bar hilang)
- Splash screen — branding saat app loading
- Offline reading — lesson content di-cache oleh service worker
- Auto-update — service worker update otomatis saat ada versi baru
Responsive Design
| Viewport | Layout |
|---|---|
| Desktop (>1024px) | 2 kolom: lesson content (8/12) + sidebar (4/12) |
| Tablet (768-1023px) | 2 kolom: lesson content (7/12) + sidebar (5/12) |
| Mobile (<768px) | 1 kolom, sidebar jadi accordion expandable |
Code Editor di Mobile
- Default height 200px dengan tombol expand
- CodeMirror 6 handle virtual keyboard secara native
- Touch-friendly buttons (target area lebih besar)
- Auto-scroll ke cursor saat virtual keyboard muncul
8. Tailscale Integration
Arsitektur Deployment
Tailscale Network
│
┌────────────────▼────────────────┐
│ elemes-ts (Tailscale container) │
│ hostname: sinau-c-dev │
│ HTTPS :443 (Funnel) │
│ │ │
│ ▼ proxy │
│ elemes-frontend :3000 │
│ (SvelteKit) │
│ │ │
│ ▼ /api/* │
│ elemes-api :5000 │
│ (Flask + gcc) │
└─────────────────────────────────┘
Akses Development
- Internal:
http://localhost:3000(langsung ke SvelteKit) - External via Tailscale:
https://sinau-c-dev.<tailnet>.ts.net - Funnel (public): Bisa diaktifkan untuk akses tanpa Tailscale client
Perubahan dari Setup Saat Ini
- Tailscale proxy target berubah dari Flask (:5000) ke SvelteKit (:3000)
- SvelteKit yang meng-handle semua request client, lalu proxy
/api/*ke Flask internal
9. Fase Implementasi
Phase 0: Backend Decomposition
Tujuan: Pecah Flask monolith menjadi modular API
- Buat struktur
elemes/api/ - Extract services:
lesson_service.py,token_service.py - Extract routes ke Blueprints:
auth.py,compile.py,lessons.py,progress.py - Rewrite
app.pysebagai factory function - Tambah
flask-cors - Write unit tests
Output: Flask API yang return JSON, siap dikonsumsi frontend
Phase 1: SvelteKit Scaffolding
Tujuan: Setup project SvelteKit dengan dependencies
- Initialize project dengan TypeScript
- Install CodeMirror 6, PWA plugin, marked
- Configure adapter-node dan API proxy
Phase 2: Core Components
Tujuan: Build komponen reusable
- Auth store + API service
- CodeEditor (CodeMirror 6)
- Navbar, Footer, LessonCard, OutputPanel
Phase 3: Pages
Tujuan: Build semua halaman
- Home page (lesson grid + SSR)
- Lesson page (content + editor + output)
- Progress report (teacher dashboard)
Phase 4: Mobile + PWA
Tujuan: Optimasi mobile dan installability
- Responsive CSS
- PWA manifest + service worker
- Offline lesson caching
- Mobile-optimized editor
Phase 5: Containerization + Tailscale
Tujuan: Deploy ke containers
- Frontend Dockerfile (Node 20)
- API Dockerfile (Python 3.11 + gcc)
- Update podman-compose.yml (3 services)
- Update Tailscale config
Phase 6: Cleanup
Tujuan: Hapus kode lama
- Remove Jinja2 templates
- Remove vanilla JS
- Remove flutter_app/ placeholder
- Remove flask-talisman (CSP di SvelteKit)
10. Stack Teknologi
Backend (Flask API)
| Teknologi | Versi | Fungsi |
|---|---|---|
| Python | 3.11 | Runtime |
| Flask | 2.3+ | Web framework (API only) |
| Flask-CORS | latest | Cross-origin untuk SvelteKit |
| Gunicorn | 21.2 | Production WSGI server |
| gcc | system | C compiler |
| python-markdown | 3.5 | Markdown → HTML |
Frontend (SvelteKit)
| Teknologi | Versi | Fungsi |
|---|---|---|
| Node.js | 20 LTS | Runtime |
| SvelteKit | 2.x | Full-stack framework |
| Svelte | 5.x | UI framework |
| TypeScript | 5.x | Type safety |
| CodeMirror 6 | 6.x | Code editor |
| @vite-pwa/sveltekit | latest | PWA support |
| marked | latest | Client-side markdown (jika perlu) |
| highlight.js | 11.x | Syntax highlighting di konten |
Infrastructure
| Teknologi | Fungsi |
|---|---|
| Podman | Container runtime |
| Podman Compose | Container orchestration |
| Tailscale | VPN + HTTPS (Funnel) |
11. API Endpoints
Semua endpoint Flask akan di-prefix dengan /api/:
Authentication
| Method | Endpoint | Request | Response |
|---|---|---|---|
| POST | /api/login |
{token} |
{success, student_name} |
| POST | /api/logout |
— | {success} |
| POST | /api/validate-token |
{token} |
{success, student_name} |
Lessons
| Method | Endpoint | Response |
|---|---|---|
| GET | /api/lessons |
[{title, filename, description, completed}] |
| GET | /api/lesson/<slug> |
{lesson_html, exercise_html, initial_code, solution_code, expected_output, key_text, title, prev, next} |
| GET | /api/key-text/<slug> |
{key_text} |
Compilation
| Method | Endpoint | Request | Response |
|---|---|---|---|
| POST | /api/compile |
{code, language?} |
{success, output, error} |
Progress
| Method | Endpoint | Request/Params | Response |
|---|---|---|---|
| POST | /api/track-progress |
{token, lesson_name} |
{success} |
| GET | /api/progress-report |
?token=xxx |
{students: [{name, progress: {}}]} |
| GET | /api/export-csv |
?token=xxx |
CSV file download |
12. Verifikasi
Testing Strategy
- Backend: pytest di dalam container (
podman exec elemes-api pytest -v) - Frontend unit: Vitest (
npm run test) - Frontend E2E: Playwright (
npm run test:e2e) - Manual: Cek di desktop dan mobile viewport
Checklist Verifikasi
- Home page menampilkan 25 lesson cards
- Login dengan token berhasil
- Lesson page menampilkan konten markdown
- Code editor berfungsi (syntax highlighting, line numbers)
- Run code → output ditampilkan
- Kode benar → progress ter-track
- Progress report menampilkan data siswa
- Export CSV berfungsi
- Responsive di mobile (375x667)
- PWA installable
- Tailscale Funnel accessible
13. Risiko dan Mitigasi
| Risiko | Dampak | Mitigasi |
|---|---|---|
| CodeMirror 6 bundle size di mobile | Slow initial load | Lazy-load CodeMirror hanya di lesson page |
| Flask API downtime saat refactor | Siswa tidak bisa belajar | Parallel running: old templates tetap jalan |
| Markdown parsing perbedaan Python vs JS | Rendering berbeda | Flask tetap parse markdown (single source of truth) |
| Tailscale config berubah | Akses terputus | Backup config lama, rollback plan ready |
Dokumen ini adalah proposal. Implementasi dimulai setelah disetujui.