server/receiver/main.go

149 lines
6.0 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"path"
"strings"
"syscall"
"time"
"srs.epita.fr/fic-server/settings"
)
func main() {
bind := "127.0.0.1:8080"
prefix := "/"
teamsDir := "./TEAMS/"
settings.SettingsDir = "./SETTINGSDIST"
// Read paremeters from environment
if v, exists := os.LookupEnv("FIC_BASEURL"); exists {
prefix = v
}
if v, exists := os.LookupEnv("FIC_RECEIVER_BIND"); exists {
bind = v
}
if v, exists := os.LookupEnv("FIC_SETTINGSDIST"); exists {
settings.SettingsDir = v
}
if v, exists := os.LookupEnv("FIC_STARTED_FILE"); exists {
startedFile = v
}
if v, exists := os.LookupEnv("FIC_SUBMISSIONS_DIRECTORY"); exists {
SubmissionDir = v
}
if v, exists := os.LookupEnv("FIC_TEAMS_DIRECTORY"); exists {
teamsDir = v
}
flag.StringVar(&bind, "bind", bind, "Bind port/socket")
flag.StringVar(&prefix, "prefix", prefix, "Request path prefix to strip (from proxy)")
flag.StringVar(&teamsDir, "teams", teamsDir, "Base directory where find existing teams")
flag.StringVar(&settings.SettingsDir, "settings", settings.SettingsDir, "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", SubmissionDir, "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("[receiver] ")
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/reset_progress", prefix), http.StripPrefix(fmt.Sprintf("%s/reset_progress", prefix), submissionTeamChecker{"reset_progress", ResetProgressHandler, teamsDir, *simulator}))
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,
ReadHeaderTimeout: 15 * time.Second,
ReadTimeout: 15 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 30 * time.Second,
}
// 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")
}