Rendering status as HTML tables.

This commit is contained in:
Erik Brakkee 2024-07-31 21:12:09 +02:00
parent 885b7790d7
commit 4c52fb0f12
6 changed files with 145 additions and 18 deletions

View File

@ -140,7 +140,11 @@ func main() {
Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) { Handler: func(w http.ResponseWriter, r *http.Request, conn net.Conn) {
websession := websessions.NewSession(conn) websession := websessions.NewSession(conn)
defer websessions.SessionClosed(websession) defer websessions.SessionClosed(websession)
websession.WriteNotifications() location, err := converge.GetUserLocation(r)
if err != nil {
panic(err)
}
websession.WriteNotifications(location)
}, },
Text: true, Text: true,
} }

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"converge/pkg/server/converge"
templates2 "converge/pkg/server/templates" templates2 "converge/pkg/server/templates"
"net/http" "net/http"
"os" "os"
@ -24,6 +25,11 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
} }
username, _ := os.LookupEnv("CONVERGE_USERNAME") username, _ := os.LookupEnv("CONVERGE_USERNAME")
location, err := converge.GetUserLocation(r)
if err != nil {
panic(err)
}
switch r.URL.Path { switch r.URL.Path {
case "": case "":
fallthrough fallthrough
@ -36,7 +42,7 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
case "downloads.html": case "downloads.html":
templates2.DownloadsTab().Render(r.Context(), w) templates2.DownloadsTab().Render(r.Context(), w)
case "sessions.html": case "sessions.html":
templates2.SessionsTab().Render(r.Context(), w) templates2.SessionsTab(nil, location).Render(r.Context(), w)
default: default:
http.NotFound(w, r) http.NotFound(w, r)
} }

View File

