Add admin features, user profile management, and generation capabilities
- Implemented admin dashboard with user management features including role assignment and deletion. - Added user profile page for updating email and password. - Created separate routes and templates for text, image, and video generation with appropriate forms. - Enhanced navigation with dropdown for generation options and loading overlay for better user experience. - Introduced comprehensive error handling and user feedback through alerts. - Updated styles for improved UI consistency and responsiveness. Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
+137
-24
@@ -42,6 +42,18 @@ def login_required(view):
|
||||
return wrapped
|
||||
|
||||
|
||||
def admin_required(view):
|
||||
@functools.wraps(view)
|
||||
def wrapped(*args, **kwargs):
|
||||
if "access_token" not in session:
|
||||
return redirect(url_for("login"))
|
||||
if session.get("user_role") != "admin":
|
||||
flash("Admin access required.", "error")
|
||||
return redirect(url_for("dashboard"))
|
||||
return view(*args, **kwargs)
|
||||
return wrapped
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Auth routes
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -63,6 +75,11 @@ def login():
|
||||
data = resp.json()
|
||||
session["access_token"] = data["access_token"]
|
||||
session["refresh_token"] = data["refresh_token"]
|
||||
me = _api("GET", "/users/me", token=data["access_token"])
|
||||
if me.status_code == 200:
|
||||
u = me.json()
|
||||
session["user_email"] = u.get("email", "")
|
||||
session["user_role"] = u.get("role", "user")
|
||||
return redirect(url_for("dashboard"))
|
||||
flash("Invalid email or password.", "error")
|
||||
return render_template("login.html")
|
||||
@@ -104,33 +121,129 @@ def dashboard():
|
||||
return render_template("dashboard.html", user=user)
|
||||
|
||||
|
||||
@app.route("/generate", methods=["GET", "POST"])
|
||||
# ── Generate ──────────────────────────────────────────────────────────────
|
||||
|
||||
@app.get("/generate")
|
||||
@login_required
|
||||
def generate():
|
||||
result = None
|
||||
error = None
|
||||
return redirect(url_for("generate_text"))
|
||||
|
||||
|
||||
@app.route("/generate/text", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def generate_text():
|
||||
result = error = None
|
||||
if request.method == "POST":
|
||||
gen_type = request.form.get("type", "text")
|
||||
model = request.form.get("model", "").strip()
|
||||
prompt = request.form.get("prompt", "").strip()
|
||||
token = session["access_token"]
|
||||
|
||||
if gen_type == "text":
|
||||
resp = _api("POST", "/generate/text", token=token,
|
||||
json={"model": model, "prompt": prompt})
|
||||
elif gen_type == "image":
|
||||
resp = _api("POST", "/generate/image", token=token,
|
||||
json={"model": model, "prompt": prompt})
|
||||
elif gen_type == "video":
|
||||
resp = _api("POST", "/generate/video", token=token,
|
||||
json={"model": model, "prompt": prompt})
|
||||
else:
|
||||
resp = None
|
||||
|
||||
if resp is not None and resp.status_code == 200:
|
||||
resp = _api("POST", "/generate/text", token=session["access_token"], json={
|
||||
"model": request.form.get("model", "").strip(),
|
||||
"prompt": request.form.get("prompt", "").strip(),
|
||||
})
|
||||
if resp.status_code == 200:
|
||||
result = resp.json()
|
||||
else:
|
||||
detail = resp.json().get("detail", "Generation failed.") if resp is not None else "Unknown error."
|
||||
error = detail
|
||||
error = resp.json().get("detail", "Generation failed.")
|
||||
return render_template("generate_text.html", result=result, error=error)
|
||||
|
||||
|
||||
@app.route("/generate/image", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def generate_image():
|
||||
result = error = None
|
||||
if request.method == "POST":
|
||||
resp = _api("POST", "/generate/image", token=session["access_token"], json={
|
||||
"model": request.form.get("model", "").strip(),
|
||||
"prompt": request.form.get("prompt", "").strip(),
|
||||
"n": int(request.form.get("n", 1)),
|
||||
"size": request.form.get("size", "1024x1024"),
|
||||
})
|
||||
if resp.status_code == 200:
|
||||
result = resp.json()
|
||||
else:
|
||||
error = resp.json().get("detail", "Generation failed.")
|
||||
return render_template("generate_image.html", result=result, error=error)
|
||||
|
||||
|
||||
@app.route("/generate/video", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def generate_video():
|
||||
result = error = None
|
||||
if request.method == "POST":
|
||||
mode = request.form.get("mode", "text")
|
||||
token = session["access_token"]
|
||||
if mode == "image":
|
||||
resp = _api("POST", "/generate/video/from-image", token=token, json={
|
||||
"model": request.form.get("model", "").strip(),
|
||||
"image_url": request.form.get("image_url", "").strip(),
|
||||
"prompt": request.form.get("prompt", "").strip(),
|
||||
"aspect_ratio": request.form.get("aspect_ratio", "16:9"),
|
||||
})
|
||||
else:
|
||||
resp = _api("POST", "/generate/video", token=token, json={
|
||||
"model": request.form.get("model", "").strip(),
|
||||
"prompt": request.form.get("prompt", "").strip(),
|
||||
"aspect_ratio": request.form.get("aspect_ratio", "16:9"),
|
||||
})
|
||||
if resp.status_code == 200:
|
||||
result = resp.json()
|
||||
else:
|
||||
error = resp.json().get("detail", "Generation failed.")
|
||||
return render_template("generate_video.html", result=result, error=error)
|
||||
|
||||
|
||||
# ── Admin ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@app.get("/admin")
|
||||
@admin_required
|
||||
def admin():
|
||||
token = session["access_token"]
|
||||
stats_resp = _api("GET", "/admin/stats", token=token)
|
||||
users_resp = _api("GET", "/users", token=token)
|
||||
stats = stats_resp.json() if stats_resp.status_code == 200 else {}
|
||||
users = users_resp.json() if users_resp.status_code == 200 else []
|
||||
return render_template("admin.html", stats=stats, users=users)
|
||||
|
||||
|
||||
@app.post("/admin/users/<user_id>/role")
|
||||
@admin_required
|
||||
def admin_set_role(user_id: str):
|
||||
role = request.form.get("role", "user")
|
||||
_api("PUT", f"/users/{user_id}/role", token=session["access_token"], json={"role": role})
|
||||
flash(f"Role updated to '{role}'.", "success")
|
||||
return redirect(url_for("admin"))
|
||||
|
||||
|
||||
@app.post("/admin/users/<user_id>/delete")
|
||||
@admin_required
|
||||
def admin_delete_user(user_id: str):
|
||||
_api("DELETE", f"/users/{user_id}", token=session["access_token"])
|
||||
flash("User deleted.", "success")
|
||||
return redirect(url_for("admin"))
|
||||
|
||||
|
||||
# ── Profile ───────────────────────────────────────────────────────────────
|
||||
|
||||
@app.route("/users/profile", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def profile():
|
||||
token = session["access_token"]
|
||||
if request.method == "POST":
|
||||
payload: dict = {}
|
||||
new_email = request.form.get("email", "").strip()
|
||||
new_password = request.form.get("password", "").strip()
|
||||
if new_email:
|
||||
payload["email"] = new_email
|
||||
if new_password:
|
||||
payload["password"] = new_password
|
||||
if payload:
|
||||
resp = _api("PUT", "/users/me", token=token, json=payload)
|
||||
if resp.status_code == 200:
|
||||
updated = resp.json()
|
||||
session["user_email"] = updated.get("email", session.get("user_email", ""))
|
||||
flash("Profile updated.", "success")
|
||||
else:
|
||||
flash(resp.json().get("detail", "Update failed."), "error")
|
||||
return redirect(url_for("profile"))
|
||||
resp = _api("GET", "/users/me", token=token)
|
||||
user = resp.json() if resp.status_code == 200 else {}
|
||||
return render_template("profile.html", user=user)
|
||||
|
||||
return render_template("generate.html", result=result, error=error)
|
||||
|
||||
Reference in New Issue
Block a user