remake in Golang

This commit is contained in:
2024-02-28 11:14:48 -08:00
parent cb03dd59ee
commit 84424fdae9
21 changed files with 734 additions and 1751 deletions

55
api/config.go Normal file
View File

@@ -0,0 +1,55 @@
package main
import (
"fmt"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/logger"
"github.com/spf13/viper"
)
var Config *viper.Viper
func LoadConfig() error {
setConfigDefaults()
// Read in environment variables
viper.AutomaticEnv() // read in environment variables that match
// Use config file (like json or yaml)
// viper.SetConfigName("config") // name of config file (without extension)
// viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
// Use .env file
viper.SetConfigFile(".env") // name of config file (without extension
// viper.AddConfigPath("/etc/appname/") // path to look for the config file in
// viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
return fmt.Errorf("fatal error config file: %w", err)
}
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Config file not found; ignore error if desired
return fmt.Errorf("config file not found: %w", err)
} else {
// Config file was found but another error was produced
return fmt.Errorf("fatal error config file: %w", err)
}
}
// Config file found and successfully parsed
Config = viper.GetViper()
logger.Log.Debug().Interface("config", Config.AllSettings()).Msg("Configuration loaded")
return nil
}
func setConfigDefaults() {
viper.SetDefault("MARIADB_CONNSTRING", "user:password@tcp(localhost:3306)/dbname?parseTime=true")
viper.SetDefault("API_PREFIX", "/api/v1")
viper.SetDefault("API_PORT", "1323")
}

46
api/db/main.go Normal file
View File

@@ -0,0 +1,46 @@
package db
import (
"database/sql"
"sync"
_ "github.com/go-sql-driver/mysql"
"github.com/spf13/viper"
)
var ActiveDB *sql.DB
var lock = new(sync.Mutex)
func GetDB() (*sql.DB, error) {
if ActiveDB != nil {
return ActiveDB, nil
}
db, err := ConnectDB()
if err != nil {
return nil, err
}
ActiveDB = db
return db, nil
}
func ConnectDB() (*sql.DB, error) {
lock.Lock()
defer lock.Unlock()
if ActiveDB != nil {
ActiveDB.Close()
ActiveDB = nil
}
cfg := viper.GetViper()
db, err := sql.Open("mysql", cfg.GetString("MARIADB_CONNSTRING"))
if err != nil {
return nil, err
}
ActiveDB = db
return db, nil
}

43
api/go.mod Normal file
View File

@@ -0,0 +1,43 @@
module gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API
go 1.22
require (
github.com/go-sql-driver/mysql v1.7.1
github.com/labstack/echo/v4 v4.11.4
github.com/labstack/gommon v0.4.2
github.com/spf13/viper v1.18.2
)
require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.2 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.20.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/guregu/null.v3 v3.5.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

105
api/go.sum Normal file
View File

@@ -0,0 +1,105 @@
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8=
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
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/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/guregu/null.v3 v3.5.0 h1:xTcasT8ETfMcUHn0zTvIYtQud/9Mx5dJqD554SZct0o=
gopkg.in/guregu/null.v3 v3.5.0/go.mod h1:E4tX2Qe3h7QdL+uZ3a0vqvYwKQsRSQKM5V4YltdgH9Y=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

37
api/logger/logger.go Normal file
View File

@@ -0,0 +1,37 @@
package logger
import (
"os"
"github.com/rs/zerolog"
"gopkg.in/natefinch/lumberjack.v2"
)
var Log zerolog.Logger
func init() {
// Set up logging
var logPath string
logPath = "logs/server_api.log"
apiLogger := &lumberjack.Logger{
Filename: logPath,
MaxSize: 10, // megabytes
MaxBackups: 3,
MaxAge: 28, // days
}
apiLogger.Rotate()
Log = zerolog.New(
zerolog.MultiLevelWriter( // Send logs to both console and file
// zerolog.ConsoleWriter{Out: os.Stdout},
os.Stdout,
// zerolog.ConsoleWriter{
// Out: apiLogger,
// NoColor: true,
// },
apiLogger,
),
).With().Timestamp().Logger()
}

