b5bde59f10
Replace all runtime template.ParseFiles() calls with template.ParseFS() using an embedded filesystem, and serve static assets from an embedded FS via http.FileServerFS(). This eliminates the need for COPY steps in the Dockerfile and ensures the binary works with readOnlyRootFilesystem: true. - Add internal/templates/embed.go exposing templates.FS - Add static/embed.go exposing static.FS - Update all handlers to use template.ParseFS(templates.FS, ...) - Update static file server to use http.FileServerFS(static.FS) - Remove COPY static/ and COPY internal/templates/ from Dockerfile - Remove TestMain working directory hack (no longer needed) Closes leeworks-agents/gitea-mobile#231 Closes leeworks-agents/gitea-mobile#220 Closes leeworks-agents/gitea-mobile#221 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
3.0 KiB
Go
114 lines
3.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"html/template"
|
|
"log/slog"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/auth"
|
|
"gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/middleware"
|
|
"gitea.leeworks.dev/0xwheatyz/gitea-mobile/internal/templates"
|
|
)
|
|
|
|
// SettingsHandler handles GET and POST requests for the settings page.
|
|
type SettingsHandler struct {
|
|
SessionSecret string
|
|
SecureCookies bool
|
|
}
|
|
|
|
type settingsData struct {
|
|
HasToken bool
|
|
Message string
|
|
MessageType string // "success", "error", "info"
|
|
}
|
|
|
|
// ServeHTTP handles the settings page.
|
|
func (h *SettingsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
h.handleGet(w, r)
|
|
case http.MethodPost:
|
|
h.handlePost(w, r)
|
|
default:
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
}
|
|
}
|
|
|
|
func (h *SettingsHandler) handleGet(w http.ResponseWriter, r *http.Request) {
|
|
hasToken := false
|
|
if token := middleware.TokenFromContext(r.Context()); token != "" {
|
|
hasToken = true
|
|
} else if _, err := auth.GetToken(r, h.SessionSecret); err == nil {
|
|
hasToken = true
|
|
}
|
|
|
|
data := settingsData{HasToken: hasToken}
|
|
|
|
// Show error banner when redirected due to expired/revoked token.
|
|
if r.URL.Query().Get("error") == "token_expired" {
|
|
data.Message = "Your Gitea API token is expired or has been revoked. Please enter a new token."
|
|
data.MessageType = "error"
|
|
}
|
|
|
|
h.renderSettings(w, data)
|
|
}
|
|
|
|
func (h *SettingsHandler) handlePost(w http.ResponseWriter, r *http.Request) {
|
|
if err := r.ParseForm(); err != nil {
|
|
h.renderWithMessage(w, r, "Failed to parse form.", "error")
|
|
return
|
|
}
|
|
|
|
action := r.FormValue("action")
|
|
|
|
switch action {
|
|
case "logout":
|
|
auth.ClearTokenCookie(w, h.SecureCookies)
|
|
h.renderWithMessage(w, r, "Token removed successfully.", "success")
|
|
return
|
|
|
|
case "save":
|
|
token := strings.TrimSpace(r.FormValue("token"))
|
|
if token == "" {
|
|
h.renderWithMessage(w, r, "Token cannot be empty.", "error")
|
|
return
|
|
}
|
|
|
|
auth.SetTokenCookie(w, token, h.SessionSecret, h.SecureCookies)
|
|
// After saving, redirect to dashboard.
|
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
|
return
|
|
|
|
default:
|
|
h.renderWithMessage(w, r, "Unknown action.", "error")
|
|
}
|
|
}
|
|
|
|
func (h *SettingsHandler) renderWithMessage(w http.ResponseWriter, r *http.Request, msg, msgType string) {
|
|
hasToken := false
|
|
if _, err := auth.GetToken(r, h.SessionSecret); err == nil {
|
|
hasToken = true
|
|
}
|
|
|
|
data := settingsData{
|
|
HasToken: hasToken,
|
|
Message: msg,
|
|
MessageType: msgType,
|
|
}
|
|
h.renderSettings(w, data)
|
|
}
|
|
|
|
func (h *SettingsHandler) renderSettings(w http.ResponseWriter, data settingsData) {
|
|
tmpl, err := template.ParseFS(templates.FS, "settings.html")
|
|
if err != nil {
|
|
slog.Error("failed to parse settings template", "error", err)
|
|
http.Error(w, "template error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
if err := tmpl.Execute(w, data); err != nil {
|
|
slog.Error("failed to execute settings template", "error", err)
|
|
}
|
|
}
|