@ -2,11 +2,14 @@ package main
import ( import (
"context" "context"
"converge/pkg/comms"
"converge/pkg/models"
templates2 "converge/pkg/server/templates" templates2 "converge/pkg/server/templates"
"github.com/a-h/templ" "github.com/a-h/templ"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"time"
) )
type RenderFunc func() templ.Component type RenderFunc func() templ.Component
@ -29,6 +32,11 @@ func render(dir string, name string, render RenderFunc) {
func main() { func main() {
dir := "html/docs" dir := "html/docs"
netherlands, err := time.LoadLocation("Europe/Amsterdam")
if err != nil {
panic(err)
}
fullindex := func() templ.Component { fullindex := func() templ.Component {
return templates2.Index("s", "example.com", "converge") return templates2.Index("s", "example.com", "converge")
} }
@ -41,5 +49,37 @@ func main() {
render(dir, "usage.html", usage) render(dir, "usage.html", usage)
render(dir, "downloads.html", templates2.Downloads) render(dir, "downloads.html", templates2.Downloads)
render(dir, "sessions.html", templates2.SessionsTab) render(dir, "sessions-none.html", func() templ.Component {
return templates2.SessionsTab(nil, netherlands)
})
render(dir, "sessions.html", func() templ.Component {
japan, err := time.LoadLocation("Asia/Tokyo")
if err != nil {
panic(err)
}
state := models.State{}
agent := models.Agent{
PublicId: "id",
StartTime: time.Now().In(japan),
AgentInfo: comms.AgentInfo{
Username: "ci",
Hostname: "container123",
Pwd: "/home/ci",
OS: "linux",
Shell: "/bin/bash",
},
ExpiryTime: time.Now().In(japan).Add(10 * time.Minute),
}
state.Agents = append(state.Agents, agent)
client := models.Client{
PublicId: "c1",
ClientId: 3,
StartTime: time.Now().In(japan),
SessionType: "sftp",
}
state.Clients = append(state.Clients, client)
return templates2.SessionsTab(&state, netherlands)
})
} }

View File

@ -27,7 +27,5 @@ spec:
- name: CONVERGE_PASSWORD - name: CONVERGE_PASSWORD
# change this password in your final deployment # change this password in your final deployment
value: "abc123" value: "abc123"
- name: TZ
value: Europe/Amsterdam

View File

@ -6,6 +6,7 @@ import (
"converge/pkg/server/templates" "converge/pkg/server/templates"
"log" "log"
"net" "net"
"net/http"
"sync" "sync"
"time" "time"
) )
@ -57,7 +58,25 @@ func (sessions *WebSessions) NewSession(wsConnection net.Conn) *WebSession {
return session return session
} }
func (session *WebSession) WriteNotifications() { func GetUserLocation(r *http.Request) (*time.Location, error) {
// Try to get timezone from a custom header
tzName := r.Header.Get("X-Timezone")
// If not found in header, try to get from query parameter
if tzName == "" {
tzName = r.URL.Query().Get("tz")
}
// If still not found, default to UTC
if tzName == "" {
tzName = "UTC"
}
// Load the location
return time.LoadLocation(tzName)
}
func (session *WebSession) WriteNotifications(location *time.Location) {
timer := time.NewTicker(10 * time.Second) timer := time.NewTicker(10 * time.Second)
defer timer.Stop() defer timer.Stop()
for { for {
@ -66,8 +85,8 @@ func (session *WebSession) WriteNotifications() {
if !ok { if !ok {
log.Println("channel closed") log.Println("channel closed")
} }
log.Println("Got notification: ", notification.Ascii) //log.Println("Got notification: ", notification)
err := templates.State(notification).Render(context.Background(), session.conn) err := templates.State(notification, location).Render(context.Background(), session.conn)
if err != nil { if err != nil {
log.Printf("WS connection closed: %v", err) log.Printf("WS connection closed: %v", err)
return return

View File

@ -1,34 +1,94 @@
package templates package templates
import "converge/pkg/models" import (
"converge/pkg/models"
"strconv"
"time"
_ "time/tzdata"
)
templ Sessions(state *models.State) { templ Sessions(state *models.State, loc *time.Location) {
<div hx-ext="ws" ws-connect="/ws/sessions"> <div hx-ext="ws" ws-connect="/ws/sessions">
<h1>sessions</h1> <h1>sessions</h1>
<div id="status"> <div id="status">
if state != nil { if state != nil {
@State(state) @State(state, loc)
} else { } else {
"Loading..." Loading...
} }
</div> </div>
</div> </div>
} }
templ State(state *models.State) { templ State(state *models.State, location *time.Location) {
<div id="status"> <div id="status">
<pre>
{ state.Ascii } <h3>agents</h3>
</pre>
if len(state.Agents) == 0 {
<p>-</p>
} else {
<table class="table">
<thead>
<tr>
<th>id</th>
<th>start time</th>
<th>expiry time</th>
<th>username</th>
<th>host</th>
<th>OS</th>
<th>shell</th>
</tr>
</thead>
for _, agent := range state.Agents {
<tr>
<td>{agent.PublicId}</td>
<td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
<td>{agent.ExpiryTime.In(location).Format(time.DateTime)}</td>
<td>{agent.AgentInfo.Username}</td>
<td>{agent.AgentInfo.Hostname}</td>
<td>{agent.AgentInfo.OS}</td>
<td>{agent.AgentInfo.Shell}</td>
</tr>
}
</table>
}
<h3>clients</h3>
if len(state.Clients) == 0 {
<p>-</p>
} else {
<table class="table">
<thead>
<tr>
<th>agent id</th>
<th>id</th>
<th>start time</th>
<th>session type</th>
</tr>
</thead>
for _, client := range state.Clients {
<tr>
<td>{client.PublicId}</td>
<td>{strconv.Itoa(client.ClientId)}</td>
<td>{client.StartTime.In(location).Format(time.DateTime)}</td>
<td>{client.SessionType}</td>
</tr>
}
</table>
}
</div> </div>
} }
templ SessionsTab() { templ SessionsTab(state *models.State, loc *time.Location) {
@BasePage(4) { @BasePage(4) {
@Sessions(nil) @Sessions(state, loc)
} }
} }