GroupVerify: Verification page, overrides and public Verified tags
Shown only where GroupVerify is: staff get the Verification page once its gv_* tables are in the database, and the public site shows "Verified" only while it's on (enabled, with a group) and not missing from css_plugins list, which the poller reads once a minute over RCON. - Verification page: plugin state and version, Steam group, saved member list, overrides. - Overrides (@zio/verify, not on yourself unless Root): give, change, remove; player drawer shows why a player is or isn't verified. - Root sets the group and turns it on/off: saved to gv_settings, and sent as css_gv_* when the plugin is loaded so it applies at once. - gv_* times use GroupVerify's own timezone setting. - MySQL integration test (SAW_TEST_DSN), unit tests for plugin list parsing and the show rule.
This commit is contained in:
parent
b2b4698776
commit
9e4bc63201
15 changed files with 1270 additions and 13 deletions
|
|
@ -7,6 +7,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -114,12 +115,46 @@ func Parse(out string) (Status, error) {
|
|||
return st, nil
|
||||
}
|
||||
|
||||
// pluginLine is one plugin in `css_plugins list`: [#3:LOADED]: "GroupVerify" (0.2.0) by astra
|
||||
var pluginLine = regexp.MustCompile(`\[#\d+:([A-Z]+)\]: "([^"]*)" \(([^)]*)\)`)
|
||||
|
||||
// ParsePlugins reads `css_plugins list` output into loaded plugins' versions by ModuleName. ok is
|
||||
// false when the output isn't a plugin list at all.
|
||||
func ParsePlugins(out string) (loaded map[string]string, ok bool) {
|
||||
if !strings.Contains(out, "List of all plugins") {
|
||||
return nil, false
|
||||
}
|
||||
loaded = map[string]string{}
|
||||
for _, m := range pluginLine.FindAllStringSubmatch(out, -1) {
|
||||
if m[1] == "LOADED" {
|
||||
loaded[m[2]] = m[3]
|
||||
}
|
||||
}
|
||||
return loaded, true
|
||||
}
|
||||
|
||||
// Whether a CSS plugin is loaded on the server, as far as the last check knows.
|
||||
type PluginState string
|
||||
|
||||
const (
|
||||
PluginLoaded PluginState = "loaded"
|
||||
PluginMissing PluginState = "missing"
|
||||
PluginUnknown PluginState = "unknown" // server down, or not checked yet
|
||||
)
|
||||
|
||||
// pluginInterval is how often the plugin list is reread; plugins rarely change.
|
||||
const pluginInterval = time.Minute
|
||||
|
||||
type Poller struct {
|
||||
rc *rcon.Client
|
||||
interval time.Duration
|
||||
|
||||
mu sync.RWMutex
|
||||
current Status
|
||||
|
||||
// From css_plugins list, nil while unknown.
|
||||
plugins map[string]string
|
||||
pluginsAt time.Time
|
||||
}
|
||||
|
||||
func NewPoller(rc *rcon.Client, interval time.Duration) *Poller {
|
||||
|
|
@ -169,10 +204,44 @@ func (p *Poller) Refresh() Status {
|
|||
st.Error = p.current.Error
|
||||
}
|
||||
p.current = st
|
||||
if !st.Online {
|
||||
p.plugins = nil
|
||||
}
|
||||
checkPlugins := st.Online && time.Since(p.pluginsAt) >= pluginInterval
|
||||
if checkPlugins {
|
||||
p.pluginsAt = time.Now() // claimed, so concurrent refreshes don't all ask
|
||||
}
|
||||
p.mu.Unlock()
|
||||
if checkPlugins {
|
||||
p.refreshPlugins()
|
||||
}
|
||||
return st
|
||||
}
|
||||
|
||||
func (p *Poller) refreshPlugins() {
|
||||
out, err := p.rc.Exec("css_plugins list")
|
||||
loaded, ok := ParsePlugins(out)
|
||||
if err != nil || !ok {
|
||||
loaded = nil
|
||||
}
|
||||
p.mu.Lock()
|
||||
p.plugins = loaded
|
||||
p.mu.Unlock()
|
||||
}
|
||||
|
||||
// Plugin reports whether a CSS plugin (by ModuleName) is loaded, and its version if it is.
|
||||
func (p *Poller) Plugin(name string) (PluginState, string) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
if p.plugins == nil {
|
||||
return PluginUnknown, ""
|
||||
}
|
||||
if v, ok := p.plugins[name]; ok {
|
||||
return PluginLoaded, v
|
||||
}
|
||||
return PluginMissing, ""
|
||||
}
|
||||
|
||||
func (p *Poller) Current() Status {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue