Version 0.1.1 rise to flask app factory
This commit is contained in:
10
Html/apps/views/__init__.py
Normal file
10
Html/apps/views/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from flask import Flask
|
||||
from .markdown import bp as markdown_bp
|
||||
from .vscode import bp as vscode_bp
|
||||
from .auth import bp as auth_bp
|
||||
from .dashboard import bp as main_bp
|
||||
def register_blueprints(app: Flask):
|
||||
app.register_blueprint(markdown_bp) # 默认就是挂在根路径
|
||||
app.register_blueprint(vscode_bp)
|
||||
app.register_blueprint(auth_bp)
|
||||
app.register_blueprint(main_bp)
|
||||
57
Html/apps/views/auth.py
Normal file
57
Html/apps/views/auth.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# myapp/views/auth.py
|
||||
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, session, current_app
|
||||
from ..services.auth_service import register_user, login_user, logout_user
|
||||
from ..auth.decorators import require_role
|
||||
|
||||
bp = Blueprint("auth", __name__)
|
||||
|
||||
@bp.get("/register")
|
||||
def register():
|
||||
return render_template("register.html")
|
||||
|
||||
@bp.get("/register_teacher")
|
||||
def register_teacher():
|
||||
return render_template("register_teacher.html")
|
||||
|
||||
@bp.post("/register_post")
|
||||
def register_post():
|
||||
data = request.get_json(force=True) or {}
|
||||
ok, msg = register_user(data.get("username"), data.get("password"), teacher=False)
|
||||
return jsonify({"success": ok, "message": msg})
|
||||
|
||||
@bp.post("/register_teacher_post")
|
||||
def register_teacher_post():
|
||||
data = request.get_json(force=True) or {}
|
||||
ok, msg = register_user(data.get("username"), data.get("password"), teacher=True)
|
||||
return jsonify({"success": ok, "message": msg})
|
||||
|
||||
@bp.get("/login")
|
||||
def login():
|
||||
# 你原来 return 后还有逻辑,已被覆盖;这里简化
|
||||
return render_template("login.html")
|
||||
|
||||
@bp.post("/login_post")
|
||||
def login_post():
|
||||
data = request.get_json(force=True) or {}
|
||||
ok, msg = login_user(data.get("username"), data.get("password"), require_teacher=False)
|
||||
return jsonify({"success": ok, "message": msg})
|
||||
|
||||
@bp.post("/login_teacher_post")
|
||||
def login_teacher_post():
|
||||
data = request.get_json(force=True) or {}
|
||||
ok, msg = login_user(data.get("username"), data.get("password"), require_teacher=True)
|
||||
return jsonify({"success": ok, "message": msg})
|
||||
|
||||
@bp.get("/teacherboard")
|
||||
@require_role(roles="teacher")
|
||||
def teacherboard():
|
||||
return render_template("teacherboard.html")
|
||||
|
||||
@bp.get("/logout")
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
@bp.get("/get_session")
|
||||
def get_session():
|
||||
return jsonify({"session": session.get("user_id", "default_session")})
|
||||
60
Html/apps/views/dashboard.py
Normal file
60
Html/apps/views/dashboard.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# myapp/views/dashboard.py
|
||||
import json
|
||||
from flask import Blueprint, render_template, request, jsonify, redirect, url_for
|
||||
from ..auth.decorators import require_role
|
||||
from ..services.user_service import get_or_load_current_user, add_course_for_current_user
|
||||
from ..services.course_service import load_course, user_selected_course_briefs
|
||||
|
||||
bp = Blueprint("main", __name__) # 根路径蓝图
|
||||
|
||||
@bp.get("/dashboard")
|
||||
@require_role
|
||||
def dashboard():
|
||||
user_obj = get_or_load_current_user()
|
||||
if user_obj is None:
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
user_course_data = user_selected_course_briefs(user_obj)
|
||||
return render_template(
|
||||
"dashboard.html",
|
||||
user_data=user_obj.to_json_without_dialog(),
|
||||
user_course_data=json.dumps(user_course_data, ensure_ascii=False),
|
||||
)
|
||||
|
||||
@bp.get("/course/<course_id>")
|
||||
@require_role
|
||||
def course(course_id):
|
||||
c = load_course(course_id)
|
||||
return render_template("course.html", course_id=course_id, course_data=c)
|
||||
|
||||
@bp.post("/select_course")
|
||||
@require_role
|
||||
def select_course():
|
||||
data = request.get_json(force=True) or {}
|
||||
course_id = data.get("course_id")
|
||||
if not course_id:
|
||||
return jsonify({"success": False, "message": "缺少 course_id"}), 400
|
||||
|
||||
course_data = load_course(course_id)
|
||||
ok = add_course_for_current_user(course_id, course_data)
|
||||
if not ok:
|
||||
return jsonify({"success": False, "message": "未登录或会话失效"}), 401
|
||||
return jsonify({"success": True, "message": "课程选择成功"})
|
||||
|
||||
@bp.get("/")
|
||||
@require_role
|
||||
def home_index():
|
||||
user_obj = get_or_load_current_user()
|
||||
if user_obj is None:
|
||||
return redirect(url_for("auth.login"))
|
||||
|
||||
selected_courses = list(getattr(user_obj, "select_course", []))
|
||||
# 课程目录:依据你的 CourseList 暴露的接口进行传递(这里直接传对象,模板里用)
|
||||
from flask import current_app
|
||||
course_list = current_app.extensions["course_list"]
|
||||
|
||||
return render_template(
|
||||
"index.html",
|
||||
courses_data=course_list,
|
||||
selected_courses=selected_courses
|
||||
)
|
||||
35
Html/apps/views/markdown.py
Normal file
35
Html/apps/views/markdown.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import os
|
||||
from flask import Blueprint, jsonify, current_app, send_from_directory
|
||||
from ..services.markdown_service import (
|
||||
convert_markdown_to_html, wrap_with_styles,
|
||||
save_html, copy_images
|
||||
)
|
||||
|
||||
bp = Blueprint("markdown", __name__)
|
||||
|
||||
@bp.route("/<course_id>-<filename>-markdown", methods=["GET"])
|
||||
def convert_md(course_id, filename):
|
||||
md_file_path = os.path.join(current_app.config["MARKDOWN_DIR"], course_id, f"{filename}.md")
|
||||
|
||||
if not os.path.exists(md_file_path):
|
||||
return jsonify({"error": "Markdown file not found"}), 404
|
||||
|
||||
# 转换 markdown -> html
|
||||
html = convert_markdown_to_html(md_file_path)
|
||||
html_with_styles = wrap_with_styles(html)
|
||||
|
||||
# 保存 HTML 文件到 static
|
||||
html_output_path = os.path.join(current_app.config["STATIC_DIR"], f"{filename}.html")
|
||||
save_html(html_with_styles, html_output_path)
|
||||
|
||||
# 拷贝图片资源
|
||||
image_source = os.path.join(current_app.config["MARKDOWN_DIR"], course_id, current_app.config["IMAGE_DIR"], filename)
|
||||
image_target = os.path.join(current_app.config["STATIC_DIR"], current_app.config["IMAGE_DIR"], filename)
|
||||
copy_images(image_source, image_target)
|
||||
|
||||
return jsonify({"html_url": f"/static/{filename}.html"})
|
||||
|
||||
# 提供静态文件访问
|
||||
@bp.route("/static/<path:filename>")
|
||||
def serve_static(filename):
|
||||
return send_from_directory(current_app.config["STATIC_DIR"], filename)
|
||||
69
Html/apps/views/vscode.py
Normal file
69
Html/apps/views/vscode.py
Normal file
@@ -0,0 +1,69 @@
|
||||
# myapp/views/vscode.py
|
||||
import os, uuid, json
|
||||
from flask import Blueprint, current_app, session, redirect, url_for, render_template, request, jsonify
|
||||
from ..services.backboard_service import realtime_response
|
||||
|
||||
bp = Blueprint("vscode", __name__)
|
||||
|
||||
@bp.route("/desktop/<user_id>/<course_id>/<chapter_id>")
|
||||
def desktop(user_id, course_id, chapter_id):
|
||||
# session 中放 uuid(访客也能进则给一个)
|
||||
if "user_id" not in session:
|
||||
session["user_id"] = "user_" + str(uuid.uuid4())
|
||||
|
||||
current_app.logger.debug("user %s uuid is %s", user_id, session["user_id"])
|
||||
|
||||
# 全局映射
|
||||
username2uuid = current_app.extensions["username2uuid"]
|
||||
uuid2username = current_app.extensions["uuid2username"]
|
||||
userid_recorder = current_app.extensions["userid_recorder"]
|
||||
|
||||
username2uuid[user_id] = session["user_id"]
|
||||
uuid2username[session["user_id"]] = user_id
|
||||
userid_recorder[f"{user_id}&{course_id}"] = session["user_id"]
|
||||
|
||||
# 按课程/章节创建工作目录
|
||||
base_root = current_app.config["STUDENT_WORKSPACE_ROOT"]
|
||||
path_dir = os.path.join(base_root, user_id, course_id, chapter_id)
|
||||
os.makedirs(path_dir, exist_ok=True)
|
||||
|
||||
# 写 .config(并按需要转换为 WSL 路径)
|
||||
cfg = current_app.config["VSCODE_WEB_PATH"]
|
||||
path_for_vscode = path_dir
|
||||
if cfg.get("is_wsl"):
|
||||
path_for_vscode = path_for_vscode.replace("\\", "/") \
|
||||
.replace(cfg.get("windows_path", ""), cfg.get("wsl_path", ""))
|
||||
|
||||
config_path = os.path.join(path_dir, ".config")
|
||||
tmpd = {
|
||||
"user_id": user_id,
|
||||
"course_id": course_id,
|
||||
"chapter_id": chapter_id,
|
||||
"path": path_for_vscode
|
||||
}
|
||||
with open(config_path, "w", encoding="utf-8") as f:
|
||||
json.dump(tmpd, f, ensure_ascii=False)
|
||||
|
||||
current_app.logger.debug("config file path: %s", config_path)
|
||||
|
||||
return render_template(
|
||||
"desktop.html",
|
||||
vscode_web_url=current_app.config["VSCODE_WEB_URL"],
|
||||
user_id=user_id, course_id=course_id, chapter_id=chapter_id,
|
||||
workspace_path=path_for_vscode
|
||||
)
|
||||
|
||||
@bp.route("/desktop_nouser/<course_id>/<chapter_id>")
|
||||
def desktop_nouser(course_id, chapter_id):
|
||||
if "user_id" not in session:
|
||||
return redirect(url_for("auth.login")) # 如果你有 auth 蓝图
|
||||
user_uuid = session["user_id"]
|
||||
username = current_app.extensions["uuid2username"].get(user_uuid)
|
||||
return redirect(url_for("vscode.desktop", user_id=username, course_id=course_id, chapter_id=chapter_id))
|
||||
|
||||
@bp.route("/vscode_data", methods=["POST"])
|
||||
def vscode_data():
|
||||
data = request.get_json(force=True)
|
||||
config = data.get("config") or {}
|
||||
realtime_response(config, data) # 调用服务层逻辑
|
||||
return jsonify({"status": "success", "received": data})
|
||||
Reference in New Issue
Block a user