Initial commit
This commit is contained in:
28
.gitignore
vendored
Normal file
28
.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=go
|
||||
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||
nn
|
||||
36
Makefile
Normal file
36
Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
# run templ generation in watch mode to detect all .templ files and
|
||||
# re-create _templ.txt files on change, then send reload event to browser.
|
||||
# Default url: http://localhost:7331
|
||||
live/templ:
|
||||
templ generate --watch --proxy="http://localhost:8080" --open-browser=false -v
|
||||
|
||||
# run air to detect any go file changes to re-build and re-run the server.
|
||||
live/server:
|
||||
go run github.com/cosmtrek/air@v1.51.0 \
|
||||
--build.cmd "go build -o tmp/bin/main" --build.bin "tmp/bin/main" --build.delay "100" \
|
||||
--build.exclude_dir "node_modules" \
|
||||
--build.include_ext "go" \
|
||||
--build.stop_on_error "false" \
|
||||
--misc.clean_on_exit true
|
||||
|
||||
# run tailwindcss to generate the styles.css bundle in watch mode.
|
||||
live/tailwind:
|
||||
npx tailwindcss -i ./style/input.css -o ./assets/styles.css --minify --watch
|
||||
|
||||
# run esbuild to generate the index.js bundle in watch mode.
|
||||
live/esbuild:
|
||||
npx esbuild script/index.ts --bundle --outdir=assets/ --watch=forever
|
||||
|
||||
# watch for any js or css change in the assets/ folder, then reload the browser via templ proxy.
|
||||
live/sync_assets:
|
||||
go run github.com/cosmtrek/air@v1.51.0 \
|
||||
--build.cmd "templ generate --notify-proxy" \
|
||||
--build.bin "true" \
|
||||
--build.delay "100" \
|
||||
--build.exclude_dir "" \
|
||||
--build.include_dir "assets" \
|
||||
--build.include_ext "js,css"
|
||||
|
||||
# start all 5 watch processes in parallel.
|
||||
live:
|
||||
make -j5 live/templ live/server live/tailwind live/esbuild live/sync_assets
|
||||
BIN
assets/favicon.ico
Normal file
BIN
assets/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
3093
assets/index.js
Normal file
3093
assets/index.js
Normal file
File diff suppressed because it is too large
Load Diff
16
assets/logo.svg
Normal file
16
assets/logo.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 545 KiB |
1
assets/styles.css
Normal file
1
assets/styles.css
Normal file
File diff suppressed because one or more lines are too long
14
go.mod
Normal file
14
go.mod
Normal file
@ -0,0 +1,14 @@
|
||||
module gitea.henriburau.de/haw-lan/cod4watcher
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require github.com/a-h/templ v0.2.707
|
||||
|
||||
require github.com/mergestat/timediff v0.0.3 // indirect
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi v1.5.5
|
||||
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||
github.com/gorcon/rcon v1.3.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
)
|
||||
12
go.sum
Normal file
12
go.sum
Normal file
@ -0,0 +1,12 @@
|
||||
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
||||
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
||||
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
||||
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/gorcon/rcon v1.3.5 h1:YE/Vrw6R99uEP08wp0EjdPAP3Jwz/ys3J8qxI1nYoeU=
|
||||
github.com/gorcon/rcon v1.3.5/go.mod h1:zR1qfKZttF8vAgH1NsP6CdpachOvLDq8jE64NboTpIM=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mergestat/timediff v0.0.3 h1:ucCNh4/ZrTPjFZ081PccNbhx9spymCJkFxSzgVuPU+Y=
|
||||
github.com/mergestat/timediff v0.0.3/go.mod h1:yvMUaRu2oetc+9IbPLYBJviz6sA7xz8OXMDfhBl7YSI=
|
||||
51
main.go
Normal file
51
main.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/routes"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cod4server, err := models.NewCOD4ServerStatus("80.57.28.137", "28960", 1*time.Second)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
status, err := cod4server.GetServerStatus()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
log.Printf("%+v", status)
|
||||
|
||||
router := chi.NewMux()
|
||||
|
||||
server := &routes.Server{}
|
||||
|
||||
router.Handle("/*", public())
|
||||
router.Get("/health", routes.Make(server.HandleHealth))
|
||||
router.Get("/captures/{captureID}", routes.Make(server.HandleCapture))
|
||||
router.Get("/", routes.Make(server.HandleHome))
|
||||
|
||||
listenAddr := os.Getenv("LISTEN_ADDR")
|
||||
slog.Info("HTTP server started", "listenAddr", listenAddr)
|
||||
http.ListenAndServe(listenAddr, router)
|
||||
}
|
||||
|
||||
func public() http.Handler {
|
||||
fmt.Println("building static files for development")
|
||||
return http.StripPrefix("/assets/", http.FileServerFS(os.DirFS("assets")))
|
||||
}
|
||||
89
models/capture.go
Normal file
89
models/capture.go
Normal file
@ -0,0 +1,89 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/mergestat/timediff"
|
||||
)
|
||||
|
||||
type MapScoreList []MapScore
|
||||
|
||||
type Capture struct {
|
||||
Id uint
|
||||
Host string
|
||||
Port string
|
||||
Name string
|
||||
Active bool
|
||||
Start time.Time
|
||||
MapScores MapScoreList
|
||||
}
|
||||
|
||||
type MapScore struct {
|
||||
Id uint
|
||||
StartTime time.Time
|
||||
Map string
|
||||
ScoreList []Score
|
||||
}
|
||||
|
||||
type Score struct {
|
||||
Id uint
|
||||
Name string
|
||||
Score int
|
||||
Ping int
|
||||
}
|
||||
|
||||
type ResultTable struct {
|
||||
Header []ResultTableHeader
|
||||
Rows []ResultTableRow
|
||||
}
|
||||
|
||||
type ResultTableHeader struct {
|
||||
Title string
|
||||
Subtitle string
|
||||
}
|
||||
|
||||
type ResultTableRow struct {
|
||||
Name string
|
||||
Total int
|
||||
Individual []int
|
||||
}
|
||||
|
||||
func (msl MapScoreList) BuildTable() *ResultTable {
|
||||
rt := &ResultTable{
|
||||
Header: []ResultTableHeader{{Title: "Name"}, {Title: "Total"}},
|
||||
}
|
||||
|
||||
userMapRows := make(map[string]*ResultTableRow)
|
||||
|
||||
for mapIndex, mapScore := range msl {
|
||||
rt.Header = append(rt.Header, ResultTableHeader{
|
||||
Title: mapScore.Map,
|
||||
Subtitle: timediff.TimeDiff(mapScore.StartTime),
|
||||
})
|
||||
for _, score := range mapScore.ScoreList {
|
||||
if score.Ping > 0 {
|
||||
if _, ok := userMapRows[score.Name]; !ok {
|
||||
rt.Rows = append(rt.Rows, ResultTableRow{
|
||||
Name: score.Name,
|
||||
Total: 0,
|
||||
Individual: make([]int, len(msl)),
|
||||
})
|
||||
|
||||
userMapRows[score.Name] = &rt.Rows[len(rt.Rows)-1]
|
||||
}
|
||||
|
||||
row := userMapRows[score.Name]
|
||||
row.Total = row.Total + score.Score
|
||||
row.Individual[mapIndex] = score.Score
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slices.SortFunc(rt.Rows, func(a, b ResultTableRow) int {
|
||||
return cmp.Compare(b.Total, a.Total)
|
||||
})
|
||||
|
||||
return rt
|
||||
}
|
||||
167
models/cod4server.go
Normal file
167
models/cod4server.go
Normal file
@ -0,0 +1,167 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var timeLayout = "Mon Jun 2 15:04:05 2006"
|
||||
|
||||
type CoD4Server struct {
|
||||
server string
|
||||
port string
|
||||
protocol string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type CoD4ServerStatus struct {
|
||||
raw string
|
||||
serverData map[string]string
|
||||
MapStartTime time.Time
|
||||
MapName string
|
||||
Score []Score
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewCOD4ServerStatus(server, port string, timeout time.Duration) (*CoD4Server, error) {
|
||||
if server == "" || port == "" {
|
||||
return nil, errors.New("server or port is empty")
|
||||
}
|
||||
|
||||
return &CoD4Server{
|
||||
server: server,
|
||||
port: port,
|
||||
protocol: "udp",
|
||||
timeout: timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CoD4Server) GetServerStatus() (*CoD4ServerStatus, error) {
|
||||
data, err := c.receiveData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parseServerData(data)
|
||||
}
|
||||
|
||||
func (c *CoD4Server) receiveData() (string, error) {
|
||||
address := fmt.Sprintf("%s:%s", c.server, c.port)
|
||||
conn, err := net.DialTimeout(c.protocol, address, c.timeout)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not connect to server: %s", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
conn.SetDeadline(time.Now().Add(c.timeout))
|
||||
_, err = conn.Write([]byte("\xFF\xFF\xFF\xFFgetstatus\x00"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to send data to server: %s", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
response := make([]byte, 8192)
|
||||
reader := bufio.NewReader(conn)
|
||||
for {
|
||||
n, err := reader.Read(response)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf.Write(response[:n])
|
||||
}
|
||||
|
||||
data := buf.String()
|
||||
if len(strings.TrimSpace(data)) == 0 {
|
||||
return "", fmt.Errorf("no data received from server: %s", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func parseServerData(data string) (*CoD4ServerStatus, error) {
|
||||
lines := strings.Split(data, "\n")
|
||||
|
||||
if len(lines) < 2 {
|
||||
return nil, fmt.Errorf("insufficient data received")
|
||||
}
|
||||
|
||||
c := &CoD4ServerStatus{
|
||||
raw: data,
|
||||
serverData: make(map[string]string),
|
||||
meta: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
tempPlayers := lines[2:]
|
||||
tempData := strings.Split(lines[1], "\\")
|
||||
|
||||
for i := 1; i < len(tempData)-1; i += 2 {
|
||||
c.serverData[tempData[i]] = tempData[i+1]
|
||||
}
|
||||
|
||||
c.serverData["sv_hostname"] = colorCode(c.serverData["sv_hostname"])
|
||||
c.serverData["_Maps"] = strings.Join(strings.Split(c.serverData["_Maps"], "-"), ",")
|
||||
c.MapName = c.serverData["mapname"]
|
||||
|
||||
startTime, err := time.Parse(timeLayout, c.serverData["g_mapStartTime"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.MapStartTime = startTime
|
||||
|
||||
for _, playerLine := range tempPlayers {
|
||||
if len(strings.TrimSpace(playerLine)) > 1 {
|
||||
temp := strings.Fields(playerLine)
|
||||
if len(temp) >= 3 {
|
||||
playerName := strings.Trim(playerLine, `"`)
|
||||
scoreValue, err := strconv.Atoi(temp[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pingValue, err := strconv.Atoi(temp[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Score = append(c.Score, Score{
|
||||
Name: playerName[strings.Index(playerName, "\"")+1:],
|
||||
Score: scoreValue,
|
||||
Ping: pingValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func colorCode(str string) string {
|
||||
str += "^"
|
||||
|
||||
colorMap := map[string]string{
|
||||
"0": "#000000",
|
||||
"1": "#F65A5A",
|
||||
"2": "#00F100",
|
||||
"3": "#EFEE04",
|
||||
"4": "#0F04E8",
|
||||
"5": "#04E8E7",
|
||||
"6": "#F75AF6",
|
||||
"7": "#FFFFFF",
|
||||
"8": "#7E7E7E",
|
||||
"9": "#6E3C3C",
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\^(\d)(.*?)\^`)
|
||||
return re.ReplaceAllStringFunc(str, func(m string) string {
|
||||
matches := re.FindStringSubmatch(m)
|
||||
return fmt.Sprintf(`<span style="color:%s;">%s</span>^`, colorMap[matches[1]], matches[2])
|
||||
})
|
||||
}
|
||||
4
models/persistence.go
Normal file
4
models/persistence.go
Normal file
@ -0,0 +1,4 @@
|
||||
package models
|
||||
|
||||
type Persistence interface {
|
||||
}
|
||||
24
routes/capture.go
Normal file
24
routes/capture.go
Normal file
@ -0,0 +1,24 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/views/capture"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (s *Server) HandleCapture(w http.ResponseWriter, r *http.Request) error {
|
||||
captureString := chi.URLParam(r, "captureID")
|
||||
captureID, err := strconv.Atoi(captureString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
foundCapture, err := s.cs.GetCaptureById(captureID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Render(w, r, capture.Capture(foundCapture))
|
||||
}
|
||||
12
routes/health.go
Normal file
12
routes/health.go
Normal file
@ -0,0 +1,12 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (s *Server) HandleHealth(w http.ResponseWriter, r *http.Request) error {
|
||||
fmt.Fprint(w, "Up and running!")
|
||||
|
||||
return nil
|
||||
}
|
||||
16
routes/home.go
Normal file
16
routes/home.go
Normal file
@ -0,0 +1,16 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/views/home"
|
||||
)
|
||||
|
||||
func (s *Server) HandleHome(w http.ResponseWriter, r *http.Request) error {
|
||||
captureList, err := s.cs.GetActiveCapures()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Render(w, r, home.Index(captureList))
|
||||
}
|
||||
27
routes/routes.go
Normal file
27
routes/routes.go
Normal file
@ -0,0 +1,27 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/services"
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
type HTTPHandler func(w http.ResponseWriter, r *http.Request) error
|
||||
|
||||
func Make(h HTTPHandler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := h(w, r); err != nil {
|
||||
slog.Error("HTTP handler error", "err", err, "path", r.URL.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
cs *services.CaptureService
|
||||
}
|
||||
|
||||
func Render(w http.ResponseWriter, r *http.Request, c templ.Component) error {
|
||||
return c.Render(r.Context(), w)
|
||||
}
|
||||
145
script/.gitignore
vendored
Normal file
145
script/.gitignore
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
### Node Patch ###
|
||||
# Serverless Webpack directories
|
||||
.webpack/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
n
|
||||
1
script/index.ts
Normal file
1
script/index.ts
Normal file
@ -0,0 +1 @@
|
||||
import 'htmx.org'
|
||||
21
script/package-lock.json
generated
Normal file
21
script/package-lock.json
generated
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.12"
|
||||
}
|
||||
},
|
||||
"node_modules/htmx.org": {
|
||||
"version": "1.9.12",
|
||||
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz",
|
||||
"integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw=="
|
||||
}
|
||||
}
|
||||
}
|
||||
14
script/package.json
Normal file
14
script/package.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.12"
|
||||
}
|
||||
}
|
||||
44
services/capture.go
Normal file
44
services/capture.go
Normal file
@ -0,0 +1,44 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
)
|
||||
|
||||
type CaptureService struct {
|
||||
}
|
||||
|
||||
var captures = []models.Capture{
|
||||
{Id: 1, Host: "80.57.28.137", Port: "28960", Active: true, Name: "Gungame HAW-LAN 11", Start: time.Now().Add(-1 * time.Hour)},
|
||||
{Id: 1, Host: "80.57.28.137", Port: "28960", Active: true, Name: "Gungame HAW-LAN 12", Start: time.Now().Add(-5 * time.Minute)},
|
||||
}
|
||||
|
||||
func (cs *CaptureService) GetActiveCapures() ([]models.Capture, error) {
|
||||
|
||||
return captures, nil
|
||||
}
|
||||
|
||||
func (cs *CaptureService) GetCaptureById(id int) (*models.Capture, error) {
|
||||
capture := captures[0]
|
||||
server, err := models.NewCOD4ServerStatus(capture.Host, capture.Port, time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status, err := server.GetServerStatus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
capture.MapScores = []models.MapScore{
|
||||
{
|
||||
Id: 0,
|
||||
StartTime: status.MapStartTime,
|
||||
Map: status.MapName,
|
||||
ScoreList: status.Score,
|
||||
},
|
||||
}
|
||||
|
||||
return &capture, nil
|
||||
}
|
||||
3
style/input.css
Normal file
3
style/input.css
Normal file
@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
9
tailwind.config.js
Normal file
9
tailwind.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./**/*.html", "./**/*.templ", "./**/*.go"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
BIN
tmp/bin/main
Executable file
BIN
tmp/bin/main
Executable file
Binary file not shown.
1
tmp/build-errors.log
Normal file
1
tmp/build-errors.log
Normal file
@ -0,0 +1 @@
|
||||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||
19
views/capture/capture.templ
Normal file
19
views/capture/capture.templ
Normal file
@ -0,0 +1,19 @@
|
||||
package capture
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Capture(capture *models.Capture) {
|
||||
@layouts.Base() {
|
||||
<div class="flex flex-col">
|
||||
<div class="block font-bold text-lg">
|
||||
{ capture.Name }
|
||||
</div>
|
||||
<div class="block">
|
||||
{ capture.Host }:{ capture.Port }
|
||||
</div>
|
||||
@components.CaptureTable(*capture.MapScores.BuildTable())
|
||||
</div>
|
||||
}
|
||||
}
|
||||
101
views/capture/capture_templ.go
Normal file
101
views/capture/capture_templ.go
Normal file
@ -0,0 +1,101 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package capture
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Capture(capture *models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 11, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Host)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 14, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Port)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 14, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.CaptureTable(*capture.MapScores.BuildTable()).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = layouts.Base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
5
views/capture/capture_templ.txt
Normal file
5
views/capture/capture_templ.txt
Normal file
@ -0,0 +1,5 @@
|
||||
<div class=\"flex flex-col\"><div class=\"block font-bold text-lg\">
|
||||
</div><div class=\"block\">
|
||||
:
|
||||
</div>
|
||||
</div>
|
||||
12
views/components/capture_card.templ
Normal file
12
views/components/capture_card.templ
Normal file
@ -0,0 +1,12 @@
|
||||
package components
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "github.com/mergestat/timediff"
|
||||
import "fmt"
|
||||
|
||||
templ CaptureCard(capture models.Capture) {
|
||||
<a href={ templ.URL(fmt.Sprintf("/captures/%d", capture.Id)) } class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
|
||||
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">{ capture.Name }</h5>
|
||||
<p class="font-normal text-gray-700 dark:text-gray-400">{ timediff.TimeDiff(capture.Start) }</p>
|
||||
</a>
|
||||
}
|
||||
74
views/components/capture_card_templ.go
Normal file
74
views/components/capture_card_templ.go
Normal file
@ -0,0 +1,74 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "github.com/mergestat/timediff"
|
||||
import "fmt"
|
||||
|
||||
func CaptureCard(capture models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 templ.SafeURL = templ.URL(fmt.Sprintf("/captures/%d", capture.Id))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_card.templ`, Line: 9, Col: 97}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timediff.TimeDiff(capture.Start))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_card.templ`, Line: 10, Col: 92}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
4
views/components/capture_card_templ.txt
Normal file
4
views/components/capture_card_templ.txt
Normal file
@ -0,0 +1,4 @@
|
||||
<a href=\"
|
||||
\" class=\"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700\"><h5 class=\"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white\">
|
||||
</h5><p class=\"font-normal text-gray-700 dark:text-gray-400\">
|
||||
</p></a>
|
||||
42
views/components/capture_table.templ
Normal file
42
views/components/capture_table.templ
Normal file
@ -0,0 +1,42 @@
|
||||
package components
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "strconv"
|
||||
|
||||
templ CaptureTable(table models.ResultTable) {
|
||||
<div class="relative overflow-x-auto">
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
for _, header := range table.Header {
|
||||
<th scope="col" class="px-6 py-3">
|
||||
<div>
|
||||
{ header.Title }
|
||||
</div>
|
||||
<div class="lowercase font-light">
|
||||
{ header.Subtitle }
|
||||
</div>
|
||||
</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, row := range table.Rows {
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{ row.Name }
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
{ strconv.Itoa(row.Total) }
|
||||
</td>
|
||||
for _, score := range row.Individual {
|
||||
<td class="px-6 py-4">
|
||||
{ strconv.Itoa(score) }
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
133
views/components/capture_table_templ.go
Normal file
133
views/components/capture_table_templ.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "strconv"
|
||||
|
||||
func CaptureTable(table models.ResultTable) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, header := range table.Header {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(header.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 14, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(header.Subtitle)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 17, Col: 25}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, row := range table.Rows {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(row.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 27, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(row.Total))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 30, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 8)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, score := range row.Individual {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 9)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(score))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 34, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 10)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 11)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 12)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
12
views/components/capture_table_templ.txt
Normal file
12
views/components/capture_table_templ.txt
Normal file
@ -0,0 +1,12 @@
|
||||
<div class=\"relative overflow-x-auto\"><table class=\"w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400\"><thead class=\"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400\"><tr>
|
||||
<th scope=\"col\" class=\"px-6 py-3\"><div>
|
||||
</div><div class=\"lowercase font-light\">
|
||||
</div></th>
|
||||
</tr></thead> <tbody>
|
||||
<tr class=\"bg-white border-b dark:bg-gray-800 dark:border-gray-700\"><th scope=\"row\" class=\"px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white\">
|
||||
</th><td class=\"px-6 py-4\">
|
||||
</td>
|
||||
<td class=\"px-6 py-4\">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table></div>
|
||||
24
views/components/navbar.templ
Normal file
24
views/components/navbar.templ
Normal file
@ -0,0 +1,24 @@
|
||||
package components
|
||||
|
||||
templ Nagivation() {
|
||||
<nav class="bg-gray-800">
|
||||
<div class="mx-auto max-w-7xl">
|
||||
<div class="relative flex h-16 items-center justify-between">
|
||||
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<div class="flex flex-shrink-0 items-center">
|
||||
<span class="text-white pr-2 font-bold">Turnier-Tracker</span>
|
||||
<img class="h-8 w-auto" src="/assets/logo.svg" alt="Your Company"/>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:block">
|
||||
<div class="flex space-x-4">
|
||||
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
|
||||
<a href="/" class="bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Dashboard</a>
|
||||
<a href="/about" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">About</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
||||
35
views/components/navbar_templ.go
Normal file
35
views/components/navbar_templ.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func Nagivation() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
1
views/components/navbar_templ.txt
Normal file
1
views/components/navbar_templ.txt
Normal file
@ -0,0 +1 @@
|
||||
<nav class=\"bg-gray-800\"><div class=\"mx-auto max-w-7xl\"><div class=\"relative flex h-16 items-center justify-between\"><div class=\"flex flex-1 items-center justify-center sm:items-stretch sm:justify-start\"><div class=\"flex flex-shrink-0 items-center\"><span class=\"text-white pr-2 font-bold\">Turnier-Tracker</span> <img class=\"h-8 w-auto\" src=\"/assets/logo.svg\" alt=\"Your Company\"></div><div class=\"hidden sm:ml-6 sm:block\"><div class=\"flex space-x-4\"><!-- Current: \"bg-gray-900 text-white\", Default: \"text-gray-300 hover:bg-gray-700 hover:text-white\" --><a href=\"/\" class=\"bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium\" aria-current=\"page\">Dashboard</a> <a href=\"/about\" class=\"text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium\">About</a></div></div></div><div class=\"absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0\"></div></div></div></nav>
|
||||
13
views/home/index.templ
Normal file
13
views/home/index.templ
Normal file
@ -0,0 +1,13 @@
|
||||
package home
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Index(captures []models.Capture) {
|
||||
@layouts.Base() {
|
||||
for _, capture := range captures {
|
||||
@components.CaptureCard(capture)
|
||||
}
|
||||
}
|
||||
}
|
||||
56
views/home/index_templ.go
Normal file
56
views/home/index_templ.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package home
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Index(captures []models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
for _, capture := range captures {
|
||||
templ_7745c5c3_Err = components.CaptureCard(capture).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = layouts.Base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
24
views/layouts/base.templ
Normal file
24
views/layouts/base.templ
Normal file
@ -0,0 +1,24 @@
|
||||
package layouts
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Base() {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>CoD 4 Turnier-Tracker</title>
|
||||
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico"/>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/styles.css"/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/js/all.min.js"></script>
|
||||
</head>
|
||||
<body class="antialiased">
|
||||
@components.Nagivation()
|
||||
<div class="mx-auto max-w-7xl mt-10 flex gap-3">
|
||||
{ children... }
|
||||
</div>
|
||||
<script src="/assets/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
53
views/layouts/base_templ.go
Normal file
53
views/layouts/base_templ.go
Normal file
@ -0,0 +1,53 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package layouts
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Base() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.Nagivation().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
3
views/layouts/base_templ.txt
Normal file
3
views/layouts/base_templ.txt
Normal file
@ -0,0 +1,3 @@
|
||||
<!doctype html><html lang=\"en\"><head><title>CoD 4 Turnier-Tracker</title><link rel=\"icon\" type=\"image/x-icon\" href=\"/assets/favicon.ico\"><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><link rel=\"stylesheet\" href=\"/assets/styles.css\"><script src=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/js/all.min.js\"></script></head><body class=\"antialiased\">
|
||||
<div class=\"mx-auto max-w-7xl mt-10 flex gap-3\">
|
||||
</div><script src=\"/assets/index.js\"></script></body></html>
|
||||
Reference in New Issue
Block a user