added http server

This commit is contained in:
Gregory Salaun 2024-09-26 12:24:56 +07:00
parent ab17255bdc
commit eef2b4b938
19 changed files with 131415 additions and 782 deletions

Binary file not shown.

70
HTTPServer.go Normal file
View File

@ -0,0 +1,70 @@
package main
import (
"html/template"
"net/http"
"github.com/gorilla/mux"
log "github.com/sirupsen/logrus"
)
var tmpl *template.Template
type HTTPServer struct {
router *mux.Router
Log4OMRepo Log4OMContactsRepository
Repo FlexDXClusterRepository
Log *log.Logger
}
func NewHTTPServer(cRepo Log4OMContactsRepository, fRepo FlexDXClusterRepository, log *log.Logger) *HTTPServer {
gRouter := mux.NewRouter()
return &HTTPServer{
router: gRouter,
Log4OMRepo: cRepo,
Repo: fRepo,
Log: log,
}
}
func (s *HTTPServer) SetRoutes() {
s.router.HandleFunc("/", s.Homepage)
s.router.HandleFunc("/spots", s.GetSpots).Methods("GET")
s.router.HandleFunc("/spotscount", s.GetSpotsCount).Methods("GET")
s.router.HandleFunc("/spotters", s.GetSpotters).Methods("GET")
}
func (s *HTTPServer) StartHTTPServer() {
tmpl, _ = template.ParseGlob("templates/*.html")
s.SetRoutes()
s.Log.Infof("starting HTTP server on %s:%s", Cfg.HTTPServer.Host, Cfg.HTTPServer.Port)
err := http.ListenAndServe(Cfg.HTTPServer.Host+":"+Cfg.HTTPServer.Port, s.router)
if err != nil {
s.Log.Warn("cannot start HTTP server: ", err)
}
}
func (s *HTTPServer) Homepage(w http.ResponseWriter, r *http.Request) {
err := tmpl.ExecuteTemplate(w, "home.html", nil)
if err != nil {
s.Log.Error("error executing home template: ", err)
}
}
func (s *HTTPServer) GetSpots(w http.ResponseWriter, r *http.Request) {
spots := s.Repo.GetAllSpots("25")
tmpl.ExecuteTemplate(w, "spot", spots)
}
func (s *HTTPServer) GetSpotsCount(w http.ResponseWriter, r *http.Request) {
spots := s.Repo.GetAllSpots("0")
count := len(spots)
tmpl.ExecuteTemplate(w, "spotCount", count)
}
func (s *HTTPServer) GetSpotters(w http.ResponseWriter, r *http.Request) {
spotters := s.Repo.GetSpotters()
tmpl.ExecuteTemplate(w, "spotters", spotters)
}

View File

