First pass of authentication
This commit is contained in:
parent
c943864edc
commit
01c6e9de87
|
|
@ -1,6 +1,7 @@
|
||||||
root = "."
|
root = "."
|
||||||
testdata_dir = "testdata"
|
testdata_dir = "testdata"
|
||||||
tmp_dir = "build/tmp"
|
tmp_dir = "build/tmp"
|
||||||
|
env_files = [".env"]
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
args_bin = []
|
args_bin = []
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@ node_modules/
|
||||||
static/assets/
|
static/assets/
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
|
.env
|
||||||
|
|
|
||||||
26
config/config.go
Normal file
26
config/config.go
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Netflix/go-env"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
DataDir string `env:"DATA_DIR"`
|
||||||
|
SiteDomain string `env:"SITE_DOMAIN"`
|
||||||
|
LoginLocked bool `env:"LOGIN_LOCKED,default=false"`
|
||||||
|
Env string `env:"ENV,default=prod"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfig() (Config, error) {
|
||||||
|
cfg := Config{}
|
||||||
|
if _, err := env.UnmarshalFromEnviron(&cfg); err != nil {
|
||||||
|
return Config{}, fmt.Errorf("failed to load config: %w", err)
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Config) IsProd() bool {
|
||||||
|
return c.Env != "dev"
|
||||||
|
}
|
||||||
19
go.mod
19
go.mod
|
|
@ -18,6 +18,7 @@ require (
|
||||||
github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect
|
github.com/Azure/go-autorest/autorest/date v0.2.0 // indirect
|
||||||
github.com/Azure/go-autorest/logger v0.1.0 // indirect
|
github.com/Azure/go-autorest/logger v0.1.0 // indirect
|
||||||
github.com/Azure/go-autorest/tracing v0.5.0 // indirect
|
github.com/Azure/go-autorest/tracing v0.5.0 // indirect
|
||||||
|
github.com/Netflix/go-env v0.1.2 // indirect
|
||||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
|
|
@ -37,21 +38,23 @@ require (
|
||||||
github.com/go-openapi/swag v0.19.12 // indirect
|
github.com/go-openapi/swag v0.19.12 // indirect
|
||||||
github.com/go-openapi/validate v0.20.0 // indirect
|
github.com/go-openapi/validate v0.20.0 // indirect
|
||||||
github.com/go-stack/stack v1.8.0 // indirect
|
github.com/go-stack/stack v1.8.0 // indirect
|
||||||
github.com/gofiber/fiber/v3 v3.0.0 // indirect
|
github.com/gofiber/fiber/v3 v3.1.0 // indirect
|
||||||
github.com/gofiber/schema v1.6.0 // indirect
|
github.com/gofiber/schema v1.7.0 // indirect
|
||||||
|
github.com/gofiber/storage/sqlite3/v2 v2.2.3 // indirect
|
||||||
github.com/gofiber/template v1.8.3 // indirect
|
github.com/gofiber/template v1.8.3 // indirect
|
||||||
github.com/gofiber/template/html/v3 v3.0.2 // indirect
|
github.com/gofiber/template/html/v3 v3.0.2 // indirect
|
||||||
github.com/gofiber/template/v2 v2.1.0 // indirect
|
github.com/gofiber/template/v2 v2.1.0 // indirect
|
||||||
github.com/gofiber/utils/v2 v2.0.0 // indirect
|
github.com/gofiber/utils/v2 v2.0.2 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.3 // indirect
|
github.com/klauspost/compress v1.18.4 // indirect
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||||
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f // indirect
|
github.com/lmika/gopkgs v0.0.0-20240408110817-a02f6fc67d1f // indirect
|
||||||
github.com/mailru/easyjson v0.7.6 // indirect
|
github.com/mailru/easyjson v0.7.6 // indirect
|
||||||
github.com/matoous/go-nanoid/v2 v2.1.0 // indirect
|
github.com/matoous/go-nanoid/v2 v2.1.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.33 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
github.com/mitchellh/mapstructure v1.4.0 // indirect
|
||||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||||
github.com/netlify/open-api/v2 v2.49.1 // indirect
|
github.com/netlify/open-api/v2 v2.49.1 // indirect
|
||||||
|
|
@ -67,11 +70,11 @@ require (
|
||||||
go.mongodb.org/mongo-driver v1.4.4 // indirect
|
go.mongodb.org/mongo-driver v1.4.4 // indirect
|
||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/crypto v0.47.0 // indirect
|
golang.org/x/crypto v0.48.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 // indirect
|
||||||
golang.org/x/net v0.49.0 // indirect
|
golang.org/x/net v0.50.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.33.0 // indirect
|
golang.org/x/text v0.34.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
modernc.org/libc v1.67.6 // indirect
|
modernc.org/libc v1.67.6 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
|
|
||||||
23
go.sum
23
go.sum
|
|
@ -31,6 +31,8 @@ github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VY
|
||||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
|
github.com/Netflix/go-env v0.1.2 h1:0DRoLR9lECQ9Zqvkswuebm3jJ/2enaDX6Ei8/Z+EnK0=
|
||||||
|
github.com/Netflix/go-env v0.1.2/go.mod h1:WlIhYi++8FlKNJtrop1mjXYAJMzv1f43K4MqCoh0yGE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
|
@ -207,8 +209,14 @@ github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/V
|
||||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
github.com/gofiber/fiber/v3 v3.0.0 h1:GPeCG8X60L42wLKrzgeewDHBr6pE6veAvwaXsqD3Xjk=
|
github.com/gofiber/fiber/v3 v3.0.0 h1:GPeCG8X60L42wLKrzgeewDHBr6pE6veAvwaXsqD3Xjk=
|
||||||
github.com/gofiber/fiber/v3 v3.0.0/go.mod h1:kVZiO/AwyT5Pq6PgC8qRCJ+j/BHrMy5jNw1O9yH38aY=
|
github.com/gofiber/fiber/v3 v3.0.0/go.mod h1:kVZiO/AwyT5Pq6PgC8qRCJ+j/BHrMy5jNw1O9yH38aY=
|
||||||
|
github.com/gofiber/fiber/v3 v3.1.0 h1:1p4I820pIa+FGxfwWuQZ5rAyX0WlGZbGT6Hnuxt6hKY=
|
||||||
|
github.com/gofiber/fiber/v3 v3.1.0/go.mod h1:n2nYQovvL9z3Too/FGOfgtERjW3GQcAUqgfoezGBZdU=
|
||||||
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
|
github.com/gofiber/schema v1.6.0 h1:rAgVDFwhndtC+hgV7Vu5ItQCn7eC2mBA4Eu1/ZTiEYY=
|
||||||
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
|
github.com/gofiber/schema v1.6.0/go.mod h1:WNZWpQx8LlPSK7ZaX0OqOh+nQo/eW2OevsXs1VZfs/s=
|
||||||
|
github.com/gofiber/schema v1.7.0 h1:yNM+FNRZjyYEli9Ey0AXRBrAY9jTnb+kmGs3lJGPvKg=
|
||||||
|
github.com/gofiber/schema v1.7.0/go.mod h1:A/X5Ffyru4p9eBdp99qu+nzviHzQiZ7odLT+TwxWhbk=
|
||||||
|
github.com/gofiber/storage/sqlite3/v2 v2.2.3 h1:m3n80wUewnB5ruAV3Qq0mzIS+bwBrYYETo4N+fvBoow=
|
||||||
|
github.com/gofiber/storage/sqlite3/v2 v2.2.3/go.mod h1:F1w9BpQtU7BD5cCjlQnFIEjWHUaAcm9Hh5fuCpfG/OE=
|
||||||
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
github.com/gofiber/template v1.8.3 h1:hzHdvMwMo/T2kouz2pPCA0zGiLCeMnoGsQZBTSYgZxc=
|
||||||
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
github.com/gofiber/template v1.8.3/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
|
||||||
github.com/gofiber/template/html/v3 v3.0.2 h1:/Fh8UcEsB4uhf1QWNbYaAOwXxSORebJ2zXkb5tgG/TI=
|
github.com/gofiber/template/html/v3 v3.0.2 h1:/Fh8UcEsB4uhf1QWNbYaAOwXxSORebJ2zXkb5tgG/TI=
|
||||||
|
|
@ -217,6 +225,8 @@ github.com/gofiber/template/v2 v2.1.0 h1:vrLY6uEW2HdioJm6J5FGUpYZuapVQhHciNz21XQ
|
||||||
github.com/gofiber/template/v2 v2.1.0/go.mod h1:ohgpR/Ng90nJbK+IyNzrgR/XpnBNt862/oTF5G7SAmE=
|
github.com/gofiber/template/v2 v2.1.0/go.mod h1:ohgpR/Ng90nJbK+IyNzrgR/XpnBNt862/oTF5G7SAmE=
|
||||||
github.com/gofiber/utils/v2 v2.0.0 h1:SCC3rpsEDWupFSHtc0RKxg/BKgV0s1qKfZg9Jv6D0sM=
|
github.com/gofiber/utils/v2 v2.0.0 h1:SCC3rpsEDWupFSHtc0RKxg/BKgV0s1qKfZg9Jv6D0sM=
|
||||||
github.com/gofiber/utils/v2 v2.0.0/go.mod h1:xF9v89FfmbrYqI/bQUGN7gR8ZtXot2jxnZvmAUtiavE=
|
github.com/gofiber/utils/v2 v2.0.0/go.mod h1:xF9v89FfmbrYqI/bQUGN7gR8ZtXot2jxnZvmAUtiavE=
|
||||||
|
github.com/gofiber/utils/v2 v2.0.2 h1:ShRRssz0F3AhTlAQcuEj54OEDtWF7+HJDwEi/aa6QLI=
|
||||||
|
github.com/gofiber/utils/v2 v2.0.2/go.mod h1:+9Ub4NqQ+IaJoTliq5LfdmOJAA/Hzwf4pXOxOa3RrJ0=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
|
@ -302,6 +312,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||||
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
|
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||||
|
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||||
|
|
@ -335,6 +347,8 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg
|
||||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
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/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.33 h1:A5blZ5ulQo2AtayQ9/limgHEkFreKj1Dv226a1K73s0=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.33/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||||
|
|
@ -481,6 +495,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||||
|
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||||
|
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
|
@ -535,6 +551,8 @@ golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
|
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||||
|
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
|
@ -582,6 +600,8 @@ golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
|
|
@ -595,6 +615,8 @@ golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||||
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
|
@ -629,6 +651,7 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||||
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA=
|
||||||
|
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
|
|
||||||
62
handlers/login.go
Normal file
62
handlers/login.go
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/session"
|
||||||
|
"lmika.dev/lmika/weiro/config"
|
||||||
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/services/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoginHandler struct {
|
||||||
|
Config config.Config
|
||||||
|
AuthService *auth.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lh *LoginHandler) Login(c fiber.Ctx) error {
|
||||||
|
if lh.Config.LoginLocked {
|
||||||
|
return c.Status(fiber.StatusForbidden).SendString("Login is locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
loginChallenge := models.NewNanoID()
|
||||||
|
|
||||||
|
sess := session.FromContext(c)
|
||||||
|
sess.Set("_login_challenge", loginChallenge)
|
||||||
|
|
||||||
|
c.Render("login/login", fiber.Map{
|
||||||
|
"challenge": loginChallenge,
|
||||||
|
}, "layouts/bare")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lh *LoginHandler) DoLogin(c fiber.Ctx) error {
|
||||||
|
var req struct {
|
||||||
|
Username string `form:"username"`
|
||||||
|
Password string `form:"password"`
|
||||||
|
LoginChallenge string `form:"_login_challenge"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Username == "" || req.Password == "" {
|
||||||
|
return c.Status(fiber.StatusBadRequest).SendString("Username and password are required")
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := session.FromContext(c)
|
||||||
|
|
||||||
|
challenge, _ := sess.Get("_login_challenge").(string)
|
||||||
|
if challenge == req.LoginChallenge {
|
||||||
|
return c.Redirect().To("/login")
|
||||||
|
}
|
||||||
|
|
||||||
|
user, err := lh.AuthService.Login(c.Context(), req.Username, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to login")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := sess.Regenerate(); err != nil {
|
||||||
|
return c.Status(fiber.StatusInternalServerError).SendString("Failed to login")
|
||||||
|
}
|
||||||
|
|
||||||
|
sess.Set("user_id", user.ID)
|
||||||
|
|
||||||
|
return c.Redirect().To("/")
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,27 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v3"
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/session"
|
||||||
"lmika.dev/lmika/weiro/models"
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/services/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AuthUser() func(c fiber.Ctx) error {
|
func AuthUser(auth *auth.Service) func(c fiber.Ctx) error {
|
||||||
return func(c fiber.Ctx) error {
|
return func(c fiber.Ctx) error {
|
||||||
// TEMP - Actually do the auth here
|
sess := session.FromContext(c)
|
||||||
user := models.User{
|
userID, _ := sess.Get("user_id").(int64)
|
||||||
ID: 1,
|
if userID == 0 {
|
||||||
Username: "testuser",
|
return c.Redirect().To("/login")
|
||||||
TimeZone: "Australia/Melbourne",
|
}
|
||||||
|
|
||||||
|
user, err := auth.GetUser(c.Context(), userID)
|
||||||
|
if err != nil {
|
||||||
|
return c.Redirect().To("/login")
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Locals("user", user)
|
c.Locals("user", user)
|
||||||
c.SetContext(models.WithUser(c.Context(), user))
|
c.SetContext(models.WithUser(c.Context(), user))
|
||||||
log.Printf("User %s authenticated", user.Username)
|
|
||||||
|
|
||||||
return c.Next()
|
return c.Next()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
72
main.go
72
main.go
|
|
@ -5,46 +5,47 @@ import (
|
||||||
"html"
|
"html"
|
||||||
"html/template"
|
"html/template"
|
||||||
"log"
|
"log"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v3"
|
"github.com/gofiber/fiber/v3"
|
||||||
|
"github.com/gofiber/fiber/v3/extractors"
|
||||||
|
"github.com/gofiber/fiber/v3/middleware/session"
|
||||||
"github.com/gofiber/fiber/v3/middleware/static"
|
"github.com/gofiber/fiber/v3/middleware/static"
|
||||||
|
"github.com/gofiber/storage/sqlite3/v2"
|
||||||
fiber_html "github.com/gofiber/template/html/v3"
|
fiber_html "github.com/gofiber/template/html/v3"
|
||||||
|
"github.com/gofiber/utils/v2"
|
||||||
"github.com/yuin/goldmark"
|
"github.com/yuin/goldmark"
|
||||||
"github.com/yuin/goldmark/extension"
|
"github.com/yuin/goldmark/extension"
|
||||||
|
"lmika.dev/lmika/weiro/config"
|
||||||
"lmika.dev/lmika/weiro/handlers"
|
"lmika.dev/lmika/weiro/handlers"
|
||||||
"lmika.dev/lmika/weiro/handlers/middleware"
|
"lmika.dev/lmika/weiro/handlers/middleware"
|
||||||
"lmika.dev/lmika/weiro/providers/db"
|
"lmika.dev/lmika/weiro/providers/db"
|
||||||
|
"lmika.dev/lmika/weiro/services/auth"
|
||||||
"lmika.dev/lmika/weiro/services/posts"
|
"lmika.dev/lmika/weiro/services/posts"
|
||||||
"lmika.dev/lmika/weiro/services/publisher"
|
"lmika.dev/lmika/weiro/services/publisher"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
dbp, err := db.New("build/weiro.db")
|
cfg, err := config.LoadConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dbp, err := db.New(filepath.Join(cfg.DataDir, "weiro.db"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
defer dbp.Close()
|
defer dbp.Close()
|
||||||
|
|
||||||
|
authSvc := auth.New(dbp)
|
||||||
publisherSvc := publisher.New(dbp)
|
publisherSvc := publisher.New(dbp)
|
||||||
publisherQueue := publisher.NewQueue(publisherSvc)
|
publisherQueue := publisher.NewQueue(publisherSvc)
|
||||||
|
|
||||||
publisherQueue.Start(context.Background())
|
publisherQueue.Start(context.Background())
|
||||||
|
|
||||||
postService := posts.New(dbp, publisherQueue)
|
postService := posts.New(dbp, publisherQueue)
|
||||||
|
|
||||||
//user, err := dbp.SelectUserByUsername(context.Background(), "testuser")
|
|
||||||
//if err != nil {
|
|
||||||
// user = models.User{
|
|
||||||
// Username: "testuser",
|
|
||||||
// PasswordHashed: []byte("changeme"),
|
|
||||||
// }
|
|
||||||
// if err := dbp.SaveUser(context.Background(), &user); err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
fiberTemplate := fiber_html.New("./views", ".html")
|
fiberTemplate := fiber_html.New("./views", ".html")
|
||||||
fiberTemplate.Funcmap["sub"] = func(x, y int) int { return x - y }
|
fiberTemplate.Funcmap["sub"] = func(x, y int) int { return x - y }
|
||||||
fiberTemplate.Funcmap["markdown"] = func() func(s string) template.HTML {
|
fiberTemplate.Funcmap["markdown"] = func() func(s string) template.HTML {
|
||||||
|
|
@ -60,16 +61,57 @@ func main() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Initialize custom config
|
||||||
|
store := sqlite3.New(sqlite3.Config{
|
||||||
|
Database: filepath.Join(cfg.DataDir, "./fiber.db"),
|
||||||
|
Table: "fiber_storage",
|
||||||
|
Reset: false,
|
||||||
|
GCInterval: 10 * time.Second,
|
||||||
|
MaxOpenConns: 100,
|
||||||
|
MaxIdleConns: 100,
|
||||||
|
ConnMaxLifetime: 1 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
app := fiber.New(fiber.Config{
|
app := fiber.New(fiber.Config{
|
||||||
Views: fiberTemplate,
|
Views: fiberTemplate,
|
||||||
ViewsLayout: "layouts/main",
|
ViewsLayout: "layouts/main",
|
||||||
PassLocalsToViews: true,
|
PassLocalsToViews: true,
|
||||||
})
|
})
|
||||||
|
app.Use(session.New(session.Config{
|
||||||
|
// Storage
|
||||||
|
Storage: store,
|
||||||
|
|
||||||
siteGroup := app.Group("/sites/:siteID", middleware.AuthUser(), middleware.RequiresSite(dbp))
|
// Security
|
||||||
|
CookieSecure: cfg.IsProd(),
|
||||||
|
CookieSameSite: "Lax",
|
||||||
|
|
||||||
|
// Session Management
|
||||||
|
IdleTimeout: 24 * time.Hour, // Inactivity timeout
|
||||||
|
AbsoluteTimeout: 7 * 24 * time.Hour, // Maximum session duration
|
||||||
|
|
||||||
|
// Cookie Settings
|
||||||
|
CookiePath: "/",
|
||||||
|
CookieDomain: cfg.SiteDomain,
|
||||||
|
CookieSessionOnly: false, // Persist across browser restarts
|
||||||
|
|
||||||
|
// Session ID
|
||||||
|
Extractor: extractors.FromCookie("__wro-session_id"),
|
||||||
|
KeyGenerator: utils.SecureToken,
|
||||||
|
|
||||||
|
// Error Handling
|
||||||
|
ErrorHandler: func(c fiber.Ctx, err error) {
|
||||||
|
log.Printf("Session error: %v", err)
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
lh := handlers.LoginHandler{Config: cfg, AuthService: authSvc}
|
||||||
ph := handlers.PostsHandler{PostService: postService}
|
ph := handlers.PostsHandler{PostService: postService}
|
||||||
|
|
||||||
|
app.Get("/login", lh.Login)
|
||||||
|
app.Post("/login", lh.Login)
|
||||||
|
|
||||||
|
siteGroup := app.Group("/sites/:siteID", middleware.AuthUser(authSvc), middleware.RequiresSite(dbp))
|
||||||
|
|
||||||
siteGroup.Get("/posts", ph.Index)
|
siteGroup.Get("/posts", ph.Index)
|
||||||
siteGroup.Get("/posts/new", ph.New)
|
siteGroup.Get("/posts/new", ph.New)
|
||||||
siteGroup.Get("/posts/:postID", ph.Edit)
|
siteGroup.Get("/posts/:postID", ph.Edit)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID int64
|
ID int64
|
||||||
|
|
@ -9,6 +13,16 @@ type User struct {
|
||||||
TimeZone string
|
TimeZone string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) SetPassword(pwd string) {
|
||||||
|
bcrypted, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
||||||
|
u.PasswordHashed = bcrypted
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u User) CheckPassword(pwd string) bool {
|
||||||
|
err := bcrypt.CompareHashAndPassword(u.PasswordHashed, []byte(pwd))
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u User) FormatTime(t time.Time) string {
|
func (u User) FormatTime(t time.Time) string {
|
||||||
if loc := getLocation(u.TimeZone); loc != nil {
|
if loc := getLocation(u.TimeZone); loc != nil {
|
||||||
return t.In(loc).Format("2006-01-02 15:04:05")
|
return t.In(loc).Format("2006-01-02 15:04:05")
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,19 @@ func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64,
|
||||||
return id, err
|
return id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectUserByID = `-- name: SelectUserByID :one
|
||||||
|
SELECT id, username, password FROM users WHERE id = ? LIMIT 1
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) SelectUserByID(ctx context.Context, id int64) (User, error) {
|
||||||
|
row := q.db.QueryRowContext(ctx, selectUserByID, id)
|
||||||
|
var i User
|
||||||
|
err := row.Scan(&i.ID, &i.Username, &i.Password)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const selectUserByUsername = `-- name: SelectUserByUsername :one
|
const selectUserByUsername = `-- name: SelectUserByUsername :one
|
||||||
SELECT id, username, password FROM users WHERE username = ?
|
SELECT id, username, password FROM users WHERE username = ? LIMIT 1
|
||||||
`
|
`
|
||||||
|
|
||||||
func (q *Queries) SelectUserByUsername(ctx context.Context, username string) (User, error) {
|
func (q *Queries) SelectUserByUsername(ctx context.Context, username string) (User, error) {
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@ func (db *Provider) SelectUserByUsername(ctx context.Context, username string) (
|
||||||
return models.User{}, err
|
return models.User{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pwdBytes, err := base64.StdEncoding.DecodeString(res.Password)
|
return dbUserToUser(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *Provider) SelectUserByID(ctx context.Context, userID int64) (models.User, error) {
|
||||||
|
res, err := db.queries.SelectUserByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.User{}, err
|
return models.User{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return models.User{
|
return dbUserToUser(res)
|
||||||
ID: res.ID,
|
|
||||||
Username: res.Username,
|
|
||||||
PasswordHashed: pwdBytes,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Provider) SaveUser(ctx context.Context, user *models.User) error {
|
func (db *Provider) SaveUser(ctx context.Context, user *models.User) error {
|
||||||
|
|
@ -47,3 +47,16 @@ func (db *Provider) SaveUser(ctx context.Context, user *models.User) error {
|
||||||
Password: hashedPassword,
|
Password: hashedPassword,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func dbUserToUser(res sqlgen.User) (models.User, error) {
|
||||||
|
pwdBytes, err := base64.StdEncoding.DecodeString(res.Password)
|
||||||
|
if err != nil {
|
||||||
|
return models.User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return models.User{
|
||||||
|
ID: res.ID,
|
||||||
|
Username: res.Username,
|
||||||
|
PasswordHashed: pwdBytes,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
36
services/auth/service.go
Normal file
36
services/auth/service.go
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"emperror.dev/errors"
|
||||||
|
"lmika.dev/lmika/weiro/models"
|
||||||
|
"lmika.dev/lmika/weiro/providers/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Service struct {
|
||||||
|
db *db.Provider
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db *db.Provider) *Service {
|
||||||
|
return &Service{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) Login(ctx context.Context, username, password string) (models.User, error) {
|
||||||
|
user, err := s.db.SelectUserByUsername(ctx, username)
|
||||||
|
if err != nil {
|
||||||
|
return models.User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !user.CheckPassword(password) {
|
||||||
|
return models.User{}, errors.New("invalid password")
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GetUser(ctx context.Context, userID int64) (models.User, error) {
|
||||||
|
return s.db.SelectUserByID(ctx, userID)
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
-- name: SelectUserByUsername :one
|
-- name: SelectUserByUsername :one
|
||||||
SELECT * FROM users WHERE username = ?;
|
SELECT * FROM users WHERE username = ? LIMIT 1;
|
||||||
|
|
||||||
|
-- name: SelectUserByID :one
|
||||||
|
SELECT * FROM users WHERE id = ? LIMIT 1;
|
||||||
|
|
||||||
-- name: InsertUser :one
|
-- name: InsertUser :one
|
||||||
INSERT INTO users (username, password) VALUES (?, ?) RETURNING id;
|
INSERT INTO users (username, password) VALUES (?, ?) RETURNING id;
|
||||||
|
|
|
||||||
12
views/layouts/bare.html
Normal file
12
views/layouts/bare.html
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Weiro</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<link rel="stylesheet" href="/static/assets/main.css">
|
||||||
|
</head>
|
||||||
|
<body class="min-vh-100 d-flex flex-column">
|
||||||
|
{{ embed }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
19
views/login/login.html
Normal file
19
views/login/login.html
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<div class="mx-auto p-2" style="width: 400px; margin-block-start: 100px;">
|
||||||
|
<div class="text-center mb-3">
|
||||||
|
<h3>Weiro Login</h3>
|
||||||
|
</div>
|
||||||
|
<input type="hidden" name="_login_challenge" value="{{ .challenge }}">
|
||||||
|
<form action="/login" method="post">
|
||||||
|
<div class="mb-2">
|
||||||
|
<label for="login_username" class="form-label">Login</label>
|
||||||
|
<input type="email" class="form-control" id="login_username">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="login_password" class="form-label">Password</label>
|
||||||
|
<input type="password" class="form-control" id="login_password">
|
||||||
|
</div>
|
||||||
|
<div class="mb-3 text-end">
|
||||||
|
<input type="submit" class="btn btn-primary" value="Login">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
Loading…
Reference in a new issue