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")
|
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 ───────────────────────────────────────────────────────────────
|
# ── Profile ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@app.route("/users/profile", methods=["GET", "POST"])
|
@app.route("/users/profile", methods=["GET", "POST"])
|
||||||
|
|||||||
@@ -139,17 +139,13 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function () {
|
(function () {
|
||||||
const BACKEND = "{{ config['BACKEND_URL'] }}";
|
|
||||||
const TOKEN = "{{ session['access_token'] }}";
|
|
||||||
const headers = { Authorization: "Bearer " + TOKEN };
|
|
||||||
|
|
||||||
let allJobs = [];
|
let allJobs = [];
|
||||||
|
|
||||||
async function loadJobs() {
|
async function loadJobs() {
|
||||||
document.getElementById("vj-tbody").innerHTML =
|
document.getElementById("vj-tbody").innerHTML =
|
||||||
'<tr><td colspan="7" class="text-muted">Loading…</td></tr>';
|
'<tr><td colspan="7" class="text-muted">Loading…</td></tr>';
|
||||||
try {
|
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());
|
if (!r.ok) throw new Error(await r.text());
|
||||||
allJobs = await r.json();
|
allJobs = await r.json();
|
||||||
renderJobs();
|
renderJobs();
|
||||||
@@ -233,7 +229,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function apiPost(path) {
|
async function apiPost(path) {
|
||||||
const r = await fetch(BACKEND + path, { method: "POST", headers });
|
const r = await fetch(path, { method: "POST" });
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
const d = await r.json().catch(() => ({}));
|
const d = await r.json().catch(() => ({}));
|
||||||
throw new Error(d.detail || r.statusText);
|
throw new Error(d.detail || r.statusText);
|
||||||
@@ -242,7 +238,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function apiDelete(path) {
|
async function apiDelete(path) {
|
||||||
const r = await fetch(BACKEND + path, { method: "DELETE", headers });
|
const r = await fetch(path, { method: "DELETE" });
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
const d = await r.json().catch(() => ({}));
|
const d = await r.json().catch(() => ({}));
|
||||||
throw new Error(d.detail || r.statusText);
|
throw new Error(d.detail || r.statusText);
|
||||||
@@ -258,12 +254,12 @@
|
|||||||
const id = btn.dataset.id;
|
const id = btn.dataset.id;
|
||||||
try {
|
try {
|
||||||
if (btn.classList.contains("vj-retry"))
|
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"))
|
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 (btn.classList.contains("vj-delete")) {
|
||||||
if (!confirm("Permanently delete this video job?")) return;
|
if (!confirm("Permanently delete this video job?")) return;
|
||||||
await apiDelete(`/admin/videos/${id}`);
|
await apiDelete(`/api/admin/videos/${id}`);
|
||||||
}
|
}
|
||||||
await loadJobs();
|
await loadJobs();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
Reference in New Issue
Block a user