94
api/main.go Normal file
View File

@@ -0,0 +1,94 @@
package main
import (
"net/http"
"time"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/logger"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
"github.com/rs/zerolog"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/routes"
)
func main() {
var err error
// err = godotenv.Load(".env")
// if err != nil {
// logger.Log.Warn().Err(err).Msg("Error loading .env file")
// }
e := echo.New()
// Set up logging
zerolog.TimeFieldFormat = time.RFC3339
logger.Log = logger.Log.Level(zerolog.TraceLevel)
// logger.Log = logger.Log.Level(zerolog.DebugLevel)
// logger.Log = logger.Log.Level(zerolog.InfoLevel)
logger.Log.Info().Msg("Logger initialized")
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
err = LoadConfig()
if err != nil {
logger.Log.Fatal().Err(err).Msg("Error loading configuration")
} else {
logger.Log.Info().Msg("Configuration loaded")
}
// Connect to the database
_, err = db.GetDB()
if err != nil {
logger.Log.Fatal().Err(err).Msg("Error connecting to the database")
} else {
logger.Log.Info().Msg("Connected to the database")
}
// Middleware
e.Use(middleware.RequestLoggerWithConfig(middleware.RequestLoggerConfig{
LogLatency: true,
LogURIPath: true,
LogMethod: true,
LogReferer: true,
LogUserAgent: true,
LogContentLength: true,
LogError: true,
LogRemoteIP: true,
LogURI: true,
LogStatus: true,
LogHost: true,
LogRoutePath: true,
LogRequestID: true,
LogValuesFunc: func(c echo.Context, v middleware.RequestLoggerValues) error {
logger.Log.Trace().
Caller().
Interface("RequestLoggerValues", v).
Send()
return nil
},
}))
e.Use(middleware.SecureWithConfig(middleware.SecureConfig{
XSSProtection: "",
ContentTypeNosniff: "",
XFrameOptions: "",
HSTSMaxAge: 3600,
// ContentSecurityPolicy: "default-src 'self'",
}))
e.IPExtractor = echo.ExtractIPDirect()
e.Use(middleware.Recover())
// Routes
routes.SetupRoutes(e)
e.Use(middleware.Static("static"))
e.Logger.Fatal(e.Start(":" + Config.GetString("API_PORT")))
}

198
api/ops/member.go Normal file
View File

@@ -0,0 +1,198 @@
package ops
import (
"context"
"fmt"
"time"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"github.com/labstack/echo/v4"
"gopkg.in/guregu/null.v3"
)
/*
DDL
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`timezone` varchar(5) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`website` varchar(240) DEFAULT NULL,
`guilded_id` varchar(10) DEFAULT NULL,
`steam_id_64` varchar(17) DEFAULT NULL,
`teamspeak_uid` varchar(32) DEFAULT NULL,
`steam_profile_name` varchar(32) DEFAULT NULL,
`discord_id` varchar(20) DEFAULT NULL,
`discord_username` varchar(32) DEFAULT NULL,
`aliases` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aliases`)),
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT NULL,
`remarks` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `steamId64` (`steam_id_64`) USING BTREE,
UNIQUE KEY `discordId` (`discord_id`) USING BTREE,
UNIQUE KEY `guilded_id` (`guilded_id`)
) ENGINE=InnoDB AUTO_INCREMENT=186 DEFAULT CHARSET=utf8mb4;
*/
// Member is a struct that represents a member
type Member struct {
ID int `json:"id"`
Name string `json:"name"`
Timezone null.String `json:"timezone"`
Email null.String `json:"email"`
Website null.String `json:"website"`
GuildedID null.String `json:"guilded_id"`
SteamID64 null.String `json:"steam_id_64"`
TeamspeakUID null.String `json:"teamspeak_uid"`
SteamProfileName null.String `json:"steam_profile_name"`
DiscordID null.String `json:"discord_id"`
DiscordUsername null.String `json:"discord_username"`
Aliases null.String `json:"aliases"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Deleted null.Int `json:"deleted"`
Remarks null.String `json:"remarks"`
}
/*
DDL
CREATE TABLE `members` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`timezone` varchar(5) DEFAULT NULL,
`email` varchar(100) DEFAULT NULL,
`website` varchar(240) DEFAULT NULL,
`guilded_id` varchar(10) DEFAULT NULL,
`steam_id_64` varchar(17) DEFAULT NULL,
`teamspeak_uid` varchar(32) DEFAULT NULL,
`steam_profile_name` varchar(32) DEFAULT NULL,
`discord_id` varchar(20) DEFAULT NULL,
`discord_username` varchar(32) DEFAULT NULL,
`aliases` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`aliases`)),
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`deleted` tinyint(4) DEFAULT NULL,
`remarks` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`),
UNIQUE KEY `steamId64` (`steam_id_64`) USING BTREE,
UNIQUE KEY `discordId` (`discord_id`) USING BTREE,
UNIQUE KEY `guilded_id` (`guilded_id`)
) ENGINE=InnoDB AUTO_INCREMENT=186 DEFAULT CHARSET=utf8mb4;
*/
// GetMembers returns a list of all members
func GetMembers(c echo.Context) error {
members := []Member{}
ctx, cancel := context.WithTimeout(
context.Background(),
2*time.Second,
)
defer cancel()
rows, err := db.ActiveDB.QueryContext(
ctx,
"SELECT * FROM members",
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
if ctx.Err() == context.DeadlineExceeded {
return c.JSON(500, map[string]interface{}{
"error": "request timed out",
})
}
// scan the rows into the members slice
for rows.Next() {
var m Member
err = rows.Scan(
&m.ID,
&m.Name,
&m.Timezone,
&m.Email,
&m.Website,
&m.GuildedID,
&m.SteamID64,
&m.TeamspeakUID,
&m.SteamProfileName,
&m.DiscordID,
&m.DiscordUsername,
&m.Aliases,
&m.CreatedAt,
&m.UpdatedAt,
&m.Deleted,
&m.Remarks,
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
members = append(members, m)
}
return c.JSON(200, members)
}
// GetMember returns a single member by ID
func GetMember(c echo.Context) error {
id := c.Param("id")
if id == "" {
return fmt.Errorf("id is required")
}
ctx, cancel := context.WithTimeout(
context.Background(),
2*time.Second,
)
defer cancel()
returnMember := Member{}
err := db.ActiveDB.QueryRowContext(
ctx,
"SELECT * FROM members WHERE id = ?",
id,
).Scan(
&returnMember.ID,
&returnMember.Name,
&returnMember.Timezone,
&returnMember.Email,
&returnMember.Website,
&returnMember.GuildedID,
&returnMember.SteamID64,
&returnMember.TeamspeakUID,
&returnMember.SteamProfileName,
&returnMember.DiscordID,
&returnMember.DiscordUsername,
&returnMember.Aliases,
&returnMember.CreatedAt,
&returnMember.UpdatedAt,
&returnMember.Deleted,
&returnMember.Remarks,
)
if err != nil {
return c.JSON(500, map[string]interface{}{
"error": err.Error(),
})
}
if ctx.Err() == context.DeadlineExceeded {
return c.JSON(500, map[string]interface{}{
"error": "request timed out",
})
}
return c.JSON(200, returnMember)
}

40
api/routes/handler.go Normal file
View File

@@ -0,0 +1,40 @@
package routes
import (
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/db"
"gitea.iceberg-gaming.com/17th-Ranger-Battalion-ORG/17th-UnitTracker-API/ops"
"errors"
"strings"
"github.com/labstack/echo/v4"
"github.com/spf13/viper"
)
var ErrInvalidPath = errors.New("invalid path")
func SetupRoutes(
e *echo.Echo,
) {
cfg := viper.GetViper()
prefixURL := strings.TrimRight(cfg.GetString("API_PREFIX"), "/")
mainPrefix := e.Group(prefixURL, func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// do something before the next handler
// ensure we always have a db connection
_, err := db.GetDB()
if err != nil {
return err
}
return next(c)
}
})
// MEMBER OPERATIONS
g := mainPrefix.Group("/members")
g.GET("", ops.GetMembers)
g.GET("/:id", ops.GetMember)
}