Settings are now given through TEAMS/settings.json instead of been given through...
[fic/server.git] / backend / main.go
1 package main
2
3 import (
4 "flag"
5 "fmt"
6 "io/ioutil"
7 "log"
8 "math/rand"
9 "os"
10 "path"
11 "strings"
12 "time"
13
14 "srs.epita.fr/fic-server/libfic"
15 "srs.epita.fr/fic-server/settings"
16
17 "gopkg.in/fsnotify.v1"
18 )
19
20 var TeamsDir string
21 var SubmissionDir string
22
23 func watchsubdir(watcher *fsnotify.Watcher, pathname string) error {
24 log.Println("Watch new directory:", pathname)
25 if err := watcher.Add(pathname); err != nil {
26 return err
27 }
28
29 if ds, err := ioutil.ReadDir(pathname); err != nil {
30 return err
31 } else {
32 for _, d := range ds {
33 p := path.Join(pathname, d.Name())
34 if d.IsDir() {
35 if err := watchsubdir(watcher, p); err != nil {
36 return err
37 }
38 } else if d.Mode().IsRegular() {
39 go treat(p)
40 }
41 }
42 return nil
43 }
44 }
45
46 func reloadSettings(config settings.FICSettings) {
47 fic.PartialValidation = config.PartialValidation
48 fic.UnlockedChallenges = !config.EnableExerciceDepend
49
50 fic.FirstBlood = config.FirstBlood
51 fic.SubmissionCostBase = config.SubmissionCostBase
52
53 log.Println("Generating files...")
54 go func() {
55 genAll()
56 log.Println("Full generation done")
57 }()
58 }
59
60 func main() {
61 var dsn = flag.String("dsn", "fic:fic@/fic", "DSN to connect to the MySQL server")
62 flag.StringVar(&SubmissionDir, "submission", "./submissions", "Base directory where save submissions")
63 flag.StringVar(&TeamsDir, "teams", "./TEAMS", "Base directory where save teams JSON files")
64 flag.StringVar(&fic.FilesDir, "files", "/files", "Request path prefix to reach files")
65 flag.Parse()
66
67 log.SetPrefix("[backend] ")
68
69 SubmissionDir = path.Clean(SubmissionDir)
70 TeamsDir = path.Clean(TeamsDir)
71
72 rand.Seed(time.Now().UnixNano())
73
74 log.Println("Creating submission directory...")
75 if _, err := os.Stat(SubmissionDir); os.IsNotExist(err) {
76 if err := os.MkdirAll(SubmissionDir, 0777); err != nil {
77 log.Fatal("Unable to create submission directory: ", err)
78 }
79 }
80
81 log.Println("Opening DB...")
82 if err := fic.DBInit(fmt.Sprintf("%s?parseTime=true", *dsn)); err != nil {
83 log.Fatal("Cannot open the database: ", err)
84 }
85 defer fic.DBClose()
86
87 // Load configuration
88 settings.LoadAndWatchSettings(path.Join(TeamsDir, settings.SettingsFile), reloadSettings)
89
90 log.Println("Registering directory events...")
91 watcher, err := fsnotify.NewWatcher()
92 if err != nil {
93 log.Fatal(err)
94 }
95 defer watcher.Close()
96
97 if err := watchsubdir(watcher, SubmissionDir); err != nil {
98 log.Fatal(err)
99 }
100
101 for {
102 select {
103 case ev := <-watcher.Events:
104 if ev.Op & fsnotify.Create == fsnotify.Create {
105 // Register new subdirectory
106 if d, err := os.Stat(ev.Name); err == nil && d.IsDir() {
107 if err := watchsubdir(watcher, ev.Name); err != nil {
108 log.Println(err)
109 }
110 }
111 } else if ev.Op & fsnotify.Write == fsnotify.Write {
112 go treat(ev.Name)
113 }
114 case err := <-watcher.Errors:
115 log.Println("error:", err)
116 }
117 }
118 }
119
120 func treat(raw_path string) {
121 // Extract
122 spath := strings.Split(strings.TrimPrefix(raw_path, SubmissionDir), "/")
123
124 if len(spath) == 3 {
125 if spath[1] == "_registration" {
126 treatRegistration(raw_path)
127 } else if team, err := fic.GetTeamByInitialName(spath[1]); err != nil {
128 log.Println("[ERR]", err)
129 } else if spath[2] == "name" {
130 treatRename(raw_path, team)
131 } else if spath[2] == "hint" {
132 treatOpeningHint(raw_path, team)
133 } else {
134 treatSubmission(raw_path, team, spath[2])
135 }
136 } else {
137 log.Println("Invalid new file:", raw_path)
138 }
139 }