Fresh restructuring of project. Now with cmd, internal, and bin folder

Should be good for future microservices and such

Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
Ethan Wellenreiter 2025-04-18 01:34:39 -04:00
parent 7eb459220a
commit aa1b8d1d1b
28 changed files with 412 additions and 714 deletions

52
backend/.air.toml Normal file
View File

@ -0,0 +1,52 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "bin"
[build]
args_bin = []
bin = "./bin/main.exe"
cmd = "go build -o ./bin/main.exe ./cmd/api"
delay = 1000
exclude_dir = ["assets", "bin", "vendor", "testdata", "docs", "scripts"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
silent = false
time = false
[misc]
clean_on_exit = false
[proxy]
app_port = 0
enabled = false
proxy_port = 0
[screen]
clear_on_rebuild = false
keep_scroll = true

View File

@ -0,0 +1 @@
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1

BIN
backend/bin/main.exe Normal file

Binary file not shown.

View File

43
backend/cmd/api/api.go Normal file
View File

@ -0,0 +1,43 @@
package main
import (
"log"
"net/http"
"time"
"git.ewellenr.ca/receipt_indexer/backend/internal/auth"
)
type application struct {
// set up the configs stuff here
config config
auth auth.Authenticator
}
type config struct {
addr string
// holds the different stuff like rate limiter, store, authenticator
}
func (app *application) mount() *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("GET /v1/health", app.healthCheckHandler)
return mux
}
func (app *application) run(mux http.Handler) error {
srv := &http.Server{
Addr: app.config.addr,
Handler: mux,
WriteTimeout: time.Second * 30, // if the server takes this much time to read or write, time it out.
ReadTimeout: time.Second * 10,
IdleTimeout: time.Minute,
}
log.Printf("Server has started at %s", app.config.addr) // temporary
return srv.ListenAndServe()
}

View File

@ -0,0 +1,8 @@
package main
import "net/http"
func (app *application) healthCheckHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
}

21
backend/cmd/api/main.go Normal file
View File

@ -0,0 +1,21 @@
package main
import (
"log"
)
func main() {
// set up the application config here
cfg := config{
addr: ":8080",
}
app := application{
config: cfg,
}
// fmt.Println(app)
mux := app.mount()
log.Fatal(app.run(mux))
}

View File

@ -1,22 +0,0 @@
module backend/app
go 1.24.2
require github.com/spf13/viper v1.20.1
require (
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,52 +0,0 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
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/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/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/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
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.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,86 +0,0 @@
package main
import ( // "ewellenr.ca/ewellenr/receipt_indexer/backend/signin"
// "git.ewellenr.ca/ewellenr/receipt_indexer/backend/internal/signin"
// "receipt_indexer/backend/internal/testing"
"backend/signin"
"github.com/spf13/viper"
)
func setupConfig() {
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath("..")
viper.AddConfigPath(".")
if err := viper.ReadInConfig(); err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); ok {
// Config file not found; ignore error if desired
} else {
// Config file was found but another error was produced
}
}
}
func main() {
// fmt.Println("hi")
// params := signin.DefaultArgon2Params
// test, _ := params.GeneratePassEncoding("hello")
// fmt.Println(test)
// fmt.Println(signin.CheckPasswordAgainstEncoding("hello", test))
// fmt.Println(signin.CheckPasswordAgainstEncoding("hello1", test))
// authenticate.test2()
setupConfig()
signin.InitializeSessionRedis(viper.GetStringMapString("redis"))
}
// fmt.Println(viper.AllKeys())
// redis := viper.GetStringMapString("redis")
// fmt.Println(viper.Get("redis"))
// fmt.Println(redis["address"])
// err := signin.InitializeRedis(viper.GetStringMapString("redis"))
// if err != nil {
// fmt.Println(err)
// }
// cookie, _ := signin.Login("monkey")
// fmt.Println(cookie)
// valid, err := signin.ValidateSession(cookie)
// fmt.Println(valid)
// if err != nil {
// fmt.Println(err)
// }
// time.Sleep(time.Second * 10)
// fmt.Println(cookie)
// valid, err = signin.ValidateSession(cookie)
// fmt.Println(valid)
// if err != nil {
// fmt.Println(err)
// }
// // fmt.Println(rs)
// // fmt.Println(map[string]int{"a": 1, "b": 2, "c": 3})
// ctx := context.Background()
// err := client.Set(ctx, "test", "bar", time.Second*30).Err()
// if err != nil {
// panic(err)
// }
// val, err := client.Get(ctx, "foo").Result()
// if err != nil {
// panic(err)
// }
// fmt.Println("foo", val)
// signin.SetupSecurityAddresses()
// }

