feat: target matrix + reset scores/environment buttons
- /api/targets (admin): matrix of all teams' domain:port targets - /api/reset/scores: wipe leaderboard (admin, confirm dialog) - /api/reset/environment: stop all teams, remove containers+receivers+ team dirs+systemd units, wipe scores, drop domains (admin, confirm) - portal targets now only domain+port (no ssh/labels) - UI: tab Target Matrix, header buttons Reset Skor / Reset Environment with confirm() alerts 'apakah anda yakin ingin mereset...'
This commit is contained in:
+38
-6
@@ -202,7 +202,7 @@ async def api_team_session(idx: int, req: Request):
|
||||
|
||||
@app.get("/api/team/{idx}/targets")
|
||||
async def api_team_targets(idx: int, req: Request):
|
||||
"""Team targets — requires team login + own host."""
|
||||
"""Team targets — requires team login + own host. Only domain + port."""
|
||||
td = orch.TEAMS_DIR / f"team{idx}"
|
||||
if not (td / "state.json").exists():
|
||||
raise HTTPException(404, "Team not found")
|
||||
@@ -223,12 +223,8 @@ async def api_team_targets(idx: int, req: Request):
|
||||
if not p:
|
||||
continue
|
||||
out.append({
|
||||
"team": st.get("label", f"Team {st.get('index')}"),
|
||||
"team_idx": st.get("index"),
|
||||
"domain": st.get("domain"),
|
||||
"challenge": name,
|
||||
"domain": st.get("domain") or (st.get("slug", f"team{st.get('index')}") + ".gemastik.imrnes.team"),
|
||||
"port": p["chall"],
|
||||
"ssh": p["ssh"],
|
||||
})
|
||||
return {"targets": out}
|
||||
|
||||
@@ -510,6 +506,19 @@ async def api_randomize(idx: int, req: Request):
|
||||
raise HTTPException(500, str(e))
|
||||
|
||||
|
||||
@app.post("/api/reset/scores")
|
||||
async def api_reset_scores(req: Request):
|
||||
"""Admin: wipe leaderboard (all solves)."""
|
||||
require_login(req)
|
||||
return orch.reset_scores()
|
||||
|
||||
@app.post("/api/reset/environment")
|
||||
async def api_reset_environment(req: Request):
|
||||
"""Admin: full environment reset (stop all, remove teams/containers/receivers)."""
|
||||
require_login(req)
|
||||
return orch.reset_environment()
|
||||
|
||||
|
||||
@app.post("/api/flag/submit")
|
||||
async def api_flag_submit(req: Request):
|
||||
"""Public endpoint: teams submit flags. No login needed."""
|
||||
@@ -549,6 +558,29 @@ async def api_public_teams():
|
||||
"""Public list of team names (for the submit dropdown)."""
|
||||
return {"teams": [{"index": t["index"], "label": t.get("label", f"Team {t['index']}")} for t in orch.list_teams()]}
|
||||
|
||||
@app.get("/api/targets")
|
||||
async def api_admin_targets(req: Request):
|
||||
"""Admin: matrix of every team's service domain:port (public targets)."""
|
||||
require_login(req)
|
||||
out = []
|
||||
for d in sorted(orch.TEAMS_DIR.glob("team*")):
|
||||
if not (d / "state.json").exists():
|
||||
continue
|
||||
st = json.loads((d / "state.json").read_text())
|
||||
dom = st.get("domain") or (st.get("slug", f"team{st.get('index')}") + ".gemastik.imrnes.team")
|
||||
for name, coff, soff in orch.CHALLENGES:
|
||||
p = st["ports"].get(name)
|
||||
if not p:
|
||||
continue
|
||||
out.append({
|
||||
"team": st.get("index"),
|
||||
"team_label": st.get("label", f"Team {st.get('index')}"),
|
||||
"challenge": name,
|
||||
"domain": dom,
|
||||
"port": p["chall"],
|
||||
})
|
||||
return {"targets": out}
|
||||
|
||||
|
||||
# ============ WebSocket SSH terminal (team portal) ============
|
||||
import paramiko
|
||||
|
||||
Reference in New Issue
Block a user