Config: move into seperate package

This commit is contained in:
kayos@tcp.direct 2022-07-09 12:39:29 -07:00
parent 235e9bb643
commit d5762ad77c
Signed by: kayos
GPG Key ID: 4B841471B4BEE979
9 changed files with 172 additions and 136 deletions

6
.gitignore vendored
View File

@ -1,5 +1,7 @@
.data
.logs
.idea
logs
data
logs/
data/
*.toml
*.log

View File

@ -1,24 +0,0 @@
[data]
directory = '.data'
max_key_size_mb = 10
max_value_size_mb = 20
[http]
bind_addr = '127.0.0.1'
bind_port = '8080'
unix_socket_path = '/var/run/tcp.ac'
unix_socket_permissions = 420
use_unix_socket = false
[logger]
debug = true
directory = '.logs'
nocolor = false
trace = false
use_date_filename = true
[other]
delete_key_size = 12
termbin_listen = '127.0.0.1:9999'
uid_size = 5

View File

@ -7,8 +7,11 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)
@ -19,7 +22,7 @@ const (
var (
// Version roughly represents the applications current version.
Version = "asdf"
Version = "0.0.0"
)
var (
@ -31,13 +34,17 @@ var (
UseUnixSocket bool
)
var usage = []string{
"\n" + Title + " v" + Version + " Usage\n",
"-c <toml> - Specify config file",
"--nocolor - disable color and banner ",
"--banner - show banner + version and exit",
"--genconfig - write default config to 'toml' then exit",
}
var usage = fmt.Sprintf(`
%s v%s
brought to you by:
--> tcp.direct <--
-c <file> Specify config file
--nocolor disable color and banner
--banner show banner + version and exit
--genconfig write default config to 'config.toml' then exit
`, Title, Version)
func printUsage() {
println(usage)
@ -93,7 +100,7 @@ func writeConfig() {
var err error
//goland:noinspection GoBoolExpressions
if runtime.GOOS == "windows" {
newconfig := "hellpot-config"
newconfig := Title
snek.SetConfigName(newconfig)
if err = snek.MergeInConfig(); err != nil {
if err = snek.SafeWriteConfigAs(newconfig + ".toml"); err != nil {
@ -101,34 +108,60 @@ func writeConfig() {
os.Exit(1)
}
}
Filename = newconfig + ".toml"
return
}
if _, err := os.Stat(prefConfigLocation); os.IsNotExist(err) {
if err = os.MkdirAll(prefConfigLocation, 0o750); err != nil {
if err = os.MkdirAll(prefConfigLocation, 0o740); err != nil {
println("error writing new config: " + err.Error())
os.Exit(1)
}
}
newconfig := prefConfigLocation + "/" + "toml"
newconfig := prefConfigLocation + "config.toml"
if err = snek.SafeWriteConfigAs(newconfig); err != nil {
fmt.Println("Failed to write new configuration file: " + err.Error())
os.Exit(1)
log.Fatal().Caller().Err(err).Str("target", newconfig).Msg("failed to write new configuration file")
}
Filename = newconfig
}
func init() {
var err error
home, err = os.UserHomeDir()
if err != nil {
println(err.Error())
os.Exit(1)
}
initDefaults()
}
var once = &sync.Once{}
func substantiateLogger() {
once.Do(func() {
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
lf, err := os.OpenFile(LogDir+"tcpac.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
if err != nil {
log.Fatal().Str("config.LogDir", LogDir).Err(err).Msg("Error opening log file!")
}
multi := zerolog.MultiLevelWriter(consoleWriter, lf)
log.Logger = zerolog.New(multi).With().Timestamp().Logger()
})
}
// Init will initialize our toml configuration engine and define our default configuration values which can be written to a new configuration file if desired
func Init() {
argParse()
prefConfigLocation = home + "/.config/" + Title
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
log.Logger = log.Output(consoleWriter).With().Timestamp().Logger()
prefConfigLocation = home + "/.config/" + Title + "/"
snek = viper.New()
if genConfig {
setDefaults()
println("config file generated at: " + Filename)
os.Exit(0)
}
@ -137,6 +170,7 @@ func Init() {
if customconfig {
associateExportedVariables()
substantiateLogger()
return
}
@ -147,8 +181,8 @@ func Init() {
}
if err := snek.MergeInConfig(); err != nil {
println("Error reading configuration file: " + err.Error())
println("Writing new configuration file...")
substantiateLogger()
log.Warn().Err(err).Msg("failed to read configuration file")
writeConfig()
}
@ -156,6 +190,7 @@ func Init() {
Filename = snek.ConfigFileUsed()
}
substantiateLogger()
associateExportedVariables()
}
@ -196,11 +231,9 @@ func loadCustomConfig(path string) {
switch {
case err1 != nil:
fmt.Println("config file read fatal error during i/o: ", err1.Error())
os.Exit(1)
log.Fatal().Err(err1).Msg("config file read fatal error during i/o")
case err2 != nil:
fmt.Println("config file read fatal error during parse: ", err2.Error())
os.Exit(1)
log.Fatal().Err(err2).Msg("config file read fatal error during parsing")
default:
break
}
@ -214,8 +247,14 @@ func processOpts() {
"http.bind_addr": &HTTPBind,
"http.bind_port": &HTTPPort,
"http.unix_socket_path": &UnixSocketPath,
"data.directory": &DBDir,
"logger.directory": &LogDir,
"other.termbin_listen": &TermbinListen,
"other.base_url": &BaseURL,
}
if !strings.HasSuffix(BaseURL, "/") {
BaseURL += "/"
}
// bool options and their exported variables
@ -228,9 +267,10 @@ func processOpts() {
// integer options and their exported variables
intOpt := map[string]*int{
"data.max_key_size": &KVMaxKeySizeMB,
"data.max_value_size": &KVMaxValueSizeMB,
"other.uid_size": &UIDSize,
"data.max_key_size": &KVMaxKeySizeMB,
"data.max_value_size": &KVMaxValueSizeMB,
"other.uid_size": &UIDSize,
"other.delete_key_size": &DeleteKeySize,
}
uint32Opt := map[string]*uint32{

View File

@ -1,9 +1,11 @@
package config
import (
"fmt"
"io"
"os"
"runtime"
"github.com/spf13/afero"
)
var (
@ -11,50 +13,72 @@ var (
defNoColor = false
)
var defOpts = map[string]map[string]interface{}{
"logger": {
"directory": ".logs",
"debug": true,
"trace": false,
"nocolor": defNoColor,
"use_date_filename": true,
},
"http": {
"use_unix_socket": false,
"unix_socket_path": "/var/run/tcp.ac",
"unix_socket_permissions": uint32(0644),
"bind_addr": "127.0.0.1",
"bind_port": "8080",
},
"data": {
"directory": ".data",
"max_key_size_mb": 10,
"max_value_size_mb": 20,
},
"other": {
"uid_size": 5,
"delete_key_size": 12,
"termbin_listen": "127.0.0.1:9999",
},
var defOpts map[string]map[string]interface{}
func initDefaults() {
defOpts = map[string]map[string]interface{}{
"logger": {
"directory": home + "/.local/share/tcp.ac/logs",
"debug": true,
"trace": false,
"nocolor": defNoColor,
"use_date_filename": true,
},
"http": {
"use_unix_socket": false,
"unix_socket_path": "/var/run/tcp.ac",
"unix_socket_permissions": uint32(0644),
"bind_addr": "127.0.0.1",
"bind_port": "8080",
},
"data": {
"directory": home + "/.local/share/tcp.ac/data",
"max_key_size_mb": 10,
"max_value_size_mb": 20,
},
"other": {
"uid_size": 5,
"delete_key_size": 12,
"termbin_listen": "127.0.0.1:9999",
"base_url": "http://localhost:8080/",
},
}
}
func gen(memfs afero.Fs) {
if err := snek.SafeWriteConfigAs("config.toml"); err != nil {
print(err.Error())
os.Exit(1)
}
var f afero.File
var err error
f, err = memfs.Open("config.toml")
if err != nil {
println(err.Error())
os.Exit(1)
}
newcfg, err := io.ReadAll(f)
if err != nil {
println(err.Error())
os.Exit(1)
}
println(string(newcfg))
}
func setDefaults() {
memfs := afero.NewMemMapFs()
//goland:noinspection GoBoolExpressions
if runtime.GOOS == "windows" {
snek.SetDefault("logger.directory", "./logs/")
defNoColor = true
}
if genConfig {
snek.SetFs(memfs)
}
for _, def := range configSections {
snek.SetDefault(def, defOpts[def])
}
if genConfig {
if err := snek.SafeWriteConfigAs("./config.toml"); err != nil {
fmt.Println(err.Error())
os.Exit(1)
}
os.Exit(0)
gen(memfs)
}
}

2
go.mod
View File

@ -12,6 +12,7 @@ require (
github.com/muesli/termenv v0.12.0
github.com/rs/zerolog v1.27.0
github.com/scottleedavis/go-exif-remove v0.0.0-20190908021517-58bdbaac8636
github.com/spf13/afero v1.8.2
github.com/spf13/viper v1.12.0
github.com/twharmon/gouid v0.5.2
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
@ -50,7 +51,6 @@ require (
github.com/pkg/errors v0.9.1 // indirect
github.com/plar/go-adaptive-radix-tree v1.0.4 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect

34
img.go
View File

@ -64,7 +64,7 @@ func imgDel(c *gin.Context) {
}
slog.Info().Str("rkey", finalTarget[1]).Msg("Image file deleted successfully")
slog.Debug().Str("rkey", finalTarget[1]).Msg("Removing delete key entry")
slog.Trace().Str("rkey", finalTarget[1]).Msg("Removing delete key entry")
err = db.With("key").Delete([]byte(rKey))
if err != nil {
slog.Error().Str("rkey", finalTarget[1]).Msg("Couldn't delete key")
@ -76,12 +76,12 @@ func imgDel(c *gin.Context) {
func imgView(c *gin.Context) {
slog := log.With().Str("caller", "imgView").Logger()
sUid := strings.Split(c.Param("uid"), ".")
rUid := sUid[0]
sUID := strings.Split(c.Param("uid"), ".")
rUID := sUID[0]
if len(sUid) > 1 {
fExt = strings.ToLower(sUid[1])
slog.Debug().Str("ext", fExt).Msg("detected file extension")
if len(sUID) > 1 {
fExt = strings.ToLower(sUID[1])
slog.Trace().Str("ext", fExt).Msg("detected file extension")
if fExt != "png" && fExt != "jpg" && fExt != "jpeg" && fExt != "gif" {
errThrow(c, 400, errors.New("bad file extension"), "invalid request")
return
@ -91,7 +91,7 @@ func imgView(c *gin.Context) {
}
// if it doesn't match the key size or it isn't alphanumeric - throw it out
if !valid.IsAlphanumeric(rUid) || len(rUid) != config.UIDSize {
if !valid.IsAlphanumeric(rUID) || len(rUID) != config.UIDSize {
slog.Warn().
Str("remoteaddr", c.ClientIP()).
Msg("request discarded as invalid")
@ -101,12 +101,12 @@ func imgView(c *gin.Context) {
}
// now that we think its a valid request we will query
slog.Debug().Str("rUid", rUid).Msg("request validated")
slog.Trace().Str("rUid", rUID).Msg("request validated")
// query bitcask for the id
fBytes, _ := db.With("img").Get([]byte(rUid))
fBytes, _ := db.With("img").Get([]byte(rUID))
if fBytes == nil {
slog.Error().Str("rUid", rUid).Msg("no corresponding file for this id")
slog.Error().Str("rUid", rUID).Msg("no corresponding file for this id")
errThrow(c, 404, errors.New("entry not found"), "File not found")
return
}
@ -131,7 +131,7 @@ func imgView(c *gin.Context) {
contentType := "image/" + imageFormat
c.Data(200, contentType, fBytes)
slog.Info().Str("rUid", rUid).Msg("Successful upload")
slog.Info().Str("rUid", rUID).Msg("Successful upload")
}
func newUIDandKey() (uid string, key string) {
@ -223,7 +223,7 @@ func imgPost(c *gin.Context) {
return
}
slog.Debug().Msg("duplicate checksum in hash database, checking if file still exists...")
slog.Trace().Caller().Msg("duplicate checksum in hash database, checking if file still exists...")
if db.With("img").Has(imgRef) {
slog.Debug().Str("ogUid", string(imgRef)).Msg("duplicate file found! returning original URL")
@ -232,7 +232,7 @@ func imgPost(c *gin.Context) {
return
}
slog.Debug().
slog.Trace().
Str("ogUid", string(imgRef)).
Msg("stale hash found, deleting entry...")
@ -245,15 +245,15 @@ func imgPost(c *gin.Context) {
uid, key := newUIDandKey()
// save checksum to db to prevent dupes in the future
err = db.With("hsh").Put([]byte(hash), []byte(uid))
err = db.With("hsh").Put(hash, []byte(uid))
if err != nil {
errThrow(c, 500, err, "upload failed")
return
}
// insert actual file to database
slog.Debug().Str("uid", uid).Msg("saving file to database")
err = db.With("img").Put([]byte(uid), []byte(scrubbed))
slog.Trace().Str("uid", uid).Msg("saving file to database")
err = db.With("img").Put([]byte(uid), scrubbed)
if err != nil {
errThrow(c, 500, err, "upload failed")
return
@ -268,7 +268,7 @@ func imgPost(c *gin.Context) {
}
// good to go, send them to the finisher function
slog.Debug().Str("uid", uid).Msg("saved to database successfully, sending to Serve")
slog.Trace().Str("uid", uid).Msg("saved to database successfully, sending to Serve")
post := &Post{
entryType: Image,

45
main.go
View File

@ -6,8 +6,8 @@ import (
"os/signal"
"syscall"
"github.com/gin-gonic/gin"
"github.com/muesli/termenv"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"git.tcp.direct/tcp.direct/tcp.ac/config"
@ -17,20 +17,20 @@ var Banner string = "CiAgLGQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
func makeDirectories() {
log.Trace().Msgf("establishing log directory presence at %s...", config.LogDir)
err := os.MkdirAll(config.LogDir, 0o644)
err := os.MkdirAll(config.LogDir, 0o740)
if err != nil {
log.Fatal().
Str("directory", config.LogDir).
Str("directory", config.LogDir).Caller().
Err(err).Msg("failed to open log directory")
return
}
log.Trace().Msgf("establishing data directory presence at %s...", config.DBDir)
err = os.MkdirAll(config.DBDir, 0o644)
err = os.MkdirAll(config.DBDir, 0o740)
if err != nil {
log.Fatal().
Str("directory", config.DBDir).
Err(err).Msg("failed to open directory")
Str("directory", config.DBDir).Caller().
Err(err).Msg("failed to open data directory")
return
}
}
@ -42,44 +42,35 @@ func printBanner() {
fmt.Println(out)
}
func catchSignal() {
func waitFor(router *gin.Engine) {
c := make(chan os.Signal, 5)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
for {
select {
case <-c:
log.Warn().Msg("Interrupt detected, shutting down gracefully...")
err := db.SyncAndCloseAll()
if err != nil {
log.Warn().Err(err).Msg("sync failure!")
}
os.Exit(0)
router.
return
}
}
}
func main() {
printBanner()
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
log.Logger = log.Output(consoleWriter).With().Timestamp().Logger()
log.Info().Msg("Initializing...")
config.Init()
printBanner()
makeDirectories()
log.Debug().Msg("debug enabled")
log.Trace().Msg("trace enabled")
lf, err := os.OpenFile(config.LogDir+"tcpac.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
if err != nil {
log.Fatal().Str("config.LogDir", config.LogDir).Err(err).Msg("Error opening log file!")
}
multi := zerolog.MultiLevelWriter(consoleWriter, lf)
log.Logger = zerolog.New(multi).With().Timestamp().Logger()
err = dbInit()
err := dbInit()
if err != nil {
log.Fatal().Err(err).Msg("bitcask failure")
}
defer db.SyncAndCloseAll()
defer func() {
err := db.SyncAndCloseAll()
if err != nil {
log.Warn().Err(err).Msg("sync failure!")
}
}()
go serveTermbin()
go httpRouter()
catchSignal()
waitFor(httpRouter())
}

View File

@ -31,8 +31,9 @@ func urlPost(c *gin.Context) {
return
}
func httpRouter() {
if !config.Debug {
func httpRouter() *gin.Engine {
if !config.Trace {
log.Debug().Caller().Msg("running gin in release mode, enable trace to run gin in debug mode")
gin.SetMode(gin.ReleaseMode)
}
@ -84,5 +85,7 @@ func httpRouter() {
log.Info().Str("Host", config.HTTPBind).
Str("Port", config.HTTPPort).
Msg("done; tcp.ac is live.")
router.Run(config.HTTPBind + ":" + config.HTTPPort)
go router.Run(config.HTTPBind + ":" + config.HTTPPort)
return router
}

8
txt.go
View File

@ -26,12 +26,12 @@ func incoming() {
case msg = <-termbin.Msg:
switch msg.Type {
case termbin.Error:
log.Error().
log.Warn().
Str("RemoteAddr", msg.RAddr).
Int("Size", msg.Size).
Msg(msg.Content)
case termbin.IncomingData:
log.Debug().
log.Trace().
Str("RemoteAddr", msg.RAddr).
Int("Size", msg.Size).
Msg("termbin_data")
@ -41,12 +41,12 @@ func incoming() {
Int("Size", msg.Size).
Msg(msg.Content)
case termbin.Debug:
log.Debug().
log.Trace().
Str("RemoteAddr", msg.RAddr).
Int("Size", msg.Size).
Msg(msg.Content)
case termbin.Final:
log.Debug().
log.Trace().
Str("RemoteAddr", msg.RAddr).
Int("Size", msg.Size).
Msg(msg.Content)