View File

@ -1,7 +0,0 @@
go 1.24.2
use (
./app
./redis_helper
./signin
)

View File

@ -1,56 +0,0 @@
cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8=
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q=
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU=
cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5PiwehZI9qGU=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE=
github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/envoyproxy/go-control-plane v0.13.1/go.mod h1:X45hY0mufo6Fd0KW3rqsGvQMw58jvjymeCzBU3mWyHw=
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok=
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=

View File

@ -1,10 +0,0 @@
module backend/redis_helper
go 1.24.2
require github.com/redis/go-redis/v9 v9.7.3
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
)

View File

@ -1,10 +0,0 @@
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=

View File

@ -1,83 +0,0 @@
package redis_helper
import (
"context"
"errors"
"strconv"
"strings"
"github.com/redis/go-redis/v9"
)
var (
ErrInvalidRedisPort = errors.New("Invalid Redis Port")
ErrInvalidRedisDB = errors.New("Invalid Redis DB mode")
ErrInvalidRedisProtocol = errors.New("Invalid Redis Protocol")
ErrInvalidDatabaseIndex = errors.New("Invalid Redis Database Index")
ErrClientNotYetSet = errors.New("Redis connection not set up")
)
var defaultRedisSettings = redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
}
func ProcessSettingsMap(settings map[string]string) (options *redis.Options, err error) {
temp := defaultRedisSettings
options = &temp
for key, val := range settings {
// fmt.Println(key, val)
if val == "" {
continue
}
switch key {
case "host":
// fmt.Println("entered")
options.Addr = val + ":" + strings.Split(options.Addr, ":")[1]
case "port":
s, err := strconv.ParseUint(val, 10, 16)
if s < 3000 || err != nil {
return nil, ErrInvalidRedisPort
}
options.Addr = strings.Split(options.Addr, ":")[0] + ":" + val
case "password":
options.Password = val
// case "db":
// s, err := strconv.ParseUint(val, 10, 64)
// if err != nil {
// return ErrInvalidRedisDB
// }
// options.DB = s
case "protocol":
s, err := strconv.ParseInt(val, 10, 0)
if err != nil {
return nil, ErrInvalidRedisProtocol
}
options.Protocol = int(s)
}
}
return options, nil
}
func SetDB(options *redis.Options, db int) error {
if db < 0 { //|| db > 16 {
return ErrInvalidDatabaseIndex
}
options.DB = db
return nil
}
func InitializeRedis(options *redis.Options) (client *redis.Client, err error) {
client = redis.NewClient(options)
err = client.Ping(context.Background()).Err()
if err != nil {
return nil, err
}
return client, nil
}

View File

@ -1,128 +0,0 @@
package signin
// credit to https://www.alexedwards.net/blog/how-to-hash-and-verify-passwords-with-argon2-in-go
import (
"fmt"
"runtime"
"strings"
"golang.org/x/crypto/argon2"
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
)
// PUBLIC METHODS AND STRUCTURES
// argon2 parameter struct
type Argon2params struct {
Memory uint32
Iterations uint32
Parallelism uint8
SaltLength uint32
KeyLength uint32
}
var DefaultArgon2Params = &Argon2params{
Memory: 64 * 1024,
Iterations: 1,
Parallelism: uint8(runtime.NumCPU()),
SaltLength: 16,
KeyLength: 32,
}
func (p *Argon2params) GeneratePassEncoding(password string) (encoding string, err error) {
salt, err := generateRandomBytes(uint(p.SaltLength))
if err != nil {
return "", err
}
hash := argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
// Base64 encode the salt and hashed password.
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
// Return a string using the standard encoded hash representation.
encoding = fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, p.Memory, p.Iterations, p.Parallelism, b64Salt, b64Hash)
return encoding, nil
}
func CheckPasswordAgainstEncoding(password string, encodedHash string) (match bool, err error) {
p, salt, hash, err := decodeHash(encodedHash)
if err != nil {
return false, err
}
// Derive the key from the other password using the same parameters.
otherHash := argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
// Check that the contents of the hashed passwords are identical. Note
// that we are using the subtle.ConstantTimeCompare() function for this
// to help prevent timing attacks.
if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
return true, nil
}
return false, nil
}
// PRIVATE STUFF
// error statements
var ErrInvalidHash = errors.New("the encoded hash is not in the correct format")
var ErrIncompatibleVersion = errors.New("incompatible version of argon2")
func decodeHash(encodedHash string) (p *Argon2params, salt []byte, hash []byte, err error) {
vals := strings.Split(encodedHash, "$")
if len(vals) != 6 {
return nil, nil, nil, ErrInvalidHash
}
var version int
_, err = fmt.Sscanf(vals[2], "v=%d", &version)
if err != nil {
return nil, nil, nil, err
}
if version != argon2.Version {
return nil, nil, nil, ErrIncompatibleVersion
}
p = &Argon2params{}
_, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.Memory, &p.Iterations, &p.Parallelism)
if err != nil {
return nil, nil, nil, err
}
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
if err != nil {
return nil, nil, nil, err
}
p.SaltLength = uint32(len(salt))
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
if err != nil {
return nil, nil, nil, err
}
p.KeyLength = uint32(len(hash))
return p, salt, hash, nil
}
func generateRandomBytes(saltLen uint) ([]byte, error) {
var bytes []byte = make([]byte, saltLen)
_, err := rand.Read(bytes)
if err != nil {
return nil, err
}
return bytes, nil
}

View File

@ -1,14 +0,0 @@
module backend/signin
go 1.24.2
require (
github.com/redis/go-redis/v9 v9.7.3
golang.org/x/crypto v0.36.0
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
golang.org/x/sys v0.31.0 // indirect
)

View File

@ -1,14 +0,0 @@
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=

View File

@ -1,156 +0,0 @@
package signin
import (
"backend/redis_helper"
"context"
"encoding/hex"
"time"
"github.com/redis/go-redis/v9"
)
// implement a periodic function to clean up the redis database of old tokens
// Redis related variables
const redisDB int = 32
var redisClient *redis.Client = nil
var ctx context.Context = nil
var SessionTime int = 600 // in seconds -1 is infinity
var TokenLength uint = 32
type SessionCookie struct {
token string
username string
}
type redisSettings struct {
host string
port uint16
password string
db uint64
protocol uint64
}
// func processSettingsMap(settings map[string]string, setting_struct *redisSettings) error {
// for key, val := range settings {
// // fmt.Println(key, val)
// if val == "" {
// continue
// }
// switch key {
// case "host":
// // fmt.Println("entered")
// setting_struct.host = val
// case "port":
// s, err := strconv.ParseUint(val, 10, 16)
// if s < 3000 || err != nil {
// return ErrInvalidRedisPort
// }
// setting_struct.port = uint16(s)
// case "password":
// setting_struct.password = val
// case "db":
// s, err := strconv.ParseUint(val, 10, 64)
// if err != nil {
// return ErrInvalidRedisDB
// }
// setting_struct.db = s
// case "protocol":
// s, err := strconv.ParseUint(val, 10, 64)
// if err != nil {
// return ErrInvalidRedisProtocol
// }
// setting_struct.db = s
// }
// }
// // fmt.Println(setting_struct)
// return nil
// }
func generateSecureToken(length uint) (string, error) {
b, err := generateRandomBytes(length)
if err != nil {
return "", err
}
return hex.EncodeToString(b), nil
}
func CreateSessionToken(username string) (token string, err error) { // generate token here
if redisClient == nil {
return "", redis_helper.ErrClientNotYetSet
}
token, err = generateSecureToken(TokenLength)
err = redisClient.Set(ctx, username, token, time.Second*time.Duration(SessionTime)).Err()
return token, err
}
func ValidateSession(username string, token string) (bool, error) { // check if it's a valid session against the redis database
if redisClient == nil {
return false, redis_helper.ErrClientNotYetSet
}
val, err := redisClient.Get(ctx, username).Result()
if err != nil || val != token {
if err == redis.Nil {
err = nil // override the error. Just to say it's not an error, but in fact it's not in the database
}
return false, err
}
return true, err
}
func InitializeSessionRedis(settings map[string]string) error {
options, err := redis_helper.ProcessSettingsMap(settings)
if err != nil {
return err
}
err = redis_helper.SetDB(options, redisDB)
if err != nil {
return err
}
// initializing redis connection
redisClient, err = redis_helper.InitializeRedis(options)
if err != nil {
return err
}
ctx = context.Background()
return nil
}
func ClearSessions() error {
if redisClient == nil {
return redis_helper.ErrClientNotYetSet
}
err := redisClient.FlushDB(ctx).Err()
if err != nil {
return err
}
return nil
}
func ClearSession(username string) error {
if redisClient == nil {
return redis_helper.ErrClientNotYetSet
}
err := redisClient.Del(ctx, username).Err()
if err != nil {
return err
}
return nil
}
// possibly need something with a csrf token

View File

