673 lines
21 KiB
Go
673 lines
21 KiB
Go
package main
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/base64"
|
|
"net/http"
|
|
"regexp"
|
|
)
|
|
|
|
type difficultyLevel struct {
|
|
ID int
|
|
LevelName string
|
|
}
|
|
|
|
func adminLoginHandler(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
err := adminLoginTemplate.Execute(w, false)
|
|
if err != nil {
|
|
http.Error(w, "Could not render template", http.StatusInternalServerError)
|
|
}
|
|
case http.MethodPost:
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
username := r.FormValue("username")
|
|
password := r.FormValue("password")
|
|
err := db.QueryRow(
|
|
"SELECT 1 FROM admins WHERE username=? AND PASSWORD=?",
|
|
username, hashPassword(password),
|
|
).Scan(new(int))
|
|
switch {
|
|
case err == sql.ErrNoRows:
|
|
err := adminLoginTemplate.Execute(w, true)
|
|
if err != nil {
|
|
http.Error(w, "Could not render template", http.StatusInternalServerError)
|
|
}
|
|
case err != nil:
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
default:
|
|
http.SetCookie(w, &http.Cookie{
|
|
Name: "admin_session",
|
|
Value: base64.StdEncoding.EncodeToString([]byte(username + ":" + hashPassword(password))),
|
|
Path: "/admin/",
|
|
HttpOnly: true,
|
|
SameSite: http.SameSiteStrictMode,
|
|
Secure: true,
|
|
})
|
|
http.Redirect(w, r, "/admin/", http.StatusSeeOther)
|
|
}
|
|
default:
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func adminLogoutHandler(w http.ResponseWriter, r *http.Request) {
|
|
http.SetCookie(w, &http.Cookie{Name: "admin_session", Value: "", Path: "/admin/", HttpOnly: true, MaxAge: -1})
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
}
|
|
|
|
func isAdmin(r *http.Request) bool {
|
|
cookie, err := r.Cookie("admin_session")
|
|
if err != nil {
|
|
return false
|
|
}
|
|
decoded, err := base64.StdEncoding.DecodeString(cookie.Value)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
var username, passwordHash string
|
|
regex := regexp.MustCompile(`^([^:]+):([a-f0-9]+)$`)
|
|
matches := regex.FindStringSubmatch(string(decoded))
|
|
if len(matches) != 3 {
|
|
return false
|
|
}
|
|
username = matches[1]
|
|
passwordHash = matches[2]
|
|
err = db.QueryRow("SELECT 1 FROM admins WHERE username=? AND PASSWORD=?", username, passwordHash).Scan(new(int))
|
|
if err != sql.ErrNoRows && err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func adminHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.ServeFileFS(w, r, templatesFS, "templates/adminPanel.html")
|
|
}
|
|
|
|
func adminTeamsHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing team
|
|
if r.PostForm.Has("delete") {
|
|
teamName := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM teams WHERE name = ?", teamName)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/teams", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new team
|
|
teamName := r.FormValue("name")
|
|
difficulty := r.FormValue("difficulty")
|
|
password := r.FormValue("password")
|
|
if teamName == "" || difficulty == "" || password == "" {
|
|
http.Error(w, "All fields are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO teams (name, difficulty_level, password, last_cipher, penalty) VALUES (?, ?, ?, 1, 0)", teamName, difficulty, hashPassword(password))
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/teams", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Fetch all teams with their difficulty levels
|
|
// Teams
|
|
rows, err := db.Query("SELECT teams.id, name, difficulty_levels.level_name, last_cipher, last_loaded_cipher, penalty FROM teams JOIN difficulty_levels ON teams.difficulty_level = difficulty_levels.id ORDER BY teams.difficulty_level, teams.name")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var teams []TeamTemplateS
|
|
for rows.Next() {
|
|
var team TeamTemplateS
|
|
if err := rows.Scan(&team.ID, &team.TeamName, &team.Difficulty, &team.LastCipher, &team.LastLoadedCipher, &team.Penalties); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
teams = append(teams, team)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// Difficulty levels for the dropdown
|
|
rows, err = db.Query("SELECT id, level_name FROM difficulty_levels ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var difficultyLevels []DifficultyLevelS
|
|
for rows.Next() {
|
|
var level DifficultyLevelS
|
|
if err := rows.Scan(&level.ID, &level.Name); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
difficultyLevels = append(difficultyLevels, level)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
teamsData := TeamsTemplateS{
|
|
Teams: teams,
|
|
Difficulties: difficultyLevels,
|
|
}
|
|
if err := AdminTeamsTemplate.Execute(w, teamsData); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminStartHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
_, err := db.Exec("UPDATE teams SET last_cipher = 1, last_loaded_cipher = 0, penalty = 0")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
_, err = db.Exec("DELETE FROM penalties")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/", http.StatusSeeOther)
|
|
}
|
|
|
|
func AdminRouteHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing route point
|
|
if r.PostForm.Has("delete") {
|
|
cipherID := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM tasks WHERE id = ?", cipherID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/routes", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new route point
|
|
order := r.FormValue("order")
|
|
level := r.FormValue("level")
|
|
position := r.FormValue("position")
|
|
cipher := r.FormValue("cipher")
|
|
endClue := r.FormValue("endclue")
|
|
if order == "" || level == "" || position == "" || cipher == "" || endClue == "" {
|
|
http.Error(w, "All fields are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO tasks (order_num, difficulty_level, position_id, cipher_id, end_clue) VALUES (?, ?, ?, ?, ?)", order, level, position, cipher, endClue)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/routes", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Fetch all ciphers for the dropdown
|
|
rows, err := db.Query("SELECT id FROM ciphers ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var ciphers []int
|
|
for rows.Next() {
|
|
var cipher int
|
|
if err := rows.Scan(&cipher); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
ciphers = append(ciphers, cipher)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// Fetch all positions for the dropdown
|
|
rows, err = db.Query("SELECT id FROM positions ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var positions []int
|
|
for rows.Next() {
|
|
var position int
|
|
if err := rows.Scan(&position); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
positions = append(positions, position)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// Levels for the dropdown
|
|
rows, err = db.Query("SELECT id FROM difficulty_levels ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var levels []int
|
|
for rows.Next() {
|
|
var level int
|
|
if err := rows.Scan(&level); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
levels = append(levels, level)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// Fetch all difficulty levels
|
|
rows, err = db.Query("SELECT id, level_name FROM difficulty_levels ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var difficultyLevels []difficultyLevel
|
|
for rows.Next() {
|
|
var level difficultyLevel
|
|
if err := rows.Scan(&level.ID, &level.LevelName); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
difficultyLevels = append(difficultyLevels, level)
|
|
}
|
|
// For each difficulty level, fetch the corresponding tasks and their details
|
|
var routes []AdminRouteTemplateS
|
|
for _, level := range difficultyLevels {
|
|
var route AdminRouteTemplateS
|
|
rows, err := db.Query("SELECT tasks.id, tasks.order_num, CIPHERS.assignment, CIPHERS.clue, tasks.end_clue, POSITIONS.gps, POSITIONS.clue, CIPHERS.solution, COALESCE(QR_CODES.uid, '') FROM TASKS JOIN CIPHERS ON TASKS.cipher_id = ciphers.id JOIN POSITIONS on TASKS.position_id = POSITIONS.id LEFT JOIN QR_CODES ON QR_CODES.position_id = POSITIONS.id WHERE TASKS.difficulty_level=? ORDER BY TASKS.order_num;", level.ID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
route.Name = level.LevelName
|
|
for rows.Next() {
|
|
var cipher CipherTemplateS
|
|
if err := rows.Scan(&cipher.ID, &cipher.Order, &cipher.Assignment, &cipher.HelpText, &cipher.FinalClue, &cipher.Coordinates, &cipher.PositionHint, &cipher.Solution, &cipher.URL); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
cipher.URL = domain + "/qr/" + cipher.URL
|
|
route.Ciphers = append(route.Ciphers, cipher)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
routes = append(routes, route)
|
|
}
|
|
// Prepare data for the template
|
|
data := AdminRoutesTemplateS{
|
|
Routes: routes,
|
|
Levels: levels,
|
|
Positions: positions,
|
|
Ciphers: ciphers,
|
|
}
|
|
// Render the template
|
|
if err := AdminRoutesTemplate.Execute(w, data); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminLevelHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing difficulty level
|
|
if r.PostForm.Has("delete") {
|
|
id := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM difficulty_levels WHERE id = ?", id)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/levels", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new difficulty level
|
|
levelName := r.FormValue("name")
|
|
if levelName == "" {
|
|
http.Error(w, "Level name cannot be empty", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO difficulty_levels (level_name) VALUES (?)", levelName)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/levels", http.StatusSeeOther)
|
|
return
|
|
}
|
|
rows, err := db.Query("SELECT id, level_name FROM difficulty_levels ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var difficultyLevels []AdminLevelTemplateS
|
|
for rows.Next() {
|
|
var level AdminLevelTemplateS
|
|
if err := rows.Scan(&level.ID, &level.Name); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
difficultyLevels = append(difficultyLevels, level)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := AdminLevelTemplate.Execute(w, difficultyLevels); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminCipherHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing cipher
|
|
if r.PostForm.Has("delete") {
|
|
cipherID := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM ciphers WHERE id = ?", cipherID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/cipher", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new cipher
|
|
assignment := r.FormValue("assignment")
|
|
solution := r.FormValue("solution")
|
|
clue := r.FormValue("clue")
|
|
if assignment == "" || solution == "" || clue == "" {
|
|
http.Error(w, "All fields are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO ciphers (assignment, solution, clue) VALUES (?, ?, ?)", assignment, solution, clue)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/cipher", http.StatusSeeOther)
|
|
return
|
|
}
|
|
rows, err := db.Query("SELECT id, assignment, solution, clue FROM ciphers ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var ciphers []AdminCipherTemplateS
|
|
for rows.Next() {
|
|
var cipher AdminCipherTemplateS
|
|
if err := rows.Scan(&cipher.ID, &cipher.Assignment, &cipher.Solution, &cipher.Clue); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
ciphers = append(ciphers, cipher)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := AdminCipherTemplate.Execute(w, ciphers); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminPositionsHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing position
|
|
if r.PostForm.Has("delete") {
|
|
positionID := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM positions WHERE id = ?", positionID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/positions", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.PostForm.Has("update") {
|
|
// Updating an existing position
|
|
positionID := r.FormValue("update")
|
|
gps := r.FormValue("gps")
|
|
if gps == "" {
|
|
http.Error(w, "GPS field cannot be empty", http.StatusBadRequest)
|
|
return
|
|
}
|
|
clue := r.FormValue("clue")
|
|
if clue == "" {
|
|
http.Error(w, "Clue field cannot be empty", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("UPDATE positions SET gps = ?, clue = ? WHERE id = ?", gps, clue, positionID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/positions", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new position
|
|
gps := r.FormValue("gps")
|
|
clue := r.FormValue("clue")
|
|
if gps == "" || clue == "" {
|
|
http.Error(w, "All fields are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO positions (gps, clue) VALUES (?, ?)", gps, clue)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/positions", http.StatusSeeOther)
|
|
return
|
|
}
|
|
rows, err := db.Query("SELECT positions.id, gps, clue, COALESCE(uid, '') FROM positions LEFT JOIN QR_CODES ON positions.id = QR_CODES.position_id ORDER BY positions.id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var positions []AdminPositionsTemplateS
|
|
for rows.Next() {
|
|
var position AdminPositionsTemplateS
|
|
if err := rows.Scan(&position.ID, &position.GPS, &position.Clue, &position.URL); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
position.URL = domain + "/qr/" + position.URL
|
|
positions = append(positions, position)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := AdminPositionsTemplate.Execute(w, positions); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminQRHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
if r.Method == http.MethodPost {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
|
return
|
|
}
|
|
// Deleting an existing QR code
|
|
if r.PostForm.Has("delete") {
|
|
qrID := r.FormValue("delete")
|
|
_, err := db.Exec("DELETE FROM qr_codes WHERE id = ?", qrID)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/qr", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Adding a new QR code
|
|
positionID := r.FormValue("position")
|
|
uid := r.FormValue("uid")
|
|
if positionID == "" || uid == "" {
|
|
http.Error(w, "All fields are required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
_, err := db.Exec("INSERT INTO qr_codes (position_id, uid) VALUES (?, ?)", positionID, uid)
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/admin/qr", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Fetch all QR codes with their associated positions
|
|
rows, err := db.Query("SELECT qr_codes.id, qr_codes.uid, COALESCE(position_id, ''), COALESCE(gps, '') FROM qr_codes LEFT JOIN positions ON qr_codes.position_id = positions.id ORDER BY qr_codes.id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var qrs []AdminQRsTemplateS
|
|
for rows.Next() {
|
|
var qr AdminQRsTemplateS
|
|
if err := rows.Scan(&qr.ID, &qr.URL, &qr.Position, &qr.GPS); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
qr.URL = domain + "/qr/" + qr.URL
|
|
qrs = append(qrs, qr)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
// Fetch all positions for the dropdown
|
|
rows, err = db.Query("SELECT id FROM positions ORDER BY id")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var positions []int
|
|
for rows.Next() {
|
|
var position int
|
|
if err := rows.Scan(&position); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
positions = append(positions, position)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := AdminQRsTemplate.Execute(w, AdminQRTemplateS{QRs: qrs, Positions: positions}); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
func AdminPenaltiesHandler(w http.ResponseWriter, r *http.Request) {
|
|
if !isAdmin(r) {
|
|
http.Redirect(w, r, "/admin/login", http.StatusSeeOther)
|
|
return
|
|
}
|
|
// Fetch all penalties with team names and task orders
|
|
rows, err := db.Query("SELECT teams.name, tasks.order_num, penalties.minutes FROM penalties JOIN teams ON penalties.team_id = teams.id JOIN tasks ON penalties.task_id = tasks.id ORDER BY teams.name, tasks.order_num")
|
|
if err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
defer rows.Close()
|
|
var penalties []AdminPenaltiesTemplateS
|
|
for rows.Next() {
|
|
var penalty AdminPenaltiesTemplateS
|
|
if err := rows.Scan(&penalty.TeamName, &penalty.TaskOrder, &penalty.Minutes); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
penalties = append(penalties, penalty)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
http.Error(w, "Database error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if err := AdminPenaltiesTemplate.Execute(w, penalties); err != nil {
|
|
http.Error(w, "Template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|