• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DigitalTolk / wireguard-ui / 24835132478

23 Apr 2026 12:26PM UTC coverage: 82.607% (+1.5%) from 81.067%
24835132478

Pull #11

github

web-flow
Merge 68b85fd12 into 74711d7b9
Pull Request #11: Fixes

437 of 554 branches covered (78.88%)

Branch coverage included in aggregate %.

431 of 547 new or added lines in 19 files covered. (78.79%)

8 existing lines in 1 file now uncovered.

2878 of 3459 relevant lines covered (83.2%)

13.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.28
/handler/api_v1_audit.go
1
package handler
2

3
import (
4
        "fmt"
5
        "net/http"
6
        "strconv"
7

8
        "github.com/labstack/echo/v4"
9
        "github.com/xuri/excelize/v2"
10

11
        "github.com/DigitalTolk/wireguard-ui/audit"
12
)
13

14
// APIListAuditLogs returns paginated audit logs
15
func APIListAuditLogs(auditLog *audit.Logger) echo.HandlerFunc {
4✔
16
        return func(c echo.Context) error {
8✔
17
                from := c.QueryParam("from")
4✔
18
                to := c.QueryParam("to")
4✔
19
                actor := c.QueryParam("actor")
4✔
20
                action := c.QueryParam("action")
4✔
21
                search := c.QueryParam("search")
4✔
22
                page, _ := strconv.Atoi(c.QueryParam("page"))
4✔
23
                perPage, _ := strconv.Atoi(c.QueryParam("per_page"))
4✔
24

4✔
25
                entries, total, err := auditLog.Query(from, to, actor, action, search, page, perPage)
4✔
26
                if err != nil {
4✔
27
                        return apiInternalError(c, "Cannot query audit logs")
×
28
                }
×
29

30
                if entries == nil {
5✔
31
                        entries = []audit.LogEntry{}
1✔
32
                }
1✔
33

34
                return c.JSON(http.StatusOK, map[string]interface{}{
4✔
35
                        "data":     entries,
4✔
36
                        "total":    total,
4✔
37
                        "page":     page,
4✔
38
                        "per_page": perPage,
4✔
39
                })
4✔
40
        }
41
}
42

43
// APIExportAuditLogs exports audit logs as an Excel file
44
func APIExportAuditLogs(auditLog *audit.Logger) echo.HandlerFunc {
1✔
45
        return func(c echo.Context) error {
2✔
46
                from := c.QueryParam("from")
1✔
47
                to := c.QueryParam("to")
1✔
48
                actor := c.QueryParam("actor")
1✔
49
                action := c.QueryParam("action")
1✔
50
                search := c.QueryParam("search")
1✔
51

1✔
52
                entries, err := auditLog.QueryAll(from, to, actor, action, search)
1✔
53
                if err != nil {
1✔
54
                        return apiInternalError(c, "Cannot query audit logs")
×
55
                }
×
56

57
                f := excelize.NewFile()
1✔
58
                sheet := "Audit Logs"
1✔
59
                f.SetSheetName("Sheet1", sheet)
1✔
60

1✔
61
                // headers
1✔
62
                headers := []string{"ID", "Timestamp", "Actor", "Action", "Resource Type", "Resource ID", "Details", "IP Address"}
1✔
63
                for i, h := range headers {
9✔
64
                        cell, _ := excelize.CoordinatesToCellName(i+1, 1)
8✔
65
                        f.SetCellValue(sheet, cell, h)
8✔
66
                }
8✔
67

68
                // style headers bold
69
                style, _ := f.NewStyle(&excelize.Style{
1✔
70
                        Font: &excelize.Font{Bold: true},
1✔
71
                })
1✔
72
                f.SetCellStyle(sheet, "A1", fmt.Sprintf("%s1", string(rune('A'+len(headers)-1))), style)
1✔
73

1✔
74
                // data
1✔
75
                for row, e := range entries {
2✔
76
                        r := row + 2
1✔
77
                        f.SetCellValue(sheet, fmt.Sprintf("A%d", r), e.ID)
1✔
78
                        f.SetCellValue(sheet, fmt.Sprintf("B%d", r), e.Timestamp.Format("2006-01-02 15:04:05"))
1✔
79
                        f.SetCellValue(sheet, fmt.Sprintf("C%d", r), e.Actor)
1✔
80
                        f.SetCellValue(sheet, fmt.Sprintf("D%d", r), e.Action)
1✔
81
                        f.SetCellValue(sheet, fmt.Sprintf("E%d", r), e.ResourceType)
1✔
82
                        f.SetCellValue(sheet, fmt.Sprintf("F%d", r), e.ResourceID)
1✔
83
                        f.SetCellValue(sheet, fmt.Sprintf("G%d", r), e.Details)
1✔
84
                        f.SetCellValue(sheet, fmt.Sprintf("H%d", r), e.IPAddress)
1✔
85
                }
1✔
86

87
                // auto-width columns
88
                for i := range headers {
9✔
89
                        col, _ := excelize.ColumnNumberToName(i + 1)
8✔
90
                        f.SetColWidth(sheet, col, col, 20)
8✔
91
                }
8✔
92

93
                c.Response().Header().Set("Content-Disposition", "attachment; filename=audit-logs.xlsx")
1✔
94
                c.Response().Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
1✔
95
                return f.Write(c.Response())
1✔
96
        }
97
}
98

99
// APIAuditLogFilters returns distinct actors and actions for filter dropdowns
NEW
100
func APIAuditLogFilters(auditLog *audit.Logger) echo.HandlerFunc {
×
NEW
101
        return func(c echo.Context) error {
×
NEW
102
                actors, actions, err := auditLog.DistinctFilters()
×
NEW
103
                if err != nil {
×
NEW
104
                        return apiInternalError(c, "Cannot query audit log filters")
×
NEW
105
                }
×
NEW
106
                return c.JSON(http.StatusOK, map[string]interface{}{
×
NEW
107
                        "actors":  actors,
×
NEW
108
                        "actions": actions,
×
NEW
109
                })
×
110
        }
111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc