Add CORS support and response caching to aggregate endpoints
- Introduced CORS configuration options in config.example.yaml, allowing specification of allowed origins for cross-origin requests. - Enhanced the aggregate handler to support response caching with a configurable TTL, improving performance for repeated requests. - Updated the aggregate API to return a structured response indicating whether any upstream requests failed, enhancing error handling and response clarity. - Modified documentation in AGGREGATE.md and README.md to reflect the new CORS and caching features. - Added tests to validate the new functionality in the aggregate handler.
This commit is contained in:
@@ -3,6 +3,7 @@ package aggregate
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BuildTraffic builds traffic matrix from successful fetches only.
|
||||
@@ -140,6 +141,11 @@ func sortedKeys(m map[string]struct{}) []string {
|
||||
return s
|
||||
}
|
||||
|
||||
type userOnServer struct {
|
||||
alias string
|
||||
u UserInfo
|
||||
}
|
||||
|
||||
// BuildUsers merged rows with totals and optional links from first successful server per user.
|
||||
func BuildUsers(results []ServerFetchResult, includeLinks bool, minTotalOctets uint64) []UsersRow {
|
||||
type acc struct {
|
||||
@@ -150,6 +156,7 @@ func BuildUsers(results []ServerFetchResult, includeLinks bool, minTotalOctets u
|
||||
rec uint64
|
||||
}
|
||||
m := map[string]*acc{}
|
||||
perUserServers := map[string][]userOnServer{}
|
||||
|
||||
for _, fr := range results {
|
||||
if !fr.OK {
|
||||
@@ -176,6 +183,7 @@ func BuildUsers(results []ServerFetchResult, includeLinks bool, minTotalOctets u
|
||||
if includeLinks && u.Links != nil && a.links == nil {
|
||||
a.links = u.Links
|
||||
}
|
||||
perUserServers[u.Username] = append(perUserServers[u.Username], userOnServer{alias: fr.Alias, u: u})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,18 +198,99 @@ func BuildUsers(results []ServerFetchResult, includeLinks bool, minTotalOctets u
|
||||
rows := make([]UsersRow, 0, len(names))
|
||||
for _, name := range names {
|
||||
a := m[name]
|
||||
sort.Slice(perUserServers[name], func(i, j int) bool {
|
||||
return perUserServers[name][i].alias < perUserServers[name][j].alias
|
||||
})
|
||||
ad, mt, ex, dq, mu := mergeUserLimitFields(perUserServers[name])
|
||||
rows = append(rows, UsersRow{
|
||||
Username: name,
|
||||
TotalMegabytes: octetsToMegabytes(a.total),
|
||||
ByServer: a.byServer,
|
||||
Links: a.links,
|
||||
ActiveUniqueIPs: a.act,
|
||||
RecentUniqueIPs: a.rec,
|
||||
Username: name,
|
||||
TotalMegabytes: octetsToMegabytes(a.total),
|
||||
ByServer: a.byServer,
|
||||
Links: a.links,
|
||||
ActiveUniqueIPs: a.act,
|
||||
RecentUniqueIPs: a.rec,
|
||||
UserAdTag: ad,
|
||||
MaxTCPConns: mt,
|
||||
ExpirationRFC3339: ex,
|
||||
DataQuotaBytes: dq,
|
||||
MaxUniqueIPs: mu,
|
||||
})
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// BuildSingleUser returns one merged user row or nil if the user is absent on all successful upstreams.
|
||||
func BuildSingleUser(results []ServerFetchResult, username string, includeLinks bool) *UsersRow {
|
||||
rows := BuildUsers(results, includeLinks, 0)
|
||||
for i := range rows {
|
||||
if rows[i].Username == username {
|
||||
return &rows[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergeUserLimitFields(rows []userOnServer) (userAdTag *string, maxTCP *uint64, exp *string, quota *uint64, maxUip *uint64) {
|
||||
for _, r := range rows {
|
||||
if r.u.UserAdTag != nil && userAdTag == nil {
|
||||
v := *r.u.UserAdTag
|
||||
userAdTag = &v
|
||||
}
|
||||
}
|
||||
var earliest time.Time
|
||||
var earliestStr string
|
||||
haveEarliest := false
|
||||
for _, r := range rows {
|
||||
if r.u.ExpirationRFC3339 == nil {
|
||||
continue
|
||||
}
|
||||
s := *r.u.ExpirationRFC3339
|
||||
t, err := time.Parse(time.RFC3339Nano, s)
|
||||
if err != nil {
|
||||
t, err = time.Parse(time.RFC3339, s)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if !haveEarliest || t.Before(earliest) {
|
||||
earliest = t
|
||||
earliestStr = s
|
||||
haveEarliest = true
|
||||
}
|
||||
}
|
||||
if haveEarliest {
|
||||
exp = &earliestStr
|
||||
}
|
||||
for _, r := range rows {
|
||||
if r.u.MaxTCPConns != nil {
|
||||
v := *r.u.MaxTCPConns
|
||||
if maxTCP == nil || v < *maxTCP {
|
||||
maxTCP = new(uint64)
|
||||
*maxTCP = v
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range rows {
|
||||
if r.u.DataQuotaBytes != nil {
|
||||
v := *r.u.DataQuotaBytes
|
||||
if quota == nil || v < *quota {
|
||||
quota = new(uint64)
|
||||
*quota = v
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, r := range rows {
|
||||
if r.u.MaxUniqueIPs != nil {
|
||||
v := *r.u.MaxUniqueIPs
|
||||
if maxUip == nil || v < *maxUip {
|
||||
maxUip = new(uint64)
|
||||
*maxUip = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return userAdTag, maxTCP, exp, quota, maxUip
|
||||
}
|
||||
|
||||
// BuildSummary computes fleet totals and top users.
|
||||
func BuildSummary(results []ServerFetchResult, topN int) SummaryData {
|
||||
if topN <= 0 {
|
||||
|
||||
Reference in New Issue
Block a user