6.1 KiB
| title | order | category |
|---|---|---|
| Backend (Flask API) | 2 | backend |
02. Backend (Flask API)
The backend is built with Flask, providing API endpoints for the SvelteKit frontend to fetch lessons, track progress, manage authentication, and proxy compilation requests.
Application Factory (elemes/app.py)
def create_app():Initializes the Flask application, loads configuration fromelemes/config.py(which reads from.env), and registers the following blueprints:auth_bp(routes/auth.py)lessons_bp(routes/lessons.py)compile_bp(routes/compile.py)progress_bp(routes/progress.py)
Core API Routes
Authentication (routes/auth.py)
def login():(POST/login) Receivestokenin JSON payload. Validates viatoken_service.validate_token(). On success, sets thestudent_tokencookie. Rate-limited and includes a 1.5s tarpit for failures.def logout():(POST/logout) Clears thestudent_tokencookie.def validate_token_route():(POST/validate-token) Checks if the currentstudent_tokencookie is valid.
Lessons (routes/lessons.py)
def api_lessons():(GET/lessons) Returns a list of all lessons and the renderedhome.mdcontent vialesson_service.get_ordered_lessons_with_learning_objectives().def api_bab(folder):(GET/bab/<folder>) Returns parsedsub-home.mddata for a folder (title, intro HTML, lesson list) vialesson_service.get_sub_home_data(). Returns 404 when the folder has nosub-home.md.def api_lesson(filename):(GET/lesson/<slug>.json) Returns the fully parsed lesson data (content, initial code, circuits, key texts, active tabs) vialesson_service.render_markdown_content(filepath). When the lesson lives in a folder that has asub-home.md,ordered_lessons(sidebar) and prev/next navigation are scoped to that folder's lesson list; otherwise it falls back to the globalhome.mdlist.def get_key_text(filename):(GET/get-key-text/<slug>) Returns only the required keywords for a specific lesson without exposing the full content logic.
Compilation (routes/compile.py)
def compile_code():(POST/compile) Acceptscodeandlanguage. Routes execution to the sandboxed worker using theCompilerFactory. Incorporates rate-limiting for anonymous users.def velxio_compile():(POST/velxio-compilemapped from/velxio/api/compile) A proxy endpoint that forwards Arduino compilation requests to the Velxio container, enforcing rate limits for anonymous users.
Progress Tracking (routes/progress.py)
def track_progress():(POST/track-progress) Acceptslesson_nameandstatus. Persists progress to PostgreSQL viatoken_service.update_student_progress().def api_progress_report():(GET/progress-report.json) Returns a matrix of all student progress. Requires a teacher token.def export_progress_csv():(GET/progress-report/export-csv) Exports progress data as a CSV download. Requires a teacher token.
Services
Token Service (services/token_service.py)
Facade over the single active storage backend — PostgreSQL, via
services/storage/postgres_backend.py. The legacy CSV backend and its helpers
(initialize_tokens_file, _load_tokens_safely) have been removed.
def validate_token(token):Returns{'student_name', 'is_teacher'}orNonefor a valid token.def is_teacher_token(token):ReturnsTrueif the token belongs to the teacher account.def get_student_progress(token):Returns a dictionary of lesson progress for a specific token.def update_student_progress(token, lesson_name, status="completed"):Persists progress to PostgreSQL.def get_teacher_token():Returns the active teacher token (implemented inpostgres_backend).def reset_student_progress(student_id, lesson_name):Resets progress by anonymousstudent_id(never the raw token).
Student tokens are stored only as an HMAC-SHA256 digest in the
access_tokens table (peppered with TOKEN_PEPPER from .env) — raw tokens
are never persisted and cannot be recovered or exported.
Lesson Service (services/lesson_service.py)
Parses Markdown files to extract content and configuration.
def get_lessons(source_path=None):Returns lessons listed in theAvailable_Lessonssection ofhome.md(or ofsource_pathwhen given, e.g. asub-home.md).def get_ordered_lessons_with_learning_objectives(progress=None, source_path=None):Returns lessons ordered as they appear inhome.md(orsource_path), optionally injected with user progress status.def find_sub_home_for_lesson(file_path):Returns(sub_home_path, folder_name)when the lesson's folder (one level insidecontent/) has asub-home.md, else(None, None).def get_sub_home_path(folder_name):Returns the absolute path to a folder'ssub-home.md(orNone), using the sameCONTENT_DIRasget_sub_home_data.def get_sub_home_data(folder_name):Parses a folder'ssub-home.md(title, intro HTML, lesson list) with an mtime-based cache so edits to the file are picked up without restart.def render_markdown_content(file_path):The core parsing function. Uses regex to extract markers like---INITIAL_CODE---,---VELXIO_CIRCUIT---, etc. It identifies theactive_tabsneeded for the frontend.def _parse_flashcards(text):Specifically parses---QUIZ_FLASHCARD---blocks into a structured JSON array for the frontend MCQ/Flashcard component.
See docs/13-content-sub-home.md for the author-facing guide on writing sub-home.md.
Compiler Framework (compiler/)
The compilation logic is abstracted via a factory pattern.
class CompilerFactory:(compiler/__init__.py)def get_compiler(self, language):Returns the appropriateBaseCompilerinstance (e.g.,CCompilerorPythonCompiler).
class BaseCompiler(ABC):(compiler/base_compiler.py)def compile(self, code, timeout=10):Abstract method.def run(self, file_path, timeout=5):Abstract method.
class CCompiler(BaseCompiler):andclass PythonCompiler(BaseCompiler):Implementation wrappers that construct payloads and send HTTP requests to thecompiler-workercontainer (http://compiler-worker:8080/execute).