about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/challenge.go24
-rw-r--r--src/http.go16
-rw-r--r--src/seed.go24
3 files changed, 57 insertions, 7 deletions
diff --git a/src/challenge.go b/src/challenge.go
index 991e4e5..1d0a210 100644
--- a/src/challenge.go
+++ b/src/challenge.go
@@ -1,10 +1,12 @@
 package main
 
+import "time"
+
 type Challenge struct {
 	Name string
 	Description string
 	Flag string // this should never leave the server
-	FoundFlag bool
+	FoundFlag time.Time
 	FlagTries uint
 	Container string // this could, but is not required as well
 	Category string
@@ -15,18 +17,34 @@ type StrippedChallenge struct {
 	Name string `json:"name"`
 	Description string `json:"description"`
 	Category string `json:"category"`
-	FoundFlag bool `json:"foundFlag"`
+	FoundFlag int64 `json:"foundFlag"`
+	FlagTries uint `json:"flagTries"`
 	ContainsLaunchable bool `json:"ContainsLaunchable"`
 	IPAddress string `json:"IPAddress"`
 }
 
+type StatsStrippedChallenge struct {
+	Name string `json:"name"`
+	FoundFlag int64 `json:"foundFlag"`
+	FlagTries uint `json:"flagTries"`
+}
+
 func stripChallenge(c Challenge) (StrippedChallenge) {
 	return StrippedChallenge{
 		Name: c.Name,
 		Description: c.Description,
 		Category: c.Category,
-		FoundFlag: c.FoundFlag,
+		FoundFlag: c.FoundFlag.Unix(),
+		FlagTries: c.FlagTries,
 		ContainsLaunchable: c.Container != "",
 		IPAddress: getAddressForChallengeContainer(c.Container),
 	}
 }
+
+func stripChallengeForStatistics(c Challenge) (StatsStrippedChallenge) {
+	return StatsStrippedChallenge{
+		Name: c.Name,
+		FoundFlag: c.FoundFlag.Unix(),
+		FlagTries: c.FlagTries,
+	}
+}
diff --git a/src/http.go b/src/http.go
index 1e60046..480e356 100644
--- a/src/http.go
+++ b/src/http.go
@@ -37,6 +37,7 @@ func setupHTTPServer() (http.Server) {
 	r.HandleFunc("/api/stopContainer", stopContainerHandler).Methods("POST")
 	r.HandleFunc("/api/getAccess", getAccessHandler).Methods("GET")
 	r.HandleFunc("/api/getTimeLimit", getTimeLimitHandler).Methods("GET")
+	r.HandleFunc("/api/getStats", getStatsHandler).Methods("GET")
 
 	return http.Server{
 		Addr: fmt.Sprintf("0.0.0.0:%d", *port),
@@ -203,6 +204,17 @@ func getChallengesHandler(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+func getStatsHandler(w http.ResponseWriter, r *http.Request) {
+	json, jsonErr := generateJSONFromChallengesForStats()
+
+	if jsonErr == nil {
+		w.Write([]byte(json))
+	} else {
+		log.Println(jsonErr)
+		w.WriteHeader(500)
+	}
+}
+
 func submitFlagHandler(w http.ResponseWriter, r *http.Request) {
 	r.ParseForm()
 	challengeName := r.Form.Get("challengeName")
@@ -232,11 +244,11 @@ func submitFlagHandler(w http.ResponseWriter, r *http.Request) {
 
 					if challenge.Flag == flag {
 						// our user found the flag \o/
-						challenges[index].FoundFlag = true
+						challenges[index].FoundFlag = time.Now()
 						correctFlag = true
 					} else {
 						// ow, bummer :(
-						challenge.FlagTries++
+						challenges[index].FlagTries++
 					}
 					break
 				}
diff --git a/src/seed.go b/src/seed.go
index 3dce55f..dd836ff 100644
--- a/src/seed.go
+++ b/src/seed.go
@@ -1,12 +1,13 @@
 package main
 
 import (
+	"encoding/json"
 	"flag"
 	"fmt"
 	"github.com/pkg/errors"
 	"io/ioutil"
 	"log"
-	"encoding/json"
+	"time"
 )
 
 var (
@@ -54,7 +55,7 @@ func readSeedFile(path string) ([]Challenge, error) {
 				Name: name,
 				Description: desc,
 				Flag: flag,
-				FoundFlag: false,
+				FoundFlag: time.Unix(0, 0),
 				FlagTries: 0,
 				Container: cont,
 				Category: category,
@@ -108,3 +109,22 @@ func generateJSONFromChallenges() (string, error) {
 	})
 	return string(marshalled), marshalError
 }
+
+// Generate a JSON string from the stored challenges, just containing enough to call it statistics
+func generateJSONFromChallengesForStats() (string, error) {
+	// To include only required information for statistics, we need to strip the challenge
+	var strippedChallenges []StatsStrippedChallenge
+	categories := map[string]int{}
+
+	for _, challenge := range challenges {
+		// Append challenge to list
+		strippedChallenges = append(strippedChallenges, stripChallengeForStatistics(challenge))
+
+		categories[challenge.Category]++
+	}
+
+	marshalled, marshalError := json.Marshal(map[string]interface{}{
+		"challenges": strippedChallenges,
+	})
+	return string(marshalled), marshalError
+}