feat: add simple audit ui

This commit is contained in:
Christoph Haas
2025-03-29 16:42:31 +01:00
parent a49cfa6343
commit 6cbccf6d43
23 changed files with 2098 additions and 1557 deletions

View File

@@ -11,6 +11,29 @@
},
"basePath": "/api/v0",
"paths": {
"/audit/entries": {
"get": {
"produces": [
"application/json"
],
"tags": [
"Audit"
],
"summary": "Get all available audit entries. Ordered by timestamp.",
"operationId": "audit_handleEntriesGet",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/model.AuditEntry"
}
}
}
}
}
},
"/auth/login": {
"post": {
"produces": [
@@ -171,6 +194,9 @@
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error"
}
}
}
@@ -1494,6 +1520,30 @@
}
},
"definitions": {
"model.AuditEntry": {
"type": "object",
"properties": {
"Message": {
"type": "string"
},
"ctx_user": {
"type": "string"
},
"id": {
"type": "integer"
},
"origin": {
"description": "origin: for example user auth, stats, ...",
"type": "string"
},
"severity": {
"type": "string"
},
"timestamp": {
"type": "string"
}
}
},
"model.ConfigOption-array_string": {
"type": "object",
"properties": {

View File

@@ -1,5 +1,21 @@
basePath: /api/v0
definitions:
model.AuditEntry:
properties:
Message:
type: string
ctx_user:
type: string
id:
type: integer
origin:
description: 'origin: for example user auth, stats, ...'
type: string
severity:
type: string
timestamp:
type: string
type: object
model.ConfigOption-array_string:
properties:
Overridable:
@@ -419,6 +435,21 @@ info:
title: WireGuard Portal SPA-UI API
version: "0.0"
paths:
/audit/entries:
get:
operationId: audit_handleEntriesGet
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/model.AuditEntry'
type: array
summary: Get all available audit entries. Ordered by timestamp.
tags:
- Audit
/auth/{provider}/callback:
get:
operationId: auth_handleOauthCallbackGet
@@ -523,6 +554,8 @@ paths:
description: The JavaScript contents
schema:
type: string
"500":
description: Internal Server Error
summary: Get the dynamic frontend configuration javascript.
tags:
- Configuration

View File

@@ -0,0 +1,69 @@
package handlers
import (
"context"
"net/http"
"github.com/go-pkgz/routegroup"
"github.com/h44z/wg-portal/internal/app/api/core/respond"
"github.com/h44z/wg-portal/internal/app/api/v0/model"
"github.com/h44z/wg-portal/internal/config"
"github.com/h44z/wg-portal/internal/domain"
)
type AuditService interface {
// GetAll returns all audit entries ordered by timestamp. Newest first.
GetAll(ctx context.Context) ([]domain.AuditEntry, error)
}
type AuditEndpoint struct {
cfg *config.Config
authenticator Authenticator
auditService AuditService
}
func NewAuditEndpoint(
cfg *config.Config,
authenticator Authenticator,
auditService AuditService,
) AuditEndpoint {
return AuditEndpoint{
cfg: cfg,
authenticator: authenticator,
auditService: auditService,
}
}
func (e AuditEndpoint) GetName() string {
return "AuditEndpoint"
}
func (e AuditEndpoint) RegisterRoutes(g *routegroup.Bundle) {
apiGroup := g.Mount("/audit")
apiGroup.Use(e.authenticator.LoggedIn(ScopeAdmin))
apiGroup.HandleFunc("GET /entries", e.handleEntriesGet())
}
// handleExternalLoginProvidersGet returns a gorm Handler function.
//
// @ID audit_handleEntriesGet
// @Tags Audit
// @Summary Get all available audit entries. Ordered by timestamp.
// @Produce json
// @Success 200 {object} []model.AuditEntry
// @Router /audit/entries [get]
func (e AuditEndpoint) handleEntriesGet() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
providers, err := e.auditService.GetAll(r.Context())
if err != nil {
respond.JSON(w, http.StatusInternalServerError, model.Error{
Code: http.StatusInternalServerError, Message: err.Error(),
})
return
}
respond.JSON(w, http.StatusOK, model.NewAuditEntries(providers))
}
}

View File

@@ -0,0 +1,36 @@
package model
import (
"github.com/h44z/wg-portal/internal/domain"
)
type AuditEntry struct {
Id uint64 `json:"Id"`
Timestamp string `json:"Timestamp"`
ContextUser string `json:"ContextUser"`
Severity string `json:"Severity"`
Origin string `json:"Origin"` // origin: for example user auth, stats, ...
Message string `message:"Message"`
}
// NewAuditEntry creates a REST API AuditEntry from a domain AuditEntry.
func NewAuditEntry(src domain.AuditEntry) AuditEntry {
return AuditEntry{
Id: src.UniqueId,
Timestamp: src.CreatedAt.Format("2006-01-02 15:04:05"),
ContextUser: src.ContextUser,
Severity: string(src.Severity),
Origin: src.Origin,
Message: src.Message,
}
}
// NewAuditEntries creates a slice of REST API AuditEntry from a slice of domain AuditEntry.
func NewAuditEntries(src []domain.AuditEntry) []AuditEntry {
dst := make([]AuditEntry, 0, len(src))
for _, entry := range src {
dst = append(dst, NewAuditEntry(entry))
}
return dst
}