Server side sorting for the websessions page. No longer using LinkedMap as a result.
This commit is contained in:
		
							parent
							
								
									1de3c90146
								
							
						
					
					
						commit
						3574b64842
					
				@ -21,7 +21,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(nil, access.Location).Render(r.Context(), w)
 | 
							templates2.SessionsTab(nil, nil, access.Location).Render(r.Context(), w)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		http.NotFound(w, r)
 | 
							http.NotFound(w, r)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -24,10 +24,10 @@ func NewPrometheusState(state *models.State) *PrometheusState {
 | 
				
			|||||||
		agents:  collections.NewLinkedMap[models.AgentGuid, *models.Agent](),
 | 
							agents:  collections.NewLinkedMap[models.AgentGuid, *models.Agent](),
 | 
				
			||||||
		clients: collections.NewLinkedMap[models.ClientGuid, *models.Client](),
 | 
							clients: collections.NewLinkedMap[models.ClientGuid, *models.Client](),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for agent := range state.Agents.RangeValues() {
 | 
						for _, agent := range state.Agents {
 | 
				
			||||||
		res.agents.Put(agent.Guid, agent)
 | 
							res.agents.Put(agent.Guid, agent)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for client := range state.Clients.RangeValues() {
 | 
						for _, client := range state.Clients {
 | 
				
			||||||
		res.clients.Put(client.Guid, client)
 | 
							res.clients.Put(client.Guid, client)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &res
 | 
						return &res
 | 
				
			||||||
 | 
				
			|||||||
@ -70,7 +70,7 @@ func main() {
 | 
				
			|||||||
	render(dir, "downloads.html", templates2.Downloads)
 | 
						render(dir, "downloads.html", templates2.Downloads)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	render(dir, "sessions-none.html", func() templ.Component {
 | 
						render(dir, "sessions-none.html", func() templ.Component {
 | 
				
			||||||
		return templates2.SessionsTab(nil, netherlands)
 | 
							return templates2.SessionsTab(nil, nil, netherlands)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	render(dir, "sessions.html", func() templ.Component {
 | 
						render(dir, "sessions.html", func() templ.Component {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -94,7 +94,7 @@ func main() {
 | 
				
			|||||||
			},
 | 
								},
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		agent.SetExpiryTime(time.Now().In(japan).Add(10 * time.Minute))
 | 
							agent.SetExpiryTime(time.Now().In(japan).Add(10 * time.Minute))
 | 
				
			||||||
		state.Agents.Put(agent.Guid, &agent)
 | 
							state.Agents[agent.Guid] = &agent
 | 
				
			||||||
		client := models.Client{
 | 
							client := models.Client{
 | 
				
			||||||
			Guid:        models.ClientGuid(strconv.Itoa(rand.Int())),
 | 
								Guid:        models.ClientGuid(strconv.Itoa(rand.Int())),
 | 
				
			||||||
			RemoteAddr:  models.RemoteAddr("10.1.3.3"),
 | 
								RemoteAddr:  models.RemoteAddr("10.1.3.3"),
 | 
				
			||||||
@ -104,7 +104,9 @@ func main() {
 | 
				
			|||||||
			StartTime:   time.Now().In(japan),
 | 
								StartTime:   time.Now().In(japan),
 | 
				
			||||||
			SessionType: models.SessionType("sftp"),
 | 
								SessionType: models.SessionType("sftp"),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		state.Clients.Put(client.Guid, &client)
 | 
							state.Clients[client.Guid] = &client
 | 
				
			||||||
		return templates2.SessionsTab(state, netherlands)
 | 
					
 | 
				
			||||||
 | 
							agents, clients := state.Slices()
 | 
				
			||||||
 | 
							return templates2.SessionsTab(agents, clients, netherlands)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ package models
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"git.wamblee.org/converge/pkg/comms"
 | 
						"git.wamblee.org/converge/pkg/comms"
 | 
				
			||||||
	"git.wamblee.org/converge/pkg/support/collections"
 | 
						"sort"
 | 
				
			||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -56,25 +56,43 @@ type Client struct {
 | 
				
			|||||||
// Created by the server and used for updating the web client
 | 
					// Created by the server and used for updating the web client
 | 
				
			||||||
// and prometheus metrics.
 | 
					// and prometheus metrics.
 | 
				
			||||||
type State struct {
 | 
					type State struct {
 | 
				
			||||||
	Agents  *collections.LinkedMap[AgentGuid, *Agent]
 | 
						Agents  map[AgentGuid]*Agent
 | 
				
			||||||
	Clients *collections.LinkedMap[ClientGuid, *Client]
 | 
						Clients map[ClientGuid]*Client
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func NewState() *State {
 | 
					func NewState() *State {
 | 
				
			||||||
	return &State{
 | 
						return &State{
 | 
				
			||||||
		Agents:  collections.NewLinkedMap[AgentGuid, *Agent](),
 | 
							Agents:  make(map[AgentGuid]*Agent),
 | 
				
			||||||
		Clients: collections.NewLinkedMap[ClientGuid, *Client](),
 | 
							Clients: make(map[ClientGuid]*Client),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// for copy on write
 | 
					// for copy on write
 | 
				
			||||||
func (state *State) Copy() *State {
 | 
					func (state *State) Copy() *State {
 | 
				
			||||||
	res := NewState()
 | 
						res := NewState()
 | 
				
			||||||
	for entry := range state.Agents.RangeEntries() {
 | 
						for k, v := range state.Agents {
 | 
				
			||||||
		res.Agents.Put(entry.Key, entry.Value)
 | 
							res.Agents[k] = v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for entry := range state.Clients.RangeEntries() {
 | 
						for k, v := range state.Clients {
 | 
				
			||||||
		res.Clients.Put(entry.Key, entry.Value)
 | 
							res.Clients[k] = v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return res
 | 
						return res
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (state *State) Slices() ([]*Agent, []*Client) {
 | 
				
			||||||
 | 
						agents := make([]*Agent, 0, len(state.Agents))
 | 
				
			||||||
 | 
						clients := make([]*Client, 0, len(state.Clients))
 | 
				
			||||||
 | 
						for _, agent := range state.Agents {
 | 
				
			||||||
 | 
							agents = append(agents, agent)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, client := range state.Clients {
 | 
				
			||||||
 | 
							clients = append(clients, client)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Slice(agents, func(i int, j int) bool {
 | 
				
			||||||
 | 
							return agents[i].StartTime.Before(agents[j].StartTime)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						sort.Slice(clients, func(i int, j int) bool {
 | 
				
			||||||
 | 
							return clients[i].StartTime.Before(clients[j].StartTime)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return agents, clients
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -140,7 +140,7 @@ func (admin *Admin) AddAgent(publicId models.RendezVousId, agentInfo comms.Envir
 | 
				
			|||||||
	agent := newAgent(commChannel, publicId, agentInfo)
 | 
						agent := newAgent(commChannel, publicId, agentInfo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin.state = admin.state.Copy()
 | 
						admin.state = admin.state.Copy()
 | 
				
			||||||
	admin.state.Agents.Put(agent.Info.Guid, agent.Info)
 | 
						admin.state.Agents[agent.Info.Guid] = agent.Info
 | 
				
			||||||
	admin.agents[publicId] = agent
 | 
						admin.agents[publicId] = agent
 | 
				
			||||||
	return agent, nil
 | 
						return agent, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -175,7 +175,7 @@ func (admin *Admin) AddClient(publicId models.RendezVousId, clientConn iowrapper
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin.state = admin.state.Copy()
 | 
						admin.state = admin.state.Copy()
 | 
				
			||||||
	admin.state.Clients.Put(client.Info.Guid, client.Info)
 | 
						admin.state.Clients[client.Info.Guid] = client.Info
 | 
				
			||||||
	admin.clients[client.Info.ClientId] = client
 | 
						admin.clients[client.Info.ClientId] = client
 | 
				
			||||||
	return client, nil
 | 
						return client, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -206,7 +206,7 @@ func (admin *Admin) RemoveAgent(publicId models.RendezVousId) error {
 | 
				
			|||||||
		log.Printf("Could not close yamux client session for '%s'\n", publicId)
 | 
							log.Printf("Could not close yamux client session for '%s'\n", publicId)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	admin.state = admin.state.Copy()
 | 
						admin.state = admin.state.Copy()
 | 
				
			||||||
	admin.state.Agents.Delete(agent.Info.Guid)
 | 
						delete(admin.state.Agents, agent.Info.Guid)
 | 
				
			||||||
	delete(admin.agents, publicId)
 | 
						delete(admin.agents, publicId)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -222,7 +222,7 @@ func (admin *Admin) RemoveClient(client *clientConnection) error {
 | 
				
			|||||||
	_ = client.clientConnection.Close()
 | 
						_ = client.clientConnection.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	admin.state = admin.state.Copy()
 | 
						admin.state = admin.state.Copy()
 | 
				
			||||||
	admin.state.Clients.Delete(client.Info.Guid)
 | 
						delete(admin.state.Clients, client.Info.Guid)
 | 
				
			||||||
	delete(admin.clients, client.Info.ClientId)
 | 
						delete(admin.clients, client.Info.ClientId)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -230,7 +230,7 @@ func (admin *Admin) RemoveClient(client *clientConnection) error {
 | 
				
			|||||||
func (admin *Admin) SetSessionType(clientId models.ClientId, sessionType models.SessionType) {
 | 
					func (admin *Admin) SetSessionType(clientId models.ClientId, sessionType models.SessionType) {
 | 
				
			||||||
	admin.mutex.Lock()
 | 
						admin.mutex.Lock()
 | 
				
			||||||
	defer admin.mutex.Unlock()
 | 
						defer admin.mutex.Unlock()
 | 
				
			||||||
	for client := range admin.state.Clients.RangeValues() {
 | 
						for _, client := range admin.state.Clients {
 | 
				
			||||||
		if client.ClientId == clientId {
 | 
							if client.ClientId == clientId {
 | 
				
			||||||
			client.SessionType = sessionType
 | 
								client.SessionType = sessionType
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
 | 
				
			|||||||
@ -134,7 +134,7 @@ func logStatusImpl(admin *models.State, notifier Notifier) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	lines = append(lines, fmt.Sprintf(format, "AGENT", "ACTIVE_SINCE", "EXPIRY_TIME",
 | 
						lines = append(lines, fmt.Sprintf(format, "AGENT", "ACTIVE_SINCE", "EXPIRY_TIME",
 | 
				
			||||||
		"USER", "HOST", "OS"))
 | 
							"USER", "HOST", "OS"))
 | 
				
			||||||
	for agent := range admin.Agents.RangeValues() {
 | 
						for _, agent := range admin.Agents {
 | 
				
			||||||
		lines = append(lines, fmt.Sprintf(format, agent.PublicId,
 | 
							lines = append(lines, fmt.Sprintf(format, agent.PublicId,
 | 
				
			||||||
			agent.StartTime.Format(time.DateTime),
 | 
								agent.StartTime.Format(time.DateTime),
 | 
				
			||||||
			agent.GetExpiryTime().Format(time.DateTime),
 | 
								agent.GetExpiryTime().Format(time.DateTime),
 | 
				
			||||||
@ -145,7 +145,7 @@ func logStatusImpl(admin *models.State, notifier Notifier) {
 | 
				
			|||||||
	lines = append(lines, "")
 | 
						lines = append(lines, "")
 | 
				
			||||||
	format = "%-10s %-20s %-20s %-20s %-20s"
 | 
						format = "%-10s %-20s %-20s %-20s %-20s"
 | 
				
			||||||
	lines = append(lines, fmt.Sprintf(format, "CLIENT", "AGENT", "ACTIVE_SINCE", "REMOTE_ADDRESS", "SESSION_TYPE"))
 | 
						lines = append(lines, fmt.Sprintf(format, "CLIENT", "AGENT", "ACTIVE_SINCE", "REMOTE_ADDRESS", "SESSION_TYPE"))
 | 
				
			||||||
	for client := range admin.Clients.RangeValues() {
 | 
						for _, client := range admin.Clients {
 | 
				
			||||||
		lines = append(lines, fmt.Sprintf(format,
 | 
							lines = append(lines, fmt.Sprintf(format,
 | 
				
			||||||
			client.ClientId,
 | 
								client.ClientId,
 | 
				
			||||||
			client.PublicId,
 | 
								client.PublicId,
 | 
				
			||||||
 | 
				
			|||||||
@ -137,7 +137,8 @@ func (session *WebSession) WriteNotifications(location *time.Location, ctx conte
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (session *WebSession) writeNotificationToClient(location *time.Location, notification *models.State) bool {
 | 
					func (session *WebSession) writeNotificationToClient(location *time.Location, notification *models.State) bool {
 | 
				
			||||||
	err := templates.State(notification, location).Render(context.Background(), session.conn)
 | 
						agents, clients := notification.Slices()
 | 
				
			||||||
 | 
						err := templates.State(agents, clients, 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 false
 | 
							return false
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
templ Sessions(state *models.State, loc *time.Location) {
 | 
					templ Sessions(agents []*models.Agent, clients []*models.Client, loc *time.Location) {
 | 
				
			||||||
  <div>
 | 
					  <div>
 | 
				
			||||||
      <div ws-send
 | 
					      <div ws-send
 | 
				
			||||||
           hx-trigger="load"
 | 
					           hx-trigger="load"
 | 
				
			||||||
@ -16,8 +16,8 @@ templ Sessions(state *models.State, loc *time.Location) {
 | 
				
			|||||||
      <h1>sessions</h1>
 | 
					      <h1>sessions</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      <div id="status">
 | 
					      <div id="status">
 | 
				
			||||||
        if state != nil {
 | 
					        if len(agents) > 0 {
 | 
				
			||||||
           @State(state, loc)
 | 
					           @State(agents, clients, loc)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
           Loading...
 | 
					           Loading...
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -26,12 +26,12 @@ templ Sessions(state *models.State, loc *time.Location) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
templ State(state *models.State, location *time.Location) {
 | 
					templ State(agents []*models.Agent, clients []*models.Client, location *time.Location) {
 | 
				
			||||||
   <div id="status">
 | 
					   <div id="status">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   <h3>agents</h3>
 | 
					   <h3>agents</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if state.Agents.Len() == 0 {
 | 
					   if len(agents) == 0 {
 | 
				
			||||||
     <p>-</p>
 | 
					     <p>-</p>
 | 
				
			||||||
   } else {
 | 
					   } else {
 | 
				
			||||||
        <table class="table">
 | 
					        <table class="table">
 | 
				
			||||||
@ -46,7 +46,7 @@ templ State(state *models.State, location *time.Location) {
 | 
				
			|||||||
               <th>shell</th>
 | 
					               <th>shell</th>
 | 
				
			||||||
           </tr>
 | 
					           </tr>
 | 
				
			||||||
           </thead>
 | 
					           </thead>
 | 
				
			||||||
   for agent := range state.Agents.RangeValues() {
 | 
					   for _, agent := range agents {
 | 
				
			||||||
           <tr>
 | 
					           <tr>
 | 
				
			||||||
               <td>{string(agent.PublicId)}</td>
 | 
					               <td>{string(agent.PublicId)}</td>
 | 
				
			||||||
               <td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
 | 
					               <td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
 | 
				
			||||||
@ -64,7 +64,7 @@ templ State(state *models.State, location *time.Location) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
   <h3>clients</h3>
 | 
					   <h3>clients</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   if state.Clients.Len() == 0 {
 | 
					   if len(clients) == 0 {
 | 
				
			||||||
        <p>-</p>
 | 
					        <p>-</p>
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
          <table class="table">
 | 
					          <table class="table">
 | 
				
			||||||
@ -80,7 +80,7 @@ templ State(state *models.State, location *time.Location) {
 | 
				
			|||||||
                  <th>shell</th>
 | 
					                  <th>shell</th>
 | 
				
			||||||
              </tr>
 | 
					              </tr>
 | 
				
			||||||
              </thead>
 | 
					              </thead>
 | 
				
			||||||
   for client := range state.Clients.RangeValues() {
 | 
					   for _, client := range clients {
 | 
				
			||||||
              <tr>
 | 
					              <tr>
 | 
				
			||||||
                  <td>{string(client.ClientId)}</td>
 | 
					                  <td>{string(client.ClientId)}</td>
 | 
				
			||||||
                  <td>{client.StartTime.In(location).Format(time.DateTime)}</td>
 | 
					                  <td>{client.StartTime.In(location).Format(time.DateTime)}</td>
 | 
				
			||||||
@ -98,8 +98,8 @@ templ State(state *models.State, location *time.Location) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
templ SessionsTab(state *models.State, loc *time.Location) {
 | 
					templ SessionsTab(agents []*models.Agent, clients []*models.Client, loc *time.Location) {
 | 
				
			||||||
    @BasePage(4) {
 | 
					    @BasePage(4) {
 | 
				
			||||||
        @Sessions(state, loc)
 | 
					        @Sessions(agents, clients, loc)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user