112 lines
3.4 KiB
Python
112 lines
3.4 KiB
Python
"""
|
|
Dokumentasi teknis dan API reference — Docs Viewer endpoints.
|
|
|
|
Endpoints:
|
|
GET /api/docs → index semua dokumen (terurut)
|
|
GET /api/docs/<slug> → rendered HTML + metadata satu dokumen
|
|
GET /api/docs/api-reference → API reference yang mengekstrak docstring route
|
|
"""
|
|
|
|
import re
|
|
|
|
from flask import Blueprint, current_app, jsonify
|
|
|
|
from services.docs_service import get_docs_index, get_doc_content
|
|
|
|
docs_bp = Blueprint("docs_api", __name__)
|
|
|
|
|
|
@docs_bp.route("/docs")
|
|
def api_docs_index():
|
|
"""List all docs files with metadata, sorted by order."""
|
|
docs = get_docs_index()
|
|
return jsonify({"docs": docs})
|
|
|
|
|
|
@docs_bp.route("/docs/<slug>")
|
|
def api_doc_detail(slug):
|
|
"""Return rendered HTML + metadata for a single doc by slug."""
|
|
data = get_doc_content(slug)
|
|
if data is None:
|
|
return jsonify({"error": "Dokumen tidak ditemukan"}), 404
|
|
return jsonify(data)
|
|
|
|
|
|
@docs_bp.route("/docs/api-reference")
|
|
def api_reference():
|
|
"""Generate API reference from route docstrings.
|
|
|
|
Scans all registered Flask routes, extracts docstrings, and formats
|
|
them as structured API reference entries.
|
|
|
|
Each entry: {method, path, name, auth, doc}
|
|
|
|
Uses ``current_app`` (bukan ``create_app()``) agar tidak membuat instance
|
|
app baru / memicu DB sync ulang pada setiap request.
|
|
"""
|
|
entries = []
|
|
|
|
# Known auth requirements per route (derived from code inspection)
|
|
auth_map = {
|
|
"/login": False,
|
|
"/validate-token": False,
|
|
"/track-progress": False, # token-based, not session auth
|
|
"/progress-report.json": True,
|
|
"/reset-progress": True,
|
|
"/compile": False, # token optional (anon allowed)
|
|
"/compile/sessions": False,
|
|
"/compile/sessions/<session_id>": False,
|
|
"/compile/sessions/<session_id>/input": False,
|
|
"/compile/sessions/<session_id>": False,
|
|
"/velxio-compile": False,
|
|
"/quiz-attempts/submit": False,
|
|
"/quiz-attempts/<lesson_name>": False,
|
|
"/students/export-csv": True,
|
|
"/students/import/preview": True,
|
|
"/students/import": True,
|
|
"/students/bulk-delete": True,
|
|
"/api/docs": False,
|
|
"/api/docs/<slug>": False,
|
|
"/api/docs/api-reference": False,
|
|
"/bab/<folder>": False,
|
|
"/lessons": False,
|
|
"/lesson/<path:filename>": False,
|
|
"/get-key-text/<filename>": False,
|
|
"/assets/<path:path>": False,
|
|
"/help": False,
|
|
"/help/asset/<path:filename>": False,
|
|
}
|
|
|
|
# Extract from Flask view functions directly
|
|
seen = set()
|
|
for rule in current_app.url_map.iter_rules():
|
|
if rule.endpoint == "static":
|
|
continue
|
|
path = rule.rule
|
|
if path in seen:
|
|
continue
|
|
seen.add(path)
|
|
|
|
methods = sorted(rule.methods - {"HEAD", "OPTIONS"})
|
|
|
|
# Try to get the view function's docstring
|
|
view_func = current_app.view_functions.get(rule.endpoint)
|
|
doc = ""
|
|
if view_func:
|
|
doc = (view_func.__doc__ or "").strip()
|
|
|
|
auth_req = auth_map.get(path, False)
|
|
|
|
entries.append({
|
|
"method": methods,
|
|
"path": path,
|
|
"name": rule.endpoint,
|
|
"auth": auth_req,
|
|
"doc": doc,
|
|
})
|
|
|
|
# Sort: methods order then path
|
|
entries.sort(key=lambda e: (e["method"][0] if e["method"] else "", e["path"]))
|
|
|
|
return jsonify({"endpoints": entries})
|