Rename frontend as receiver
1
frontend/.gitignore
vendored
|
|
@ -1 +0,0 @@
|
|||
frontend
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# You can define the current base URL in your environment
|
||||
[ -n "${CURRENT_BASE}" ] || CURRENT_BASE="/"
|
||||
|
||||
usage() {
|
||||
echo "$0 NEWBASE STATICDIR"
|
||||
echo
|
||||
echo " This script can be used to change the base URL of the frontend."
|
||||
echo " Gives as PATH, the path to the static directory."
|
||||
echo " You should use this script only one time (ie. from a fresh git repository),"
|
||||
echo " as it doesn't erase previously defined base: it assumes the current base is /."
|
||||
echo
|
||||
echo " For example:"
|
||||
echo
|
||||
echo " $0 /$(date -d 'next year' +%Y)/" static/
|
||||
echo
|
||||
}
|
||||
|
||||
run() {
|
||||
local NEWBASE=$1
|
||||
local FILE=$2
|
||||
|
||||
if [ -d "${FILE}" ]
|
||||
then
|
||||
for f in "${FILE}/"*.html "${FILE}/"*.js
|
||||
do
|
||||
run "${NEWBASE}" "${f}"
|
||||
done
|
||||
[ -d "${FILE}/js/" ] && run "${NEWBASE}" "${FILE}/js"
|
||||
[ -d "${FILE}/views/" ] && run "${NEWBASE}" "${FILE}/views"
|
||||
elif [ -f "${FILE}" ]
|
||||
then
|
||||
sed -ri "s@(href|src)=\"${CURRENT_BASE}@\1=\"${NEWBASE}@g;s@\\\$http.get\(\"${CURRENT_BASE}@\$http.get\(\"${NEWBASE}@g;s@\\\$http\((.*)\"${CURRENT_BASE}@\$http(\1\"${NEWBASE}@g" ${FILE}
|
||||
fi
|
||||
}
|
||||
|
||||
if [ $# -gt 1 ]
|
||||
then
|
||||
run $1 $2
|
||||
else
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
var denyNameChange bool = true
|
||||
|
||||
func ChNameHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if denyNameChange {
|
||||
log.Printf("UNHANDELED %s name change request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
http.Error(w, "{\"errmsg\":\"Le changement de nom est prohibé.\"}", http.StatusForbidden)
|
||||
} else if saveTeamFile(path.Join(team, "name"), w, r) {
|
||||
// File enqueued for backend treatment
|
||||
http.Error(w, "{\"errmsg\":\"Demande de changement de nom acceptée\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
func WantChoicesHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if challengeEnd != nil && time.Now().After(*challengeEnd) {
|
||||
http.Error(w, "{\"errmsg\":\"Le challenge est terminé, trop tard !\"}", http.StatusGone)
|
||||
return
|
||||
}
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if saveTeamFile(path.Join(team, "choices"), w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Demande de choix acceptée...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
|
|
@ -1,19 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
func HintHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if challengeEnd != nil && time.Now().After(*challengeEnd) {
|
||||
http.Error(w, "{\"errmsg\":\"Le challenge est terminé, trop tard pour un indice !\"}", http.StatusGone)
|
||||
return
|
||||
}
|
||||
|
||||
// Enqueue file for backend treatment
|
||||
if saveTeamFile(path.Join(team, "hint"), w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Demande d'astuce acceptée...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
var acceptNewIssues bool = true
|
||||
|
||||
func IssueHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if !acceptNewIssues {
|
||||
log.Printf("UNHANDELED %s issue request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
http.Error(w, "{\"errmsg\":\"Il n'est pas possible de rapporter d'anomalie.\"}", http.StatusForbidden)
|
||||
} else if saveTeamFile(path.Join(team, "issue"), w, r) {
|
||||
// File enqueued for backend treatment
|
||||
http.Error(w, "{\"errmsg\":\"Anomalie signalée avec succès. Merci de votre patience...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
118
frontend/main.go
|
|
@ -1,118 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var bind = flag.String("bind", "127.0.0.1:8080", "Bind port/socket")
|
||||
var prefix = flag.String("prefix", "/", "Request path prefix to strip (from proxy)")
|
||||
var teamsDir = flag.String("teams", "./TEAMS/", "Base directory where find existing teams")
|
||||
flag.StringVar(&settings.SettingsDir, "settings", "./SETTINGSDIST", "Base directory where read settings")
|
||||
flag.StringVar(&startedFile, "startedFile", startedFile, "Path to the file to create/remove whether or not the challenge is running")
|
||||
flag.StringVar(&SubmissionDir, "submission", "./submissions/", "Base directory where save submissions")
|
||||
var simulator = flag.String("simulator", "", "Team to simulate (for development only)")
|
||||
flag.StringVar(&staticDir, "static", staticDir, "Set to serve pages as well (for development only, use with -simulator)")
|
||||
flag.Parse()
|
||||
|
||||
log.SetPrefix("[frontend] ")
|
||||
|
||||
startedFile = path.Clean(startedFile)
|
||||
SubmissionDir = path.Clean(SubmissionDir)
|
||||
TmpSubmissionDir = path.Join(SubmissionDir, ".tmp")
|
||||
|
||||
log.Println("Creating submission directory...")
|
||||
if _, err := os.Stat(TmpSubmissionDir); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(TmpSubmissionDir, 0700); err != nil {
|
||||
log.Fatal("Unable to create submission directory:", err)
|
||||
}
|
||||
}
|
||||
|
||||
*prefix = strings.TrimRight(*prefix, "/")
|
||||
|
||||
// Load configuration
|
||||
settings.LoadAndWatchSettings(path.Join(settings.SettingsDir, settings.SettingsFile), reloadSettings)
|
||||
|
||||
// Register handlers
|
||||
http.Handle(fmt.Sprintf("%s/chname", *prefix), http.StripPrefix(fmt.Sprintf("%s/chname", *prefix), submissionTeamChecker{"name change", ChNameHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/issue", *prefix), http.StripPrefix(fmt.Sprintf("%s/issue", *prefix), submissionTeamChecker{"issue", IssueHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/openhint/", *prefix), http.StripPrefix(fmt.Sprintf("%s/openhint/", *prefix), submissionTeamChecker{"opening hint", HintHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/wantchoices/", *prefix), http.StripPrefix(fmt.Sprintf("%s/wantchoices/", *prefix), submissionTeamChecker{"wantint choices", WantChoicesHandler, *teamsDir, *simulator}))
|
||||
http.Handle(fmt.Sprintf("%s/registration", *prefix), http.StripPrefix(fmt.Sprintf("%s/registration", *prefix), submissionChecker{"registration", RegistrationHandler}))
|
||||
http.Handle(fmt.Sprintf("%s/resolution/", *prefix), http.StripPrefix(fmt.Sprintf("%s/resolution/", *prefix), ResolutionHandler{}))
|
||||
http.Handle(fmt.Sprintf("%s/submission/", *prefix), http.StripPrefix(fmt.Sprintf("%s/submission/", *prefix), submissionTeamChecker{"submission", SubmissionHandler, *teamsDir, *simulator}))
|
||||
|
||||
if *simulator != "" {
|
||||
if _, err := os.Stat(path.Join(*teamsDir, *simulator)); os.IsNotExist(err) {
|
||||
log.Printf("WARNING: Team '%s' doesn't exist yet in %s.", *simulator, *teamsDir)
|
||||
}
|
||||
|
||||
// Serve team files
|
||||
http.Handle(fmt.Sprintf("%s/wait.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(path.Join(*teamsDir, *simulator)))))
|
||||
http.Handle(fmt.Sprintf("%s/my.json", *prefix), http.StripPrefix(*prefix, TeamMyServer{path.Join(*teamsDir, *simulator)}))
|
||||
|
||||
// Serve generated content
|
||||
http.Handle(fmt.Sprintf("%s/teams.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
|
||||
http.Handle(fmt.Sprintf("%s/themes.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
|
||||
http.Handle(fmt.Sprintf("%s/stats.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(*teamsDir))))
|
||||
http.Handle(fmt.Sprintf("%s/settings.json", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(settings.SettingsDir))))
|
||||
|
||||
// Serve static assets
|
||||
http.Handle(fmt.Sprintf("%s/css/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(staticDir))))
|
||||
http.Handle(fmt.Sprintf("%s/js/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir(staticDir))))
|
||||
|
||||
http.Handle(fmt.Sprintf("%s/files/", *prefix), http.StripPrefix(*prefix, http.FileServer(http.Dir("FILES"))))
|
||||
|
||||
// Serve index
|
||||
http.HandleFunc(fmt.Sprintf("%s/edit", *prefix), serveIndex)
|
||||
http.HandleFunc(fmt.Sprintf("%s/rank", *prefix), serveIndex)
|
||||
http.HandleFunc(fmt.Sprintf("%s/register", *prefix), serveIndex)
|
||||
http.HandleFunc(fmt.Sprintf("%s/rules", *prefix), serveIndex)
|
||||
http.HandleFunc(fmt.Sprintf("%s/videos", *prefix), serveIndex)
|
||||
}
|
||||
|
||||
// Prepare graceful shutdown
|
||||
interrupt := make(chan os.Signal, 1)
|
||||
signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: *bind,
|
||||
}
|
||||
|
||||
// Serve pages
|
||||
go func() {
|
||||
log.Fatal(srv.ListenAndServe())
|
||||
}()
|
||||
log.Println(fmt.Sprintf("Ready, listening on %s", *bind))
|
||||
|
||||
// Wait shutdown signal and touch timestamp
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-interrupt:
|
||||
break loop
|
||||
case <-ticker.C:
|
||||
now := time.Now()
|
||||
os.Chtimes(SubmissionDir, now, now)
|
||||
}
|
||||
}
|
||||
|
||||
log.Print("The service is shutting down...")
|
||||
srv.Shutdown(context.Background())
|
||||
log.Println("done")
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"path"
|
||||
)
|
||||
|
||||
var allowRegistration bool = false
|
||||
|
||||
func RegistrationHandler(w http.ResponseWriter, r *http.Request, sURL []string) {
|
||||
if !allowRegistration {
|
||||
log.Printf("UNHANDLED %s registration request from %s: %s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
http.Error(w, "{\"errmsg\":\"L'enregistrement d'équipe n'est pas permis.\"}", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
teamInitialName := "-"
|
||||
if t := r.Header.Get("X-FIC-Team"); t != "" {
|
||||
teamInitialName = t
|
||||
} else {
|
||||
http.Error(w, "{\"errmsg\":\"Votre jeton d'authentification semble invalide. Contactez l'équipe serveur.\"}", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Check request type and size
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if r.ContentLength < 0 || r.ContentLength > 4095 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
if err := saveFile(path.Join(SubmissionDir, "_registration", teamInitialName), r); err != nil {
|
||||
log.Println("Unable to open registration file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
} else {
|
||||
// File enqueued for backend treatment
|
||||
http.Error(w, "{\"errmsg\":\"Demande d'enregistrement acceptée\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var enableResolutionRoute bool = false
|
||||
|
||||
type ResolutionHandler struct{}
|
||||
|
||||
const resolutiontpl = `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Résolution</title>
|
||||
</head>
|
||||
<body style="margin: 0">
|
||||
<video src="{{.}}" controls width="100%" height="100%"></video>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
func (s ResolutionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||
r.RemoteAddr = addr
|
||||
}
|
||||
|
||||
if !enableResolutionRoute {
|
||||
log.Printf("UNHANDELED %s request from %s: /resolution%s [%s]\n", r.Method, r.RemoteAddr, r.URL.Path, r.UserAgent())
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("%s \"%s /resolution%s\" [%s]\n", r.RemoteAddr, r.Method, r.URL.Path, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
|
||||
if resolutionTmpl, err := template.New("resolution").Parse(resolutiontpl); err != nil {
|
||||
log.Println("Cannot create template: ", err)
|
||||
} else if err = resolutionTmpl.Execute(w, path.Join("/vids/", strings.Replace(url.PathEscape(r.URL.Path), "%2F", "/", -1))); err != nil {
|
||||
log.Println("An error occurs during template execution: ", err)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var SubmissionDir string
|
||||
var TmpSubmissionDir string
|
||||
|
||||
func saveTeamFile(p string, w http.ResponseWriter, r *http.Request) bool {
|
||||
if len(SubmissionDir) < 1 || len(p) < 1 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return false
|
||||
} else if len(p) <= 0 {
|
||||
log.Println("EMPTY $EXERCICE RECEIVED:", p)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return false
|
||||
} else if _, err := os.Stat(path.Join(SubmissionDir, p)); !os.IsNotExist(err) {
|
||||
// Previous submission not treated
|
||||
http.Error(w, "{\"errmsg\":\"Du calme ! une requête est déjà en cours de traitement.\"}", http.StatusPaymentRequired)
|
||||
return false
|
||||
} else if err = saveFile(path.Join(SubmissionDir, p), r); err != nil {
|
||||
log.Println("Unable to handle submission file:", err)
|
||||
http.Error(w, "{\"errmsg\":\"Internal server error. Please retry in few seconds.\"}", http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func saveFile(p string, r *http.Request) error {
|
||||
dirname := path.Dir(p)
|
||||
if _, err := os.Stat(dirname); os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dirname, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Write content to temp file
|
||||
tmpfile, err := ioutil.TempFile(TmpSubmissionDir, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
writer := bufio.NewWriter(tmpfile)
|
||||
reader := bufio.NewReader(r.Body)
|
||||
if _, err = reader.WriteTo(writer); err != nil {
|
||||
return err
|
||||
}
|
||||
writer.Flush()
|
||||
tmpfile.Close()
|
||||
|
||||
if err = os.Rename(tmpfile.Name(), p); err != nil {
|
||||
log.Println("[ERROR] Unable to move file: ", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"srs.epita.fr/fic-server/settings"
|
||||
)
|
||||
|
||||
var startedFile = "started"
|
||||
|
||||
var touchTimer *time.Timer = nil
|
||||
var challengeStart time.Time
|
||||
var challengeEnd *time.Time
|
||||
|
||||
func touchStartedFile() {
|
||||
if fd, err := os.Create(startedFile); err == nil {
|
||||
log.Println("Started! Go, Go, Go!!")
|
||||
fd.Close()
|
||||
} else {
|
||||
log.Fatal("Unable to start challenge:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func reloadSettings(config *settings.Settings) {
|
||||
if challengeStart != config.Start || challengeEnd != config.End {
|
||||
if touchTimer != nil {
|
||||
touchTimer.Stop()
|
||||
}
|
||||
|
||||
if config.Start.Unix() == 0 {
|
||||
log.Println("WARNING: No challenge start defined!")
|
||||
|
||||
if _, err := os.Stat(startedFile); !os.IsNotExist(err) {
|
||||
os.Remove(startedFile)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
startSub := time.Until(config.Start)
|
||||
if startSub > 0 {
|
||||
log.Println("Challenge will starts at", config.Start, "in", startSub)
|
||||
|
||||
if _, err := os.Stat(startedFile); !os.IsNotExist(err) {
|
||||
os.Remove(startedFile)
|
||||
}
|
||||
|
||||
touchTimer = time.AfterFunc(config.Start.Sub(time.Now().Add(time.Duration(1*time.Second))), touchStartedFile)
|
||||
} else {
|
||||
log.Println("Challenge started at", config.Start, "since", -startSub)
|
||||
touchStartedFile()
|
||||
}
|
||||
log.Println("Challenge ends on", config.End)
|
||||
|
||||
challengeStart = config.Start
|
||||
challengeEnd = config.End
|
||||
} else {
|
||||
log.Println("Configuration reloaded, but start/end times doesn't change.")
|
||||
}
|
||||
|
||||
enableResolutionRoute = config.EnableResolutionRoute
|
||||
denyNameChange = config.DenyNameChange
|
||||
acceptNewIssues = config.AcceptNewIssue
|
||||
allowRegistration = config.AllowRegistration
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
)
|
||||
|
||||
var staticDir = "static"
|
||||
|
||||
func serveIndex(w http.ResponseWriter, r *http.Request) {
|
||||
http.ServeFile(w, r, path.Join(staticDir, "index.html"))
|
||||
}
|
||||
|
||||
type TeamMyServer struct {
|
||||
path2Dir string
|
||||
}
|
||||
|
||||
func (s TeamMyServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if _, err := os.Stat(startedFile); os.IsNotExist(err) {
|
||||
http.ServeFile(w, r, path.Join(s.path2Dir, "wait.json"))
|
||||
} else {
|
||||
http.ServeFile(w, r, path.Join(s.path2Dir, "my.json"))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type submissionHandler func(w http.ResponseWriter, r *http.Request, sURL []string)
|
||||
|
||||
type submissionChecker struct {
|
||||
kind string
|
||||
next submissionHandler
|
||||
}
|
||||
|
||||
type submissionTeamHandler func(w http.ResponseWriter, r *http.Request, team string, sURL []string)
|
||||
|
||||
type submissionTeamChecker struct {
|
||||
kind string
|
||||
next submissionTeamHandler
|
||||
teamsDir string
|
||||
simulator string
|
||||
}
|
||||
|
||||
func (c submissionChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if addr := r.Header.Get("X-Forwarded-For"); addr != "" {
|
||||
r.RemoteAddr = addr
|
||||
}
|
||||
team := "-"
|
||||
if t := r.Header.Get("X-FIC-Team"); t != "" {
|
||||
team = t
|
||||
}
|
||||
log.Printf("%s %s \"%s %s\" => %s [%s]\n", r.RemoteAddr, team, r.Method, r.URL.Path, c.kind, r.UserAgent())
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
// Check request type and size
|
||||
if r.Method != "POST" || r.ContentLength < 0 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if r.ContentLength > 4097 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête trop longue\"}", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract URL arguments
|
||||
var sURL = strings.Split(strings.TrimPrefix(r.URL.Path, "/"), "/")
|
||||
|
||||
c.next(w, r, sURL)
|
||||
}
|
||||
|
||||
func (c submissionTeamChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
submissionChecker{c.kind, func(w http.ResponseWriter, r *http.Request, sURL []string) {
|
||||
team := c.simulator
|
||||
if t := r.Header.Get("X-FIC-Team"); t != "" {
|
||||
team = t
|
||||
}
|
||||
|
||||
// Check team validity and existance
|
||||
if len(team) < 1 || team == "-" || team == "public" {
|
||||
log.Println("INVALID TEAM:", team)
|
||||
http.Error(w, "{\"errmsg\":\"Équipe inexistante.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if _, err := os.Stat(path.Join(c.teamsDir, team)); os.IsNotExist(err) {
|
||||
log.Println("UNKNOWN TEAM:", team)
|
||||
http.Error(w, "{\"errmsg\":\"Équipe inexistante.\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
c.next(w, r, team, sURL)
|
||||
}}.ServeHTTP(w, r)
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SubmissionHandler(w http.ResponseWriter, r *http.Request, team string, sURL []string) {
|
||||
if challengeEnd != nil && time.Now().After(*challengeEnd) {
|
||||
http.Error(w, "{\"errmsg\":\"Vous ne pouvez plus soumettre, le challenge est terminé.\"}", http.StatusGone)
|
||||
return
|
||||
}
|
||||
|
||||
if len(sURL) != 1 {
|
||||
http.Error(w, "{\"errmsg\":\"Arguments manquants.\"}", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Check exercice validity then save the submission
|
||||
if pex, err := strconv.ParseInt(sURL[0], 10, 64); err != nil {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if exercice := fmt.Sprintf("%d", pex); len(exercice) < 1 {
|
||||
http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest)
|
||||
return
|
||||
} else if saveTeamFile(path.Join(team, exercice), w, r) {
|
||||
http.Error(w, "{\"errmsg\":\"Son traitement est en cours...\"}", http.StatusAccepted)
|
||||
}
|
||||
}
|
||||