166 lines
5.6 KiB
Go
166 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"strconv"
|
|
)
|
|
|
|
func (app *App) handleGetSettings(w http.ResponseWriter, r *http.Request) {
|
|
cfg := app.loadSMTPConfig()
|
|
|
|
baseURL := app.baseURL
|
|
if baseURL == "" {
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'base_url'`).Scan(&baseURL)
|
|
}
|
|
|
|
pass := ""
|
|
if cfg.Password != "" {
|
|
pass = "***"
|
|
}
|
|
|
|
var noteLabel, noteRequired, signupsOpen string
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'volunteer_note_label'`).Scan(¬eLabel)
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'volunteer_note_required'`).Scan(¬eRequired)
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'shift_signups_open'`).Scan(&signupsOpen)
|
|
if noteLabel == "" {
|
|
noteLabel = "Additional note"
|
|
}
|
|
|
|
var ssoURL, ssoSecret string
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'discourse_sso_url'`).Scan(&ssoURL)
|
|
app.db.QueryRow(`SELECT value FROM config WHERE key = 'discourse_sso_secret'`).Scan(&ssoSecret)
|
|
maskedSSOSecret := ""
|
|
if ssoSecret != "" {
|
|
maskedSSOSecret = "***"
|
|
}
|
|
|
|
writeJSON(w, map[string]any{
|
|
"smtp_host": cfg.Host,
|
|
"smtp_port": cfg.Port,
|
|
"smtp_user": cfg.User,
|
|
"smtp_password": pass,
|
|
"smtp_from": cfg.From,
|
|
"smtp_from_name": cfg.FromName,
|
|
"base_url": baseURL,
|
|
"volunteer_note_label": noteLabel,
|
|
"volunteer_note_required": noteRequired == "true",
|
|
"shift_signups_open": signupsOpen == "true",
|
|
"discourse_sso_url": ssoURL,
|
|
"discourse_sso_secret": maskedSSOSecret,
|
|
})
|
|
}
|
|
|
|
func (app *App) handleUpdateSettings(w http.ResponseWriter, r *http.Request) {
|
|
var body map[string]any
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
writeError(w, "invalid request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
keys := []string{"smtp_host", "smtp_port", "smtp_user", "smtp_password", "smtp_from", "smtp_from_name", "base_url",
|
|
"volunteer_note_label", "volunteer_note_required", "discourse_sso_url", "discourse_sso_secret"}
|
|
for _, k := range keys {
|
|
v, ok := body[k]
|
|
if !ok {
|
|
continue
|
|
}
|
|
var val string
|
|
switch vv := v.(type) {
|
|
case string:
|
|
if (k == "smtp_password" || k == "discourse_sso_secret") && (vv == "" || vv == "***") {
|
|
continue
|
|
}
|
|
val = vv
|
|
case float64:
|
|
val = strconv.Itoa(int(vv))
|
|
case bool:
|
|
if vv {
|
|
val = "true"
|
|
} else {
|
|
val = "false"
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
app.db.Exec(`INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)`, k, val)
|
|
}
|
|
|
|
app.handleGetSettings(w, r)
|
|
}
|
|
|
|
func (app *App) handleResetTickets(w http.ResponseWriter, r *http.Request) {
|
|
ts := now()
|
|
result, err := app.db.Exec(`UPDATE tickets SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL`, ts, ts)
|
|
if err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
writeJSON(w, map[string]any{"deleted": n})
|
|
}
|
|
|
|
func (app *App) handleResetVolunteers(w http.ResponseWriter, r *http.Request) {
|
|
ts := now()
|
|
result, err := app.db.Exec(`UPDATE volunteers SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL`, ts, ts)
|
|
if err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
writeJSON(w, map[string]any{"deleted": n})
|
|
}
|
|
|
|
func (app *App) handleResetShifts(w http.ResponseWriter, r *http.Request) {
|
|
ts := now()
|
|
result, err := app.db.Exec(`UPDATE shifts SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL`, ts, ts)
|
|
if err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
// Also soft-delete orphaned volunteer_shifts
|
|
app.db.Exec(`UPDATE volunteer_shifts SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL AND shift_id IN (SELECT id FROM shifts WHERE deleted_at IS NOT NULL)`, ts, ts)
|
|
writeJSON(w, map[string]any{"deleted": n})
|
|
}
|
|
|
|
func (app *App) handleResetDepartments(w http.ResponseWriter, r *http.Request) {
|
|
ts := now()
|
|
// Soft-delete shifts in these departments first (so sync picks them up)
|
|
app.db.Exec(`UPDATE volunteer_shifts SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL AND shift_id IN (SELECT id FROM shifts WHERE department_id IN (SELECT id FROM departments WHERE deleted_at IS NULL))`, ts, ts)
|
|
app.db.Exec(`UPDATE shifts SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL AND department_id IN (SELECT id FROM departments WHERE deleted_at IS NULL)`, ts, ts)
|
|
result, err := app.db.Exec(`UPDATE departments SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL`, ts, ts)
|
|
if err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
writeJSON(w, map[string]any{"deleted": n})
|
|
}
|
|
|
|
func (app *App) handleResetVolunteerShifts(w http.ResponseWriter, r *http.Request) {
|
|
ts := now()
|
|
result, err := app.db.Exec(`UPDATE volunteer_shifts SET deleted_at=?, updated_at=? WHERE deleted_at IS NULL`, ts, ts)
|
|
if err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
n, _ := result.RowsAffected()
|
|
writeJSON(w, map[string]any{"deleted": n})
|
|
}
|
|
|
|
func (app *App) handleTestEmail(w http.ResponseWriter, r *http.Request) {
|
|
var body struct {
|
|
To string `json:"to"`
|
|
}
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.To == "" {
|
|
writeError(w, "to email address required", http.StatusBadRequest)
|
|
return
|
|
}
|
|
cfg := app.loadSMTPConfig()
|
|
if err := sendEmail(cfg, body.To, "Turnpike test email", "This is a test email from your Turnpike instance. SMTP is configured correctly."); err != nil {
|
|
writeError(w, err.Error(), http.StatusInternalServerError)
|
|
return
|
|
}
|
|
writeJSON(w, map[string]any{"ok": true})
|
|
}
|