initial commit from original sev version

This commit is contained in:
Bartal Læarsson
2026-06-25 12:03:26 +01:00
commit 9c157cc5a0
31 changed files with 468875 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
package handlers
import (
"html/template"
"abuse_registration_poc/internal/auth"
"abuse_registration_poc/internal/store"
"abuse_registration_poc/internal/web"
)
type Dependencies struct {
Store *store.MemoryStore
Users *auth.UserStore
Version string
AppName string
BaseURL string
}
type Handler struct {
store *store.MemoryStore
users *auth.UserStore
index *template.Template
version string
appName string
baseURL string
}
func New(deps Dependencies) (*Handler, error) {
index, err := template.ParseFS(web.Templates, "templates/index.html")
if err != nil {
return nil, err
}
return &Handler{
store: deps.Store,
users: deps.Users,
index: index,
version: deps.Version,
appName: deps.AppName,
baseURL: deps.BaseURL,
}, nil
}
+19
View File
@@ -0,0 +1,19 @@
package handlers
import (
"net/http"
"time"
)
func (h *Handler) Health(writer http.ResponseWriter, request *http.Request) {
rows, lastReset, nextReset := h.store.Snapshot()
WriteJSON(writer, http.StatusOK, map[string]any{
"status": "ok",
"service": h.appName,
"version": h.version,
"dataset_count": len(rows),
"reset_interval": h.store.ResetInterval().String(),
"last_reset": lastReset.Format(time.RFC3339),
"next_reset": nextReset.Format(time.RFC3339),
})
}
+32
View File
@@ -0,0 +1,32 @@
package handlers
import (
"net/http"
"time"
"abuse_registration_poc/internal/models"
)
func (h *Handler) Index(writer http.ResponseWriter, request *http.Request) {
_, _, nextReset := h.store.Snapshot()
data := struct {
Version string
NextReset string
Locations []string
Categories []string
Genders []string
Statuses []string
}{
Version: h.version,
NextReset: nextReset.Format(time.RFC3339),
Locations: models.FaroeLocations,
Categories: models.AbuseCategories,
Genders: models.Genders,
Statuses: models.Statuses,
}
writer.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := h.index.ExecuteTemplate(writer, "index.html", data); err != nil {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
}
+128
View File
@@ -0,0 +1,128 @@
package handlers
import (
"net/http"
"strconv"
"strings"
"time"
"abuse_registration_poc/internal/models"
)
func (h *Handler) DemoRegistrations(writer http.ResponseWriter, request *http.Request) {
WriteJSON(writer, http.StatusOK, h.store.List(filterFromQuery(request)))
}
func (h *Handler) ListRegistrations(writer http.ResponseWriter, request *http.Request) {
WriteJSON(writer, http.StatusOK, h.store.List(filterFromQuery(request)))
}
func (h *Handler) GetRegistration(writer http.ResponseWriter, request *http.Request, id string) {
registration, ok := h.store.Get(id)
if !ok {
WriteJSON(writer, http.StatusNotFound, map[string]string{"message": "Registration not found."})
return
}
WriteJSON(writer, http.StatusOK, registration)
}
func (h *Handler) CreateRegistration(writer http.ResponseWriter, request *http.Request) {
var input models.Registration
if err := DecodeJSON(request, &input); err != nil {
WriteJSON(writer, http.StatusBadRequest, map[string]string{"message": "Could not parse request data."})
return
}
created, err := h.store.Create(input)
if err != nil {
WriteJSON(writer, http.StatusBadRequest, map[string]string{"message": err.Error()})
return
}
WriteJSON(writer, http.StatusCreated, created)
}
func (h *Handler) UpdateRegistration(writer http.ResponseWriter, request *http.Request, id string) {
var input models.Registration
if err := DecodeJSON(request, &input); err != nil {
WriteJSON(writer, http.StatusBadRequest, map[string]string{"message": "Could not parse request data."})
return
}
updated, ok, err := h.store.Replace(id, input)
if err != nil {
WriteJSON(writer, http.StatusBadRequest, map[string]string{"message": err.Error()})
return
}
if !ok {
WriteJSON(writer, http.StatusNotFound, map[string]string{"message": "Registration not found."})
return
}
WriteJSON(writer, http.StatusOK, updated)
}
func (h *Handler) DeleteRegistration(writer http.ResponseWriter, request *http.Request, id string) {
if !h.store.Delete(id) {
WriteJSON(writer, http.StatusNotFound, map[string]string{"message": "Registration not found."})
return
}
WriteJSON(writer, http.StatusOK, map[string]any{"message": "Registration deleted successfully.", "id": id})
}
func (h *Handler) ResetRegistrations(writer http.ResponseWriter, request *http.Request) {
h.store.Reset()
rows, _, nextReset := h.store.Snapshot()
WriteJSON(writer, http.StatusOK, map[string]any{
"message": "Dataset reset to base sample data.",
"dataset_count": len(rows),
"next_reset": nextReset.Format(time.RFC3339),
})
}
func (h *Handler) Categories(writer http.ResponseWriter, request *http.Request) {
WriteJSON(writer, http.StatusOK, models.AbuseCategories)
}
func (h *Handler) Locations(writer http.ResponseWriter, request *http.Request) {
WriteJSON(writer, http.StatusOK, models.FaroeLocations)
}
func filterFromQuery(request *http.Request) models.RegistrationFilter {
query := request.URL.Query()
filter := models.RegistrationFilter{
Location: query.Get("location"),
AbuseType: query.Get("abuse_type"),
Gender: query.Get("gender"),
Status: query.Get("status"),
Search: query.Get("search"),
Limit: queryInt(query.Get("limit"), 0),
Offset: queryInt(query.Get("offset"), 0),
}
if from, ok := queryTime(query.Get("from")); ok {
filter.From = from
filter.HasFrom = true
}
if to, ok := queryTime(query.Get("to")); ok {
filter.To = to
filter.HasTo = true
}
return filter
}
func queryInt(value string, fallback int) int {
value = strings.TrimSpace(value)
if value == "" {
return fallback
}
parsed, err := strconv.Atoi(value)
if err != nil || parsed < 0 {
return fallback
}
return parsed
}
func queryTime(value string) (time.Time, bool) {
parsed, err := models.ParseInputTime(value)
if err != nil {
return time.Time{}, false
}
return parsed, true
}
+24
View File
@@ -0,0 +1,24 @@
package handlers
import (
"encoding/json"
"net/http"
)
func WriteJSON(writer http.ResponseWriter, status int, payload any) {
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
writer.WriteHeader(status)
_ = json.NewEncoder(writer).Encode(payload)
}
func WriteText(writer http.ResponseWriter, status int, message string) {
writer.Header().Set("Content-Type", "text/plain; charset=utf-8")
writer.WriteHeader(status)
_, _ = writer.Write([]byte(message))
}
func DecodeJSON(request *http.Request, target any) error {
decoder := json.NewDecoder(request.Body)
decoder.DisallowUnknownFields()
return decoder.Decode(target)
}
+35
View File
@@ -0,0 +1,35 @@
package handlers
import (
"log"
"net/http"
"abuse_registration_poc/internal/models"
"abuse_registration_poc/internal/utils"
)
// Login mirrors the original API login handler shape: validate user credentials,
// generate a JWT, and return {message, token}.
func (h *Handler) Login(writer http.ResponseWriter, request *http.Request) {
var user models.ValidateUser
if err := DecodeJSON(request, &user); err != nil {
log.Printf("Error parsing request data: %v", err)
WriteJSON(writer, http.StatusBadRequest, map[string]string{"message": "Could not parse request data."})
return
}
if err := h.users.ValidateCredentials(&user); err != nil {
log.Printf("Error validating credentials: %v", err)
WriteJSON(writer, http.StatusUnauthorized, map[string]string{"message": "Could not authenticate user."})
return
}
token, err := utils.GenerateToken(user.UserName, user.ID, user.Role)
if err != nil {
log.Printf("Error generating token: %v", err)
WriteJSON(writer, http.StatusInternalServerError, map[string]string{"message": "Could not authenticate user."})
return
}
WriteJSON(writer, http.StatusOK, map[string]string{"message": "Login successful!", "token": token})
}