1use serde_json::json;
2
3use crate::error::HtbError;
4use crate::models::ctf::{
5 CtfChallengeSolve, CtfEvent, CtfEventData, CtfEventDetail, CtfFlagResult, CtfMenu,
6 CtfScoreboard, CtfSolve, CtfUserProfile,
7};
8use crate::models::ActionResponse;
9
10use super::HtbClient;
11
12pub struct CtfApi<'a>(pub(crate) &'a HtbClient);
13
14impl CtfApi<'_> {
15 pub async fn profile(&self) -> Result<CtfUserProfile, HtbError> {
16 self.0.get("/api/users/profile").await
17 }
18
19 pub async fn events(&self) -> Result<Vec<CtfEvent>, HtbError> {
20 self.0.get("/api/ctfs").await
21 }
22
23 pub async fn event_details(&self, slug: &str) -> Result<CtfEventDetail, HtbError> {
24 let encoded = super::encode_path(slug);
25 self.0.get(&format!("/api/ctfs/details/{encoded}")).await
26 }
27
28 pub async fn event_data(&self, event_id: u64) -> Result<CtfEventData, HtbError> {
29 self.0.get(&format!("/api/ctfs/{event_id}")).await
30 }
31
32 pub async fn menu(&self, event_id: u64) -> Result<CtfMenu, HtbError> {
33 self.0.get(&format!("/api/ctfs/{event_id}/menu")).await
34 }
35
36 pub async fn submit_flag(
37 &self,
38 challenge_id: u64,
39 flag: &str,
40 ) -> Result<CtfFlagResult, HtbError> {
41 self.0
42 .post(
43 "/api/flags/own",
44 &json!({"challenge_id": challenge_id, "flag": flag}),
45 )
46 .await
47 }
48
49 pub async fn download_file(&self, challenge_id: u64) -> Result<Vec<u8>, HtbError> {
50 self.0
51 .get_bytes(&format!("/api/challenges/{challenge_id}/download"))
52 .await
53 }
54
55 pub async fn container_start(&self, challenge_id: u64) -> Result<ActionResponse, HtbError> {
56 self.0
57 .post(
58 "/api/challenges/containers/start",
59 &json!({"id": challenge_id}),
60 )
61 .await
62 }
63
64 pub async fn container_stop(&self, challenge_id: u64) -> Result<ActionResponse, HtbError> {
65 self.0
66 .post(
67 "/api/challenges/containers/stop",
68 &json!({"id": challenge_id}),
69 )
70 .await
71 }
72
73 pub async fn scoreboard(&self, event_id: u64) -> Result<CtfScoreboard, HtbError> {
74 self.0.get(&format!("/api/ctfs/scores/{event_id}")).await
75 }
76
77 pub async fn solves(&self, event_id: u64) -> Result<Vec<CtfSolve>, HtbError> {
78 self.0.get(&format!("/api/ctfs/solves/{event_id}")).await
79 }
80
81 pub async fn challenge_solves(
82 &self,
83 challenge_id: u64,
84 ) -> Result<Vec<CtfChallengeSolve>, HtbError> {
85 self.0
86 .get(&format!("/api/challenges/{challenge_id}/solves"))
87 .await
88 }
89}