Compare commits
No commits in common. "ff1e3f04624bbd188b70ab2db18a9dadd53b624d" and "4ea67fc2e91b41f284d091623af6083604006928" have entirely different histories.
ff1e3f0462
...
4ea67fc2e9
10 changed files with 12 additions and 36 deletions
|
@ -40,7 +40,7 @@ config
|
||||||
|
|
||||||
### Config options:
|
### Config options:
|
||||||
Below are defaults of config.txt. The settings are defined in newline separated key=value pairs.
|
Below are defaults of config.txt. The settings are defined in newline separated key=value pairs.
|
||||||
Please don't include the bash-style "comments" in your actual config,
|
Please don't include the bash-style "comments" in your config,
|
||||||
they are provided purely for demonstration only and **will break the config if present**.
|
they are provided purely for demonstration only and **will break the config if present**.
|
||||||
```
|
```
|
||||||
username=admin # Your username
|
username=admin # Your username
|
||||||
|
@ -48,8 +48,7 @@ password=admin # Your password
|
||||||
port=7101 # What port to run on (probably leave on 7101 if using docker)
|
port=7101 # What port to run on (probably leave on 7101 if using docker)
|
||||||
timezone=Local # IANA Time zone database identifier ("UTC", Local", "Europe/Moscow" etc.), Defaults to Local if can't parse.
|
timezone=Local # IANA Time zone database identifier ("UTC", Local", "Europe/Moscow" etc.), Defaults to Local if can't parse.
|
||||||
language=en # ISO-639 language code (currently supported - en, ru)
|
language=en # ISO-639 language code (currently supported - en, ru)
|
||||||
log_to_file=false # Whether to write logs to a file
|
log_to_file=false # Whether to write logs to a file (located in <config-dir>/log.txt)
|
||||||
log_file=config/log.txt # Where to store the log file if it is enabled
|
|
||||||
enable_scram=false # Whether the app should shut down if there are 3 or more failed login attempts within 100 seconds
|
enable_scram=false # Whether the app should shut down if there are 3 or more failed login attempts within 100 seconds
|
||||||
|
|
||||||
# Not present by default, set only if you want to be notified of any failed login attempts over telegram
|
# Not present by default, set only if you want to be notified of any failed login attempts over telegram
|
||||||
|
@ -74,6 +73,4 @@ If you for some reason decide to run plain executable instead of docker, it supp
|
||||||
override password
|
override password
|
||||||
-port int
|
-port int
|
||||||
override port
|
override port
|
||||||
-debug
|
|
||||||
show debug log
|
|
||||||
```
|
```
|
2
TODO.md
2
TODO.md
|
@ -8,6 +8,6 @@ List of things to add to this project
|
||||||
* API revamp
|
* API revamp
|
||||||
* Check export function for improvements
|
* Check export function for improvements
|
||||||
* Customise log file
|
* Customise log file
|
||||||
* More slog.Debug
|
* More slog.Debug and a debug flag?
|
||||||
* Consider less clunky auth method
|
* Consider less clunky auth method
|
||||||
* *Go* dependency-less? <-- this is a terrible idea
|
* *Go* dependency-less? <-- this is a terrible idea
|
4
auth.go
4
auth.go
|
@ -43,7 +43,7 @@ func NoteLoginFail(username string, password string, r *http.Request) {
|
||||||
|
|
||||||
// BasicAuth is a middleware that handles authentication & authorization for the app.
|
// BasicAuth is a middleware that handles authentication & authorization for the app.
|
||||||
// It uses BasicAuth because I doubt there is a need for something sophisticated in a small hobby project
|
// It uses BasicAuth because I doubt there is a need for something sophisticated in a small hobby project
|
||||||
// Originally taken from Alex Edwards's https://www.alexedwards.net/blog/basic-authentication-in-go, MIT Licensed. (13.03.2024)
|
// Originally taken from https://www.alexedwards.net/blog/basic-authentication-in-go (13.03.2024)
|
||||||
func BasicAuth(next http.Handler) http.Handler {
|
func BasicAuth(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
username, password, ok := r.BasicAuth()
|
username, password, ok := r.BasicAuth()
|
||||||
|
@ -81,7 +81,7 @@ func Scram() {
|
||||||
// NotifyTelegram attempts to send a message to admin through telegram
|
// NotifyTelegram attempts to send a message to admin through telegram
|
||||||
func NotifyTelegram(msg string) {
|
func NotifyTelegram(msg string) {
|
||||||
if Cfg.TelegramChat == "" || Cfg.TelegramToken == "" {
|
if Cfg.TelegramChat == "" || Cfg.TelegramToken == "" {
|
||||||
slog.Debug("ignoring telegram request due to lack of credentials")
|
slog.Warn("ignoring telegram request due to lack of credentials")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client := &http.Client{}
|
client := &http.Client{}
|
||||||
|
|
13
config.go
13
config.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -22,7 +21,6 @@ type Config struct {
|
||||||
Timezone *time.Location `config:"timezone" type:"location"`
|
Timezone *time.Location `config:"timezone" type:"location"`
|
||||||
Language string `config:"language" type:"string"`
|
Language string `config:"language" type:"string"`
|
||||||
LogToFile bool `config:"log_to_file" type:"bool"`
|
LogToFile bool `config:"log_to_file" type:"bool"`
|
||||||
LogFile string `config:"log_file" type:"string"`
|
|
||||||
Scram bool `config:"enable_scram" type:"bool"`
|
Scram bool `config:"enable_scram" type:"bool"`
|
||||||
|
|
||||||
TelegramToken string `config:"tg_token" type:"string"`
|
TelegramToken string `config:"tg_token" type:"string"`
|
||||||
|
@ -116,22 +114,13 @@ func (c *Config) Reload() error {
|
||||||
} else {
|
} else {
|
||||||
c.Timezone = loc
|
c.Timezone = loc
|
||||||
}
|
}
|
||||||
slog.Debug("reloaded config", "config", c)
|
|
||||||
|
|
||||||
return LoadLanguage(c.Language) // Load selected language
|
return LoadLanguage(c.Language) // Load selected language
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConfigInit loads config on startup
|
// ConfigInit loads config on startup
|
||||||
// Some defaults are declared here
|
|
||||||
func ConfigInit() Config {
|
func ConfigInit() Config {
|
||||||
cfg := Config{
|
cfg := Config{Port: 7101, Username: "admin", Password: "admin", Timezone: time.Local, Language: "en"} // Some defaults are declared here
|
||||||
Port: 7101,
|
|
||||||
Username: "admin",
|
|
||||||
Password: "admin",
|
|
||||||
Timezone: time.Local,
|
|
||||||
Language: "en",
|
|
||||||
LogFile: "config/log.txt",
|
|
||||||
}
|
|
||||||
err := cfg.Reload()
|
err := cfg.Reload()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
@ -4,5 +4,4 @@ port=7101
|
||||||
timezone=Local
|
timezone=Local
|
||||||
language=en
|
language=en
|
||||||
log_to_file=false
|
log_to_file=false
|
||||||
log_file=config/log.txt
|
|
||||||
enable_scram=false
|
enable_scram=false
|
||||||
|
|
2
flags.go
2
flags.go
|
@ -11,7 +11,6 @@ func FlagInit() {
|
||||||
username := flag.String("user", "", "override username")
|
username := flag.String("user", "", "override username")
|
||||||
password := flag.String("pass", "", "override password")
|
password := flag.String("pass", "", "override password")
|
||||||
port := flag.Int("port", 0, "override port")
|
port := flag.Int("port", 0, "override port")
|
||||||
debug := flag.Bool("debug", false, "debug logging")
|
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
if *config != "" {
|
if *config != "" {
|
||||||
|
@ -30,5 +29,4 @@ func FlagInit() {
|
||||||
if *port != 0 {
|
if *port != 0 {
|
||||||
Cfg.Port = *port
|
Cfg.Port = *port
|
||||||
}
|
}
|
||||||
DebugMode = *debug
|
|
||||||
}
|
}
|
||||||
|
|
2
i18n.go
2
i18n.go
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
var Translations = map[string]string{}
|
var Translations = map[string]string{}
|
||||||
|
|
||||||
// LoadLanguage loads a json file for selected language into the Translations map
|
|
||||||
func LoadLanguage(language string) error {
|
func LoadLanguage(language string) error {
|
||||||
filename := "i18n/" + language + ".json"
|
filename := "i18n/" + language + ".json"
|
||||||
|
|
||||||
|
@ -29,7 +28,6 @@ func LoadLanguage(language string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TranslatableText attempts to match an id to a string in current language
|
|
||||||
func TranslatableText(id string) string {
|
func TranslatableText(id string) string {
|
||||||
if v, ok := Translations[id]; !ok {
|
if v, ok := Translations[id]; !ok {
|
||||||
return id
|
return id
|
||||||
|
|
14
logger.go
14
logger.go
|
@ -8,15 +8,15 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DebugMode = false
|
var LogFile = "config/log.txt"
|
||||||
|
|
||||||
// LogInit makes slog output to both stdout and a file if needed, and enables debug mode if selected
|
// LogInit makes slog output to both stdout and a file if needed
|
||||||
func LogInit() {
|
func LogInit() {
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
if Cfg.LogToFile {
|
if Cfg.LogToFile {
|
||||||
f, err := os.OpenFile(Cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile(LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("error opening log file, logging to stdout", "path", Cfg.LogFile, "error", err)
|
slog.Error("error opening log file, logging to stdout", "path", LogFile, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// No defer f.Close() because that breaks the MultiWriter
|
// No defer f.Close() because that breaks the MultiWriter
|
||||||
|
@ -26,10 +26,6 @@ func LogInit() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make slog and chi use intended format
|
// Make slog and chi use intended format
|
||||||
var opts *slog.HandlerOptions
|
slog.SetDefault(slog.New(slog.NewTextHandler(w, nil)))
|
||||||
if DebugMode {
|
|
||||||
opts = &slog.HandlerOptions{Level: slog.LevelDebug}
|
|
||||||
}
|
|
||||||
slog.SetDefault(slog.New(slog.NewTextHandler(w, opts)))
|
|
||||||
middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: log.Default(), NoColor: true})
|
middleware.DefaultLogger = middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: log.Default(), NoColor: true})
|
||||||
}
|
}
|
||||||
|
|
2
main.go
2
main.go
|
@ -3,7 +3,7 @@ package main
|
||||||
var Cfg = ConfigInit()
|
var Cfg = ConfigInit()
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
FlagInit()
|
|
||||||
LogInit()
|
LogInit()
|
||||||
|
FlagInit()
|
||||||
Serve()
|
Serve()
|
||||||
}
|
}
|
||||||
|
|
1
serve.go
1
serve.go
|
@ -44,6 +44,5 @@ func Serve() {
|
||||||
r.Handle("/public/*", http.StripPrefix("/public/", fs))
|
r.Handle("/public/*", http.StripPrefix("/public/", fs))
|
||||||
|
|
||||||
slog.Info("🌺 Website working", "port", Cfg.Port)
|
slog.Info("🌺 Website working", "port", Cfg.Port)
|
||||||
slog.Debug("Debug mode enabled")
|
|
||||||
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(Cfg.Port), r))
|
log.Fatal(http.ListenAndServe(":"+strconv.Itoa(Cfg.Port), r))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue