feat: add admin API endpoints for video management, update frontend to use new API routes
Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -523,6 +523,39 @@ def admin_videos():
|
||||
return render_template("admin/videos.html")
|
||||
|
||||
|
||||
# ── Admin API proxies (same-origin for browser JS, avoids mixed-content) ──
|
||||
|
||||
@app.get("/api/admin/videos")
|
||||
@admin_required
|
||||
def api_admin_list_videos():
|
||||
resp = _api("GET", "/admin/videos", token=session["access_token"])
|
||||
return jsonify(resp.json()), resp.status_code
|
||||
|
||||
|
||||
@app.post("/api/admin/videos/<job_id>/retry")
|
||||
@admin_required
|
||||
def api_admin_retry_video(job_id: str):
|
||||
resp = _api(
|
||||
"POST", f"/admin/videos/{job_id}/retry", token=session["access_token"])
|
||||
return jsonify(resp.json()), resp.status_code
|
||||
|
||||
|
||||
@app.post("/api/admin/videos/<job_id>/cancel")
|
||||
@admin_required
|
||||
def api_admin_cancel_video(job_id: str):
|
||||
resp = _api(
|
||||
"POST", f"/admin/videos/{job_id}/cancel", token=session["access_token"])
|
||||
return jsonify(resp.json()), resp.status_code
|
||||
|
||||
|
||||
@app.delete("/api/admin/videos/<job_id>")
|
||||
@admin_required
|
||||
def api_admin_delete_video(job_id: str):
|
||||
resp = _api(
|
||||
"DELETE", f"/admin/videos/{job_id}", token=session["access_token"])
|
||||
return jsonify(resp.json()), resp.status_code
|
||||
|
||||
|
||||
# ── Profile ───────────────────────────────────────────────────────────────
|
||||
|
||||
@app.route("/users/profile", methods=["GET", "POST"])
|
||||
|
||||
@@ -139,17 +139,13 @@
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const BACKEND = "{{ config['BACKEND_URL'] }}";
|
||||
const TOKEN = "{{ session['access_token'] }}";
|
||||
const headers = { Authorization: "Bearer " + TOKEN };
|
||||
|
||||
let allJobs = [];
|
||||
|
||||
async function loadJobs() {
|
||||
document.getElementById("vj-tbody").innerHTML =
|
||||
'<tr><td colspan="7" class="text-muted">Loading…</td></tr>';
|
||||
try {
|
||||
const r = await fetch(BACKEND + "/admin/videos", { headers });
|
||||
const r = await fetch("/api/admin/videos");
|
||||
if (!r.ok) throw new Error(await r.text());
|
||||
allJobs = await r.json();
|
||||
renderJobs();
|
||||
@@ -233,7 +229,7 @@
|
||||
}
|
||||
|
||||
async function apiPost(path) {
|
||||
const r = await fetch(BACKEND + path, { method: "POST", headers });
|
||||
const r = await fetch(path, { method: "POST" });
|
||||
if (!r.ok) {
|
||||
const d = await r.json().catch(() => ({}));
|
||||
throw new Error(d.detail || r.statusText);
|
||||
@@ -242,7 +238,7 @@
|
||||
}
|
||||
|
||||
async function apiDelete(path) {
|
||||
const r = await fetch(BACKEND + path, { method: "DELETE", headers });
|
||||
const r = await fetch(path, { method: "DELETE" });
|
||||
if (!r.ok) {
|
||||
const d = await r.json().catch(() => ({}));
|
||||
throw new Error(d.detail || r.statusText);
|
||||
@@ -258,12 +254,12 @@
|
||||
const id = btn.dataset.id;
|
||||
try {
|
||||
if (btn.classList.contains("vj-retry"))
|
||||
await apiPost(`/admin/videos/${id}/retry`);
|
||||
await apiPost(`/api/admin/videos/${id}/retry`);
|
||||
if (btn.classList.contains("vj-cancel"))
|
||||
await apiPost(`/admin/videos/${id}/cancel`);
|
||||
await apiPost(`/api/admin/videos/${id}/cancel`);
|
||||
if (btn.classList.contains("vj-delete")) {
|
||||
if (!confirm("Permanently delete this video job?")) return;
|
||||
await apiDelete(`/admin/videos/${id}`);
|
||||
await apiDelete(`/api/admin/videos/${id}`);
|
||||
}
|
||||
await loadJobs();
|
||||
} catch (err) {
|
||||
|
||||
Reference in New Issue
Block a user