Files
upaas/internal/handlers/setup.go
clawbot 7a34fc999c
All checks were successful
Check / check (push) Successful in 4s
Update golangci-lint to v2.12.2 with canonical config (#187)
Bumps golangci-lint from v2.10.1 to v2.12.2 everywhere it is pinned and installs the canonical `.golangci.yml`, then fixes every finding the new linter surfaces so `make check` is green.

## Version pins

- `Dockerfile` lint stage: `golangci/golangci-lint:v2.12.2` (Debian-based), tag plus digest pin
- `script/bootstrap`: `GOLANGCI_LINT_VERSION=2.12.2` with updated `linux-amd64`/`linux-arm64` release-archive sha256 pins

## Config

`.golangci.yml` replaced with the canonical config. Material change: the old file declared `version: "2"` but kept settings under the legacy top-level `linters-settings` key, which golangci-lint v2 ignores — so the intended thresholds (`lll` 88, `funlen` 80/50, `cyclop` 15, `dupl` 100) were not being applied. The canonical file moves them under `linters.settings` and drops `issues.exclude-use-default`.

## Lint fixes (216 findings)

- `lll` (96): wrapped lines to the 88-column limit
- `noctx` (46): `httptest.NewRequestWithContext` with `t.Context()` throughout the tests
- `goconst` (24): shared constants for template/JSON keys in `internal/handlers` and repeated test literals
- `gosec` (23): app-page redirects now go through a `redirectToApp` helper that path-escapes the app ID (G710 open redirect); `http.ServeFile` of the internally derived deployment log path annotated like the adjacent `os.Stat` (G703)
- `dupl` (22): extracted a generic `findAllByAppID` in `internal/models`, a `deleteAppResource` helper in `internal/handlers`, a shared `parsePush` in `internal/service/webhook`, and table-driven/helper-based dedup in tests
- `nolintlint` (5): removed `//nolint:funlen` directives made obsolete by the new limits (plus one more that became obsolete after refactoring)
- `nilerr` (3, surfaced during fixing): resource-delete lookups now propagate the find error to the caller

No behavior changes intended; all tests pass and `make check` is green.

Note: golangci-lint v2.12 warns that `gomodguard` is deprecated in favor of `gomodguard_v2` — a future canonical-config update should address this centrally.
Co-authored-by: sneak <sneak@sneak.berlin>
Reviewed-on: #187
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-08-07 22:21:42 +02:00

117 lines
2.8 KiB
Go

package handlers
import (
"net/http"
"sneak.berlin/go/upaas/templates"
)
const (
// minPasswordLength is the minimum required password length.
minPasswordLength = 8
)
// HandleSetupGET returns the setup page handler.
func (h *Handlers) HandleSetupGET() http.HandlerFunc {
tmpl := templates.GetParsed()
return func(writer http.ResponseWriter, request *http.Request) {
data := h.addGlobals(map[string]any{}, request)
h.renderTemplate(writer, tmpl, "setup.html", data)
}
}
// setupFormData holds form data for the setup page.
type setupFormData struct {
username string
password string
passwordConfirm string
}
// validateSetupForm validates the setup form and returns an error message if invalid.
func validateSetupForm(formData setupFormData) string {
if formData.username == "" || formData.password == "" {
return "Username and password are required"
}
if len(formData.password) < minPasswordLength {
return "Password must be at least 8 characters"
}
if formData.password != formData.passwordConfirm {
return "Passwords do not match"
}
return ""
}
// renderSetupError renders the setup page with an error message.
func (h *Handlers) renderSetupError(
tmpl *templates.TemplateExecutor,
writer http.ResponseWriter,
request *http.Request,
username string,
errorMsg string,
) {
data := h.addGlobals(map[string]any{
"Username": username,
dataKeyError: errorMsg,
}, request)
h.renderTemplate(writer, tmpl, "setup.html", data)
}
// HandleSetupPOST handles the setup form submission.
func (h *Handlers) HandleSetupPOST() http.HandlerFunc {
tmpl := templates.GetParsed()
return func(writer http.ResponseWriter, request *http.Request) {
parseErr := request.ParseForm()
if parseErr != nil {
http.Error(writer, "Bad Request", http.StatusBadRequest)
return
}
formData := setupFormData{
username: request.FormValue("username"),
password: request.FormValue("password"),
passwordConfirm: request.FormValue("password_confirm"),
}
if validationErr := validateSetupForm(formData); validationErr != "" {
h.renderSetupError(tmpl, writer, request, formData.username, validationErr)
return
}
user, createErr := h.auth.CreateUser(
request.Context(),
formData.username,
formData.password,
)
if createErr != nil {
h.log.Error("failed to create user", "error", createErr)
h.renderSetupError(tmpl, writer, request, formData.username, "Failed to create user")
return
}
sessionErr := h.auth.CreateSession(writer, request, user)
if sessionErr != nil {
h.log.Error("failed to create session", "error", sessionErr)
h.renderSetupError(
tmpl,
writer,
request,
formData.username,
"Failed to create session",
)
return
}
http.Redirect(writer, request, "/", http.StatusSeeOther)
}
}