@ -1,65 +0,0 @@
package signin
import (
"net/http"
)
type Login struct {
HashedPassword string
SessionToken string
CSRFToken string
}
var users = map[string]Login{}
func validUsername(username string) bool {
// Implement your logic to check if the username is valid
return true
}
func validPassword(password string) bool {
// Implement your logic to check if the password is valid
return true
}
func login(w http.ResponseWriter, r *http.Request) {
}
func logout(w http.ResponseWriter, r *http.Request) {
}
func register(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
err := http.StatusMethodNotAllowed
http.Error(w, "Invalid method", err)
return
}
username := r.FormValue("username")
password := r.FormValue("password")
if !validUsername(username) || !validPassword(password) {
err := http.StatusUnauthorized
http.Error(w, "Invalid username/password", err)
return
}
if _, ok := users[username]; ok { // place holder. actually checks the database
}
}
func test_protected(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello from the protected endpoint!"))
}
func SetupSecurityAddresses() {
http.HandleFunc("/login", login)
http.HandleFunc("/logout", logout)
http.HandleFunc("/register", register)
http.HandleFunc("/test-protected", test_protected)
}

View File

@ -1,11 +0,0 @@
redis:
host: "10.0.20.46"
port:
password: "banananana"
database:
address:
port:
user:
password:

3
backend/go.mod Normal file
View File

@ -0,0 +1,3 @@
module git.ewellenr.ca/receipt_indexer/backend
go 1.24.2

View File

@ -0,0 +1,15 @@
package auth
type Authenticator interface {
NewUser(username string, password string) error
ChangePassword(username string, oldPassword string, newPassword string) error
ResetPassword(username string, newPassword string) error // need to enable some sort of authorization stuff
DeleteUser(username string, password string) error
CreateSessionTokens() (token string, regentoken string, err error)
RegenSessionTokens(regenToken string) (token string, regentoken string, err error)
ValidateToken(token string) error
EndSession() error
// need to figure out the role stuff
}

View File

@ -0,0 +1,130 @@
package auth
import (
"git.ewellenr.ca/receipt_indexer/backend/internal/lcrypto"
"git.ewellenr.ca/receipt_indexer/backend/internal/storage"
)
type LocalAuth struct {
sessStore storage.SessionStore
userStore storage.UserStore
crypographer lcrypto.Hasher
sessionKeyLength uint
}
func generateSessionKey(length uint) (string, error) {
key, err := lcrypto.GenerateRandomBytes(length)
return string(key), err
}
func (la *LocalAuth) NewUser(username string, password string) (err error) {
// check for existing user
}
func (la *LocalAuth) CreateSessionTokens() (token string, regentoken string, err error) {
token, err = generateSessionKey(la.sessionKeyLength)
if err != nil {
return "", "", err
}
err = la.sessStore.AddSession(token)
return "", "", err
}
// // argon2 parameter struct
// type Argon2params struct {
// Memory uint32
// Iterations uint32
// Parallelism uint8
// SaltLength uint32
// KeyLength uint32
// }
// var DefaultArgon2Params = &Argon2params{
// Memory: 64 * 1024,
// Iterations: 1,
// Parallelism: uint8(runtime.NumCPU()),
// SaltLength: 16,
// KeyLength: 32,
// }
// func (p *Argon2params) GeneratePassEncoding(password string) (encoding string, err error) {
// salt, err := generateRandomBytes(uint(p.SaltLength))
// if err != nil {
// return "", err
// }
// hash := argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
// // Base64 encode the salt and hashed password.
// b64Salt := base64.RawStdEncoding.EncodeToString(salt)
// b64Hash := base64.RawStdEncoding.EncodeToString(hash)
// // Return a string using the standard encoded hash representation.
// encoding = fmt.Sprintf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, p.Memory, p.Iterations, p.Parallelism, b64Salt, b64Hash)
// return encoding, nil
// }
// func CheckPasswordAgainstEncoding(password string, encodedHash string) (match bool, err error) {
// p, salt, hash, err := decodeHash(encodedHash)
// if err != nil {
// return false, err
// }
// // Derive the key from the other password using the same parameters.
// otherHash := argon2.IDKey([]byte(password), salt, p.Iterations, p.Memory, p.Parallelism, p.KeyLength)
// // Check that the contents of the hashed passwords are identical. Note
// // that we are using the subtle.ConstantTimeCompare() function for this
// // to help prevent timing attacks.
// if subtle.ConstantTimeCompare(hash, otherHash) == 1 {
// return true, nil
// }
// return false, nil
// }
// // PRIVATE STUFF
// // error statements
// var ErrInvalidHash = errors.New("the encoded hash is not in the correct format")
// var ErrIncompatibleVersion = errors.New("incompatible version of argon2")
// func decodeHash(encodedHash string) (p *Argon2params, salt []byte, hash []byte, err error) {
// vals := strings.Split(encodedHash, "$")
// if len(vals) != 6 {
// return nil, nil, nil, ErrInvalidHash
// }
// var version int
// _, err = fmt.Sscanf(vals[2], "v=%d", &version)
// if err != nil {
// return nil, nil, nil, err
// }
// if version != argon2.Version {
// return nil, nil, nil, ErrIncompatibleVersion
// }
// p = &Argon2params{}
// _, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.Memory, &p.Iterations, &p.Parallelism)
// if err != nil {
// return nil, nil, nil, err
// }
// salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
// if err != nil {
// return nil, nil, nil, err
// }
// p.SaltLength = uint32(len(salt))
// hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
// if err != nil {
// return nil, nil, nil, err
// }
// p.KeyLength = uint32(len(hash))
// return p, salt, hash, nil
// }

