enhance utilisation of postgres features
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-04-20 10:19:27 +10:00
parent 98e92a8264
commit 8ccf5a7009
28 changed files with 2836 additions and 422 deletions
+5 -5
View File
@@ -8,14 +8,14 @@ templ Header() {
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="vCTP dashboard and API endpoint"/>
<meta name="color-scheme" content="light"/>
<meta name="theme-color" content="#1b61c9"/>
<meta name="theme-color" content="#195fc8"/>
<title>vCTP API</title>
<link rel="icon" href="/favicon.ico"/>
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/>
<link rel="icon" href={ "/favicon.ico?v=" + version.Value }/>
<link rel="icon" type="image/png" sizes="16x16" href={ "/favicon-16x16.png?v=" + version.Value }/>
<link rel="icon" type="image/png" sizes="32x32" href={ "/favicon-32x32.png?v=" + version.Value }/>
<script src="/assets/js/htmx@v2.0.2.min.js"></script>
<script src={ "/assets/js/web3-charts.js?v=" + version.Value }></script>
<link href={ "/assets/css/output@" + version.Value + ".css" } rel="stylesheet"/>
<link href="/assets/css/web3.css" rel="stylesheet"/>
<link href={ "/assets/css/web3.css?v=" + version.Value } rel="stylesheet"/>
</head>
}
+60 -8
View File
@@ -31,33 +31,85 @@ func Header() templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent
}
ctx = templ.ClearChildren(ctx)
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"vCTP dashboard and API endpoint\"><meta name=\"color-scheme\" content=\"light\"><meta name=\"theme-color\" content=\"#1b61c9\"><title>vCTP API</title><link rel=\"icon\" href=\"/favicon.ico\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/favicon-16x16.png\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/favicon-32x32.png\"><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><script src=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><meta name=\"description\" content=\"vCTP dashboard and API endpoint\"><meta name=\"color-scheme\" content=\"light\"><meta name=\"theme-color\" content=\"#195fc8\"><title>vCTP API</title><link rel=\"icon\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/js/web3-charts.js?v=" + version.Value)
var templ_7745c5c3_Var2 templ.SafeURL
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon.ico?v=" + version.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 17, Col: 62}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 13, Col: 59}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"></script><link href=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var3 templ.SafeURL
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/output@" + version.Value + ".css")
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon-16x16.png?v=" + version.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 18, Col: 61}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 14, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\" rel=\"stylesheet\"><link href=\"/assets/css/web3.css\" rel=\"stylesheet\"></head>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "\"><link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var4 templ.SafeURL
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinURLErrs("/favicon-32x32.png?v=" + version.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 15, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><script src=\"/assets/js/htmx@v2.0.2.min.js\"></script><script src=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs("/assets/js/web3-charts.js?v=" + version.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 17, Col: 62}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"></script><link href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var6 templ.SafeURL
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/output@" + version.Value + ".css")
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 18, Col: 61}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" rel=\"stylesheet\"><link href=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var7 templ.SafeURL
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinURLErrs("/assets/css/web3.css?v=" + version.Value)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/header.templ`, Line: 19, Col: 56}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\" rel=\"stylesheet\"></head>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
+17 -3
View File
@@ -12,6 +12,20 @@ type SegmentedLink struct {
Class string
}
func actionLinkClass(class string) string {
if class == "" {
return "web2-button"
}
return class
}
func segmentedLinkClass(class string) string {
if class == "" {
return "web3-button"
}
return class
}
templ PageHeader(pill string, title string, subtitle string, actions []ActionLink) {
<div class="web2-page-head-row">
<div class="web2-head-copy">
@@ -26,7 +40,7 @@ templ PageHeader(pill string, title string, subtitle string, actions []ActionLin
if len(actions) > 0 {
<div class="web2-actions">
for _, action := range actions {
<a class={ action.Class } href={ action.Href }>{ action.Label }</a>
<a class={ actionLinkClass(action.Class) } href={ action.Href }>{ action.Label }</a>
}
</div>
}
@@ -37,7 +51,7 @@ templ SegmentedActions(actions []SegmentedLink) {
if len(actions) > 0 {
<div class="web3-button-group">
for _, action := range actions {
<a class={ action.Class } href={ action.Href }>{ action.Label }</a>
<a class={ segmentedLinkClass(action.Class) } href={ action.Href }>{ action.Label }</a>
}
</div>
}
@@ -45,7 +59,7 @@ templ SegmentedActions(actions []SegmentedLink) {
templ SectionHead(title string, badge string) {
<div class="web2-section-head">
<h2>{ title }</h2>
<h2 class="web2-section-title">{ title }</h2>
if badge != "" {
<span class="web2-badge">{ badge }</span>
}
+17 -6
View File
@@ -1,6 +1,9 @@
package views
import "vctp/components/core"
import (
"strings"
"vctp/components/core"
)
type BuildInfo struct {
BuildTime string
@@ -8,6 +11,14 @@ type BuildInfo struct {
GoVersion string
}
func truncateSHA(sha string) string {
trimmed := strings.TrimSpace(sha)
if len(trimmed) <= 14 {
return trimmed
}
return trimmed[:14] + "..."
}
templ Index(info BuildInfo) {
<!DOCTYPE html>
<html lang="en">
@@ -41,15 +52,15 @@ templ Index(info BuildInfo) {
</div>
<div class="web2-card">
<p class="web2-kpi-label">SHA1 Version</p>
<p class="web2-kpi-value">{ info.SHA1Ver }</p>
<p class="web2-kpi-value web2-kpi-value-mono web2-kpi-truncate" title={ info.SHA1Ver }>{ truncateSHA(info.SHA1Ver) }</p>
</div>
<div class="web2-card">
<p class="web2-kpi-label">Go Runtime</p>
<p class="web2-kpi-value">{ info.GoVersion }</p>
</div>
</section>
<section class="grid gap-6 lg:grid-cols-3">
<div class="web2-card">
<section class="web2-index-sections">
<div class="web2-card web2-card-overview web2-index-overview">
<h2 class="mb-2">Overview</h2>
<p class="web2-page-subtitle">
vCTP is a vSphere Chargeback Tracking Platform.
@@ -61,7 +72,7 @@ templ Index(info BuildInfo) {
Use <code class="web2-code">/api/auth/me</code> to inspect active claims and roles during integration and diagnostics.
</p>
</div>
<div class="web2-card">
<div class="web2-card web2-card-featured web2-index-featured">
<h2 class="mb-2">Snapshots and Reports</h2>
<div class="web2-paragraphs web2-page-subtitle">
<p>Hourly snapshots capture inventory per vCenter (concurrency via <code class="web2-code">hourly_snapshot_concurrency</code>), then daily and monthly summaries are derived from those snapshots.</p>
@@ -76,7 +87,7 @@ templ Index(info BuildInfo) {
<p>Monthly aggregation reports include a Daily Totals sheet with full-day interval labels (YYYY-MM-DD to YYYY-MM-DD) and prorated totals.</p>
</div>
</div>
<div class="web2-card">
<div class="web2-card web2-index-wide">
<h2 class="mb-2">Prorating and Aggregation</h2>
<div class="web2-paragraphs web2-page-subtitle">
<p><code class="web2-code">SamplesPresent</code> is the count of snapshots in which the VM appears; <code class="web2-code">TotalSamples</code> is the count of unique snapshot times for that vCenter/day.</p>
File diff suppressed because one or more lines are too long