package main import ( "encoding/json" "log" "math/rand" "net/http" "os" "path/filepath" "strconv" "strings" "text/template" "git.andreafazzi.eu/andrea/probo/models" "git.andreafazzi.eu/andrea/probo/store/file" ) var ( DefaultDataDir = "data" DefaultSessionDir = "sessions" DefaultTemplateDir = "templates" DefaultStaticDir = "static" ) type Config struct { SessionDir string TemplateDir string StaticDir string } type ExamSession []*models.Exam type ExamTemplateData struct { *models.Exam SessionID string } type Server struct { config *Config mux *http.ServeMux responseStore *file.ResponseFileStore } func GetDefaultSessionDir() string { return filepath.Join(DefaultDataDir, DefaultSessionDir) } func GetDefaultTemplateDir() string { return DefaultTemplateDir } func GetDefaultStaticDir() string { return DefaultStaticDir } func NewServer(config *Config) (*Server, error) { _, err := os.Stat(config.SessionDir) if err != nil { return nil, err } _, err = os.Stat(config.TemplateDir) if err != nil { return nil, err } _, err = os.Stat(config.StaticDir) if err != nil { return nil, err } s := &Server{ config, http.NewServeMux(), nil, } s.mux.Handle("/static/", http.StripPrefix("/static", http.FileServer(http.Dir(config.StaticDir)))) s.mux.HandleFunc("/create", s.createExamSessionHandler) s.mux.HandleFunc("/", s.getExamHandler) return s, nil } func NewDefaultServer() (*Server, error) { return NewServer(&Config{ SessionDir: GetDefaultSessionDir(), TemplateDir: GetDefaultTemplateDir(), StaticDir: GetDefaultStaticDir(), }) } func (s *Server) createExamSessionHandler(w http.ResponseWriter, r *http.Request) { var p ExamSession err := json.NewDecoder(r.Body).Decode(&p) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } id := generateRandomID() path := filepath.Join(s.config.SessionDir, id) err = os.MkdirAll(path, os.ModePerm) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } for _, exam := range p { file, err := os.Create(filepath.Join(path, strconv.Itoa(exam.Participant.Token)) + ".json") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } defer file.Close() err = json.NewEncoder(file).Encode(exam) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } response := map[string]string{"id": id} json.NewEncoder(w).Encode(response) } func (s *Server) getExamHandler(w http.ResponseWriter, r *http.Request) { urlParts := strings.Split(r.URL.Path, "/") examID := urlParts[1] token := urlParts[2] filePath := filepath.Join(s.config.SessionDir, examID, token+".json") data, err := os.ReadFile(filePath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } exam := new(models.Exam) err = json.Unmarshal(data, &exam) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if r.Method == "GET" { w.Header().Set("Content-Type", "text/html") tplData, err := os.ReadFile(filepath.Join(GetDefaultTemplateDir(), "exam.tpl")) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } tmpl := template.Must(template.New("exam").Parse(string(tplData))) err = tmpl.Execute(w, ExamTemplateData{exam, examID}) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } } if r.Method == "POST" { err := r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } w.Header().Set("Content-Type", "text/html") if r.FormValue("answer") == exam.Quizzes[0].Correct.ID { w.Write([]byte("

Corretto!

")) return } w.Write([]byte("

Errato!

")) return } } func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.mux.ServeHTTP(w, r) } func generateRandomID() string { id := "" for i := 0; i < 6; i++ { id += strconv.Itoa(rand.Intn(9) + 1) } return id } func main() { server, err := NewDefaultServer() if err != nil { panic(err) } log.Println("Probo server started.", "Config", server.config) http.ListenAndServe(":8080", server) }