18
backend/internal/env/env.go vendored Normal file
View File

@ -0,0 +1,18 @@
package env
type environment interface {
Initialize(path string)
GetString(key string, defaultValue string) string
GetBool(key string, defaultValue bool) bool
GetInt(key string, defaultValue int) int
}
// func GetString(key, fallback string) string {
// value, ok := os.LookupEnv(key)
// if !ok {
// return fallback
// }
// return value
// }
// repeate for bool and int and such

View File

@ -0,0 +1,105 @@
package lcrypto
import (
"crypto/rand"
"crypto/subtle"
"encoding/base64"
"errors"
"fmt"
"strings"
"golang.org/x/crypto/argon2"
)
var (
ErrInvalidHash = errors.New("the encoded hash is not in the correct format")
ErrIncompatibleVersion = errors.New("incompatible hash version")
ErrWrongHashAlgo = errors.New("wrong hash algorithm")
)
type Hasher interface {
CheckPass(pass string, encoded string) (bool, error)
hash(pass string) ([]byte, error)
Hash(password string) (string, error)
}
type HashAlgo interface {
name() string
version() uint
serialize() ([]byte, error)
deserialize(serialization []byte) error
}
func compareHash(h1 []byte, h2 []byte) (bool, error) {
if subtle.ConstantTimeCompare(h1, h2) == 1 {
return true, nil
}
return false, nil
}
func GenerateRandomBytes(saltLen uint) ([]byte, error) {
var bytes []byte = make([]byte, saltLen)
_, err := rand.Read(bytes)
if err != nil {
return nil, err
}
return bytes, nil
}
func encodeHashString(hashalgo string, version uint, hash []byte, salt []byte, params HashAlgo) (string, error) {
// Base64 encode the salt and hashed password.
b64Salt := base64.RawStdEncoding.EncodeToString(salt)
b64Hash := base64.RawStdEncoding.EncodeToString(hash)
paramEncode, err := params.serialize()
if err != nil {
return "", err
}
b64Params := base64.RawStdEncoding.EncodeToString(paramEncode)
encoding := fmt.Sprintf("$%s$v=%d$p=%s$%s$%s", hashalgo, version, b64Params, b64Salt, b64Hash)
return encoding, nil
}
func decodeHashString(encodedString string) (encodedHash string, hash []byte, salt []byte, err error) {
vals := strings.Split(encodedString, "$")
if len(vals) != 5 {
return "", nil, nil, ErrInvalidHash
}
var version int
_, err = fmt.Sscanf(vals[2], "v=%d", &version)
if err != nil {
return "", nil, nil, err
}
if version != argon2.Version {
return nil, nil, nil, ErrIncompatibleVersion
}
p = &Argon2params{}
_, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.Memory, &p.Iterations, &p.Parallelism)
if err != nil {
return nil, nil, nil, err
}
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
if err != nil {
return "", nil, nil, err
}
p.SaltLength = uint32(len(salt))
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
if err != nil {
return "", nil, nil, err
}
p.KeyLength = uint32(len(hash))
return p, salt, hash, nil
return "", nil, nil, nil
}

View File

@ -0,0 +1,9 @@
package storage
import "time"
type SessionStore interface {
AddSession(sessKey string) error
RemoveSession(sessKey string) error
SetLifespan(sessKey string, lf time.Time) error
}

View File

@ -0,0 +1,7 @@
package storage
type UserStore interface {
AddUser(user string, password string) error
RemoveUser(user string, password string) error
UpdateUserPass(user string, password string) error
}