@ -12,6 +12,7 @@ import (
)
var spotRe *regexp.Regexp = regexp.MustCompile(`DX\sde\s([\w\d]+).*:\s+(\d+.\d)\s+([\w\d]+)\s+(CW|SSB|FT8|FT4|RTTY|USB|LSB)?\s+(.*)\s\s\s+([\d]+\w{1})`)
var count int = 0
type TCPClient struct {
Login string
@ -67,7 +68,7 @@ func (c *TCPClient) StartClient() {
if err != nil {
c.Log.Error("cannot connect to Telnet Client:", err)
}
c.Log.Infof("connected to %s:%s", c.Address, c.Port)
c.Log.Infof("connected to DX cluster %s:%s", c.Address, c.Port)
err = c.Conn.SetKeepAlive(true)
if err != nil {
@ -77,6 +78,14 @@ func (c *TCPClient) StartClient() {
c.Reader = bufio.NewReader(c.Conn)
c.Writer = bufio.NewWriter(c.Conn)
go func() {
for message := range c.TCPServer.CmdChan {
message := message + "\n"
c.Log.Infof("Received DX Command: %s", message)
c.WriteString(message)
}
}()
go c.ReadLine()
}
@ -107,6 +116,7 @@ func (c *TCPClient) SetFilters() {
}
func (c *TCPClient) ReadLine() {
for {
message, err := c.Reader.ReadString('\n')
if err != nil {
@ -125,6 +135,11 @@ func (c *TCPClient) ReadLine() {
// Send the spot message to TCP server
if len(c.TCPServer.Clients) > 0 {
if count == 0 {
// wait 3 seconds before sending messages to allow the client to connect
time.Sleep(time.Second * 3)
count++
}
c.MsgChan <- message
}
}
@ -139,3 +154,12 @@ func (tc *TCPClient) Write(data []byte) (n int, err error) {
return
}
func (tc *TCPClient) WriteString(data string) (n int, err error) {
n, err = tc.Writer.Write([]byte(data))
if err == nil {
err = tc.Writer.Flush()
}
return
}

View File

@ -92,20 +92,19 @@ func (s *TCPServer) handleConnection() {
message = strings.TrimSpace(message)
s.Log.Infof("Message reçu du client: %s\n", message)
switch message {
case "bye":
// if message is by then disconnect
if message == "bye" {
s.Mutex.Lock()
delete(s.Clients, s.Conn)
s.Mutex.Unlock()
s.Log.Infof("client %s disconnected", s.Conn.RemoteAddr().String())
return
case "SH/DX 30":
return
default:
s.Write("cannot identify command\n")
}
if strings.Contains(message, "DX") && message != "SH/DX 30" {
// send DX spot to the client
s.CmdChan <- message
}
}
}

View File

@ -38,6 +38,11 @@ type Config struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
} `yaml:"telnet"`
HTTPServer struct {
Host string `yaml:"host"`
Port string `yaml:"port"`
} `yaml:"httpserver"`
}
func NewConfig(configPath string) error {

View File

@ -6,9 +6,9 @@ sqlite:
cluster:
server: dxc.k0xm.net
port: 7300
login: xv9q-5
login: xv9q-2
skimmer: true
ft8: false
ft8: true
flex:
ip: 10.10.10.120
spot_life: 600
@ -16,4 +16,7 @@ clublog:
api: 5767f19333363a9ef432ee9cd4141fe76b8adf38
telnet:
host: 0.0.0.0
port: 7301
port: 7301
httpserver:
host: 0.0.0.0
port: 3000

View File

@ -20,7 +20,12 @@ type Contact struct {
Country string
}
type ContactsRepository struct {
type Spotter struct {
Spotter string
NumberofSpots string
}
type Log4OMContactsRepository struct {
db *sql.DB
Log *log.Logger
}
@ -30,12 +35,12 @@ type FlexDXClusterRepository struct {
Log *log.Logger
}
func NewContactsRepository(filePath string, log *log.Logger) *ContactsRepository {
func NewLog4OMContactsRepository(filePath string, log *log.Logger) *Log4OMContactsRepository {
db, err := sql.Open("sqlite3", filePath)
if err != nil {
fmt.Println("Cannot open db", err)
}
return &ContactsRepository{
return &Log4OMContactsRepository{
db: db,
Log: log}
}
@ -84,13 +89,15 @@ func NewFlexDXDatabase(filePath string, log *log.Logger) *FlexDXClusterRepositor
}
}
func (r *ContactsRepository) ListByCountry(countryID string) ([]*Contact, error) {
func (r *Log4OMContactsRepository) ListByCountry(countryID string) ([]*Contact, error) {
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ?", countryID)
if err != nil {
log.Error("could not query database", err)
return nil, err
}
defer rows.Close()
contacts := []*Contact{}
for rows.Next() {
c := Contact{}
@ -103,7 +110,7 @@ func (r *ContactsRepository) ListByCountry(countryID string) ([]*Contact, error)
return contacts, nil
}
func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([]*Contact, error) {
func (r *Log4OMContactsRepository) ListByCountryMode(countryID string, mode string) ([]*Contact, error) {
modeUSB := "USB"
modeLSB := "LSB"
@ -116,6 +123,8 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
return nil, err
}
defer rows.Close()
contacts := []*Contact{}
for rows.Next() {
c := Contact{}
@ -135,6 +144,8 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
return nil, err
}
defer rows.Close()
contacts := []*Contact{}
for rows.Next() {
c := Contact{}
@ -149,13 +160,15 @@ func (r *ContactsRepository) ListByCountryMode(countryID string, mode string) ([
}
}
func (r *ContactsRepository) ListByCountryBand(countryID string, band string) ([]*Contact, error) {
func (r *Log4OMContactsRepository) ListByCountryBand(countryID string, band string) ([]*Contact, error) {
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE dxcc = ? AND band = ?", countryID, band)
if err != nil {
fmt.Println(err)
return nil, err
}
defer rows.Close()
contacts := []*Contact{}
for rows.Next() {
c := Contact{}
@ -168,13 +181,15 @@ func (r *ContactsRepository) ListByCountryBand(countryID string, band string) ([
return contacts, nil
}
func (r *ContactsRepository) ListByCallSign(callSign string, band string, mode string) ([]*Contact, error) {
func (r *Log4OMContactsRepository) ListByCallSign(callSign string, band string, mode string) ([]*Contact, error) {
rows, err := r.db.Query("SELECT callsign, band, mode, dxcc, stationcallsign, country FROM log WHERE callsign = ? AND band = ? AND mode = ?", callSign, band, mode)
if err != nil {
fmt.Println(err)
return nil, err
}
defer rows.Close()
contacts := []*Contact{}
for rows.Next() {
c := Contact{}
@ -187,13 +202,76 @@ func (r *ContactsRepository) ListByCallSign(callSign string, band string, mode s
return contacts, nil
}
func (r *FlexDXClusterRepository) GetAllSpots(limit string) []FlexSpot {
Spots := []FlexSpot{}
var query string
if limit == "0" {
query = "SELECT * from spots ORDER BY id DESC"
} else {
query = fmt.Sprintf("SELECT * from spots ORDER BY id DESC LIMIT %s", limit)
}
rows, err := r.db.Query(query)
if err != nil {
r.Log.Error(err)
return nil
}
defer rows.Close()
s := FlexSpot{}
for rows.Next() {
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
&s.Comment, &s.Color, &s.BackgroundColor); err != nil {
fmt.Println(err)
return nil
}
Spots = append(Spots, s)
}
return Spots
}
func (r *FlexDXClusterRepository) GetSpotters() []Spotter {
sList := []Spotter{}
rows, err := r.db.Query("select spotter, count(*) as occurences from spots group by spotter order by occurences desc, spotter limit 3")
if err != nil {
r.Log.Error(err)
return nil
}
defer rows.Close()
s := Spotter{}
for rows.Next() {
if err := rows.Scan(&s.Spotter, &s.NumberofSpots); err != nil {
fmt.Println(err)
return nil
}
sList = append(sList, s)
}
return sList
}
func (r *FlexDXClusterRepository) FindDXSameBand(spot FlexSpot) (*FlexSpot, error) {
rows, err := r.db.Query("SELECT * from spots WHERE dx = ? AND band = ?", spot.DX, spot.Band)
if err != nil {
fmt.Println(err)
r.Log.Error(err)
return nil, err
}
defer rows.Close()
s := FlexSpot{}
for rows.Next() {
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -219,8 +297,14 @@ func (r *FlexDXClusterRepository) CreateSpot(spot FlexSpot) {
}
func (r *FlexDXClusterRepository) UpdateSpotSameBand(spot FlexSpot) {
return
func (r *FlexDXClusterRepository) UpdateSpotSameBand(spot FlexSpot) error {
_, err := r.db.Exec(`UPDATE spots SET commandNumber = ?, DX = ?, freqMhz = ?, freqHz = ?, band = ?, mode = ?, spotter = ?, flexMode = ?, source = ?, time = ?, timestamp = ?, lifeTime = ?, priority = ?, comment = ?, color = ?, backgroundColor = ? WHERE DX = ? AND band = ?`,
spot.CommandNumber, spot.DX, spot.FrequencyMhz, spot.FrequencyHz, spot.Band, spot.Mode, spot.SpotterCallsign, spot.FlexMode, spot.Source, spot.UTCTime, spot.TimeStamp, spot.LifeTime, spot.Priority, spot.Comment, spot.Color, spot.BackgroundColor, spot.DX, spot.Band)
if err != nil {
r.Log.Errorf("could not update database: %s", err)
return err
}
return nil
}
func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string) (*FlexSpot, error) {
@ -230,6 +314,8 @@ func (r *FlexDXClusterRepository) FindSpotByCommandNumber(commandNumber string)
return nil, err
}
defer rows.Close()
s := FlexSpot{}
for rows.Next() {
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -248,6 +334,8 @@ func (r *FlexDXClusterRepository) FindSpotByFlexSpotNumber(spotNumber string) (*
return nil, err
}
defer rows.Close()
s := FlexSpot{}
for rows.Next() {
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,
@ -266,6 +354,8 @@ func (r *FlexDXClusterRepository) UpdateFlexSpotNumberByID(flexSpotNumber string
r.Log.Errorf("could not update database: %s", err)
}
defer rows.Close()
s := FlexSpot{}
for rows.Next() {
if err := rows.Scan(&s.ID, &s.CommandNumber, &s.FlexSpotNumber, &s.DX, &s.FrequencyMhz, &s.FrequencyHz, &s.Band, &s.Mode, &s.SpotterCallsign, &s.FlexMode, &s.Source, &s.UTCTime, &s.TimeStamp, &s.LifeTime, &s.Priority,

Binary file not shown.

View File

@ -78,7 +78,7 @@ func (fc *FlexClient) StartFlexClient() {
fc.Log.Error("could not connect to flex radio, exiting...", err)
os.Exit(1)
}
fc.Log.Infof("connected to %s:%s", fc.Address, fc.Port)
fc.Log.Infof("connected to flex radio at %s:%s", fc.Address, fc.Port)
go func() {
for message := range fc.SpotChan {
@ -103,6 +103,8 @@ func (fc *FlexClient) StartFlexClient() {
clrSpotAllCmd := fmt.Sprintf("C%v|spot clear", CommandNumber)
fc.Write(clrSpotAllCmd)
CommandNumber++
fc.Log.Info("Subscribed to spot on FlexRadio and Deleted all spots from panadapter")
}
func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
@ -151,8 +153,14 @@ func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
flexSpot.Priority = "5"
}
if spot.DX == Cfg.SQLite.Callsign {
flexSpot.Color = "#ff0000"
flexSpot.Priority = "2"
flexSpot.BackgroundColor = "#000000"
}
flexSpot.Comment = strings.ReplaceAll(flexSpot.Comment, " ", "\u00A0")
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "]"
flexSpot.Comment = flexSpot.Comment + "\u00A0" + "[" + flexSpot.Mode + "] [" + flexSpot.SpotterCallsign + "]"
srcFlexSpot, err := fc.Repo.FindDXSameBand(flexSpot)
if err != nil {
@ -163,29 +171,29 @@ func (fc *FlexClient) SendSpottoFlex(spot TelnetSpot) {
fc.Repo.CreateSpot(flexSpot)
stringSpot := fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s", flexSpot.CommandNumber, flexSpot.FrequencyMhz,
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
CommandNumber++
fc.SendSpot(stringSpot)
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band == flexSpot.Band && srcFlexSpot.FrequencyMhz != flexSpot.FrequencyMhz {
fc.Repo.UpdateSpotSameBand(flexSpot)
stringSpot := fmt.Sprintf("C%v|spot set %v rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s", flexSpot.CommandNumber, srcFlexSpot.CommandNumber, flexSpot.FrequencyMhz,
stringSpot := fmt.Sprintf("C%v|spot set %v rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s", flexSpot.CommandNumber, srcFlexSpot.FlexSpotNumber, flexSpot.FrequencyMhz,
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
CommandNumber++
stringDelete := fmt.Sprintf("C%v|spot remove %v", CommandNumber, srcFlexSpot.FlexSpotNumber)
fc.DeleteAndSendSpot(stringSpot, stringDelete)
fc.SendSpot(stringSpot)
} else if srcFlexSpot.DX != "" && srcFlexSpot.Band != flexSpot.Band {
fc.Repo.CreateSpot(flexSpot)
stringSpot := fmt.Sprintf("C%v|spot add rx_freq=%v callsign=%s mode=%s source=%s spotter_callsign=%s timestamp=%v lifetime_seconds=%s comment=%s color=%s background_color=%s priority=%s", flexSpot.CommandNumber, flexSpot.FrequencyMhz,
flexSpot.DX, flexSpot.Mode, flexSpot.Source, flexSpot.SpotterCallsign, flexSpot.TimeStamp, flexSpot.LifeTime, flexSpot.Comment, flexSpot.Color, flexSpot.BackgroundColor, flexSpot.Priority)
CommandNumber++
fc.SendSpot(stringSpot)
}
CommandNumber++
}
func (fc *FlexClient) SendSpot(stringSpot string) {
fc.Write(stringSpot)
}
func (fc *FlexClient) DeleteAndSendSpot(stringSpot string, deleteSpot string) {
fc.Write(deleteSpot)
fc.Write(stringSpot)
}
func (fc *FlexClient) ReadLine() {
for {
@ -195,10 +203,8 @@ func (fc *FlexClient) ReadLine() {
os.Exit(1)
}
// msgRaw := strings.TrimSpace(message)
// fc.Log.Info(msgRaw)
// fc.Log.Info(message)
// Response when spot is added
regRespSpot := *regexp.MustCompile(`R(\d+)\|0\|(\d+)\n`)
respSpot := regRespSpot.FindStringSubmatch(message)
@ -232,8 +238,9 @@ func (fc *FlexClient) ReadLine() {
respDelete := regSpotDeleted.FindStringSubmatch(message)
if len(respDelete) > 0 {
spot, _ := fc.Repo.FindSpotByFlexSpotNumber(respDelete[1])
fc.Repo.DeleteSpotByFlexSpotNumber(respDelete[1])
fc.Log.Infof("deleted spot %v from database", respDelete[1])
fc.Log.Infof("Spot: DX: %s - Spotter: %s - Freq: %s - Band: %s - FlexID: %v deleted from database", spot.DX, spot.SpotterCallsign, spot.FrequencyHz, spot.Band, respDelete[1])
}
}
}

131789
flexradio.log

File diff suppressed because it is too large Load Diff

1
go.mod
View File

@ -9,6 +9,7 @@ require (
)
require (
github.com/gorilla/mux v1.8.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect

2
go.sum
View File

@ -1,6 +1,8 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=

View File

@ -15,6 +15,7 @@ func NewLog() *log.Logger {
}
w := io.MultiWriter(os.Stdout, f)
// w := io.Writer(os.Stdout)
l := &log.Logger{
Out: w,

35
main.go
View File

@ -3,6 +3,9 @@ package main
import (
"flag"
"log"
"os"
"os/signal"
"syscall"
"git.rouggy.com/rouggy/FlexDXCluster/logger"
)
@ -48,16 +51,42 @@ func main() {
DeleteDatabase("./flex.sqlite", log)
repo := NewFlexDXDatabase("flex.sqlite", log)
fRepo := NewFlexDXDatabase("flex.sqlite", log)
defer fRepo.db.Close()
cRepo := NewLog4OMContactsRepository(Cfg.SQLite.SQLitePath, log)
defer cRepo.db.Close()
TCPServer := NewTCPServer(Cfg.Telnet.Host, Cfg.Telnet.Port, log)
FlexClient := NewFlexClient(*repo, *TCPServer, log)
FlexClient := NewFlexClient(*fRepo, *TCPServer, log)
TCPClient := NewTCPClient(*Cfg, TCPServer, FlexClient, log)
HTTPServer := NewHTTPServer(*cRepo, *fRepo, log)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
go FlexClient.StartFlexClient()
go TCPClient.StartClient()
go TCPServer.StartServer()
select {}
go HTTPServer.StartHTTPServer()
for sig := range sigCh {
log.Infof("received signal: %v, shutting down TCP Client.", sig)
TCPClient.Close()
if err := fRepo.db.Close(); err != nil {
log.Error("failed to close the database connection properly")
os.Exit(1)
}
if err := cRepo.db.Close(); err != nil {
log.Error("failed to close Log4OM database connection properly")
os.Exit(1)
}
os.Exit(0)
}
}

View File

@ -25,7 +25,6 @@ type TelnetSpot struct {
NewBand bool
NewMode bool
CallsignWorked bool
SpotChan chan TelnetSpot
}
func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSpot, log *log.Logger) {
@ -49,7 +48,7 @@ func ProcessTelnetSpot(re *regexp.Regexp, spotRaw string, SpotChan chan TelnetSp
spot.NewMode = false
spot.NewDXCC = false
contactRepo := NewContactsRepository(Cfg.SQLite.SQLitePath, log)
contactRepo := NewLog4OMContactsRepository(Cfg.SQLite.SQLitePath, log)
defer contactRepo.db.Close()

43
templates/home.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="theme-color" content="#18181B">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://unpkg.com/htmx.org@1.9.5" integrity="sha384-xcuj3WpfgjlKF+FXhSQFQ0ZNr39ln+hwjN3npfM9VBnUskLolQAcN80McRIVOPuO" crossorigin="anonymous"></script>
<title>FlexDXCluster DashBoard</title>
<style>
.card {
display:inline-block;
margin-right:25px!important;
}
.card-deck {
display: flex;
flex-wrap: wrap;
align-items: stretch;
}
</style>
</head>
<body>
<div class="container" style="width:1000px; margin: 0 auto;">
<div class="card-deck">
<div class="card shadow p-3 mb-5 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Spots</h5>
<h6 class="card-subtitle mb-2 text-muted">Current number of spots</h6>
<div class="card-text fs-1 fw-bold text-center" id="spotCount" hx-get="/spotscount" hx-trigger="every 1s" hx-swap="innerHTML"></div>
</div>
</div>
<div class="card shadow p-3 mb-5 bg-body rounded" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">Top Spotters</h5>
<div id="spotters" hx-get="/spotters" hx-trigger="every 1s" hx-swap="innerHTML"><p class="card-text"></p></div>
</div>
</div>
</div>
</div>
<div id="spot" style="width:1000px; margin:0 auto;" hx-get="/spots" hx-trigger="every 1s" hx-swap="innerHTML"></div>
</div>
</body>
</html>

31
templates/spot.html Normal file
View File

@ -0,0 +1,31 @@
{{define "spot"}}
<table class="table table-sm table-bordered shadow p-3 mb-5 bg-body rounded">
<thead class="table-light">
<tr>
<th scope="col">DX</th>
<th scope="col">Spotter</th>
<th scope="col">Freq</th>
<th scope="col">Band</th>
<th scope="col">Mode</th>
<th scope="col">UTC Time</th>
<th scope="col">Comment</th>
</tr>
</thead>
<tbody>
{{ range .}}
<tr>
<td class="align-middle">{{ .DX }}</td>
<td class="align-middle">{{ .SpotterCallsign }}</td>
<td class="align-middle">{{ .FrequencyMhz }}</td>
<td class="align-middle">{{ .Band }}</td>
<td class="align-middle">{{ .Mode }}</td>
<td class="align-middle">{{ .UTCTime }}</td>
<td class="align-middle">{{ .Comment }}</td>
</tr>
{{ end }}
</tbody>
</table>
{{end}}

5
templates/spotCount.html Normal file
View File

@ -0,0 +1,5 @@
{{ define "spotCount" }}
{{ . }}
{{ end }}

9
templates/spotters.html Normal file
View File

@ -0,0 +1,9 @@
{{ define "spotters" }}
{{ range.}}
<span class="fw-bold">{{ .Spotter }}</span>: <span>{{ .NumberofSpots }} spots</span><br>
{{ end }}
{{ end }}