rebase 12/02, use go1.16rc1 in make, remove ioutil, start switching to io/fs for file i/o

ioutil's contents are now in io and os.
Eventually jfa-go's files will be embedded in the binary with go1.16's
new embed feature. Using io/fs will provide abstraction for accessing
these files, and allow for both embedded and non-embedded versions.
Also, internal paths to things like email templates, etc. will be
prefixed with "jfa-go:" to indicate to use the app's own Filesystem
instead of reading the file normally. This also allows for custom files
to continue to be used as they are currently.
This commit is contained in:
Harvey Tindall 2021-01-31 23:12:50 +00:00
parent 1af8d1f77d
commit fefe2d82a4
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
10 changed files with 170 additions and 144 deletions

View File

@ -43,7 +43,7 @@ ts-debug:
cp -r ts build/data/web/js
swagger:
go get github.com/swaggo/swag/cmd/swag
go1.16rc1 get github.com/swaggo/swag/cmd/swag
swag init -g main.go
version:
@ -51,10 +51,10 @@ version:
compile:
$(info Downloading deps)
go mod download
go1.16rc1 mod download
$(info Building)
mkdir -p build
CGO_ENABLED=0 go build -o build/jfa-go *.go
CGO_ENABLED=0 go1.16rc1 build -o build/jfa-go *.go
compress:
upx --lzma build/jfa-go

View File

@ -2,6 +2,7 @@ package main
import (
"fmt"
"io/fs"
"path/filepath"
"strconv"
"strings"
@ -11,6 +12,14 @@ import (
var emailEnabled = false
func (app *appContext) GetPath(sect, key string) (fs.FS, string) {
val := app.config.Section(sect).Key(key).MustString("")
if strings.HasPrefix(val, "jfa-go:") {
return app.localFS, strings.TrimPrefix(val, "jfa-go:")
}
return app.systemFS, val
}
func (app *appContext) loadConfig() error {
var err error
app.config, err = ini.Load(app.configPath)
@ -31,26 +40,26 @@ func (app *appContext) loadConfig() error {
app.URLBase = strings.TrimSuffix(app.config.Section("ui").Key("url_base").MustString(""), "/")
app.config.Section("email").Key("no_username").SetValue(strconv.FormatBool(app.config.Section("email").Key("no_username").MustBool(false)))
app.config.Section("password_resets").Key("email_html").SetValue(app.config.Section("password_resets").Key("email_html").MustString(filepath.Join(app.localPath, "email.html")))
app.config.Section("password_resets").Key("email_text").SetValue(app.config.Section("password_resets").Key("email_text").MustString(filepath.Join(app.localPath, "email.txt")))
app.config.Section("password_resets").Key("email_html").SetValue(app.config.Section("password_resets").Key("email_html").MustString("jfa-go:" + "email.html"))
app.config.Section("password_resets").Key("email_text").SetValue(app.config.Section("password_resets").Key("email_text").MustString("jfa-go:" + "email.txt"))
app.config.Section("invite_emails").Key("email_html").SetValue(app.config.Section("invite_emails").Key("email_html").MustString(filepath.Join(app.localPath, "invite-email.html")))
app.config.Section("invite_emails").Key("email_text").SetValue(app.config.Section("invite_emails").Key("email_text").MustString(filepath.Join(app.localPath, "invite-email.txt")))
app.config.Section("invite_emails").Key("email_html").SetValue(app.config.Section("invite_emails").Key("email_html").MustString("jfa-go:" + "invite-email.html"))
app.config.Section("invite_emails").Key("email_text").SetValue(app.config.Section("invite_emails").Key("email_text").MustString("jfa-go:" + "invite-email.txt"))
app.config.Section("email_confirmation").Key("email_html").SetValue(app.config.Section("email_confirmation").Key("email_html").MustString(filepath.Join(app.localPath, "confirmation.html")))
app.config.Section("email_confirmation").Key("email_text").SetValue(app.config.Section("email_confirmation").Key("email_text").MustString(filepath.Join(app.localPath, "confirmation.txt")))
app.config.Section("email_confirmation").Key("email_html").SetValue(app.config.Section("email_confirmation").Key("email_html").MustString("jfa-go:" + "confirmation.html"))
app.config.Section("email_confirmation").Key("email_text").SetValue(app.config.Section("email_confirmation").Key("email_text").MustString("jfa-go:" + "confirmation.txt"))
app.config.Section("notifications").Key("expiry_html").SetValue(app.config.Section("notifications").Key("expiry_html").MustString(filepath.Join(app.localPath, "expired.html")))
app.config.Section("notifications").Key("expiry_text").SetValue(app.config.Section("notifications").Key("expiry_text").MustString(filepath.Join(app.localPath, "expired.txt")))
app.config.Section("notifications").Key("expiry_html").SetValue(app.config.Section("notifications").Key("expiry_html").MustString("jfa-go:" + "expired.html"))
app.config.Section("notifications").Key("expiry_text").SetValue(app.config.Section("notifications").Key("expiry_text").MustString("jfa-go:" + "expired.txt"))
app.config.Section("notifications").Key("created_html").SetValue(app.config.Section("notifications").Key("created_html").MustString(filepath.Join(app.localPath, "created.html")))
app.config.Section("notifications").Key("created_text").SetValue(app.config.Section("notifications").Key("created_text").MustString(filepath.Join(app.localPath, "created.txt")))
app.config.Section("notifications").Key("created_html").SetValue(app.config.Section("notifications").Key("created_html").MustString("jfa-go:" + "created.html"))
app.config.Section("notifications").Key("created_text").SetValue(app.config.Section("notifications").Key("created_text").MustString("jfa-go:" + "created.txt"))
app.config.Section("deletion").Key("email_html").SetValue(app.config.Section("deletion").Key("email_html").MustString(filepath.Join(app.localPath, "deleted.html")))
app.config.Section("deletion").Key("email_text").SetValue(app.config.Section("deletion").Key("email_text").MustString(filepath.Join(app.localPath, "deleted.txt")))
app.config.Section("deletion").Key("email_html").SetValue(app.config.Section("deletion").Key("email_html").MustString("jfa-go:" + "deleted.html"))
app.config.Section("deletion").Key("email_text").SetValue(app.config.Section("deletion").Key("email_text").MustString("jfa-go:" + "deleted.txt"))
app.config.Section("welcome_email").Key("email_html").SetValue(app.config.Section("welcome_email").Key("email_html").MustString(filepath.Join(app.localPath, "welcome.html")))
app.config.Section("welcome_email").Key("email_text").SetValue(app.config.Section("welcome_email").Key("email_text").MustString(filepath.Join(app.localPath, "welcome.txt")))
app.config.Section("welcome_email").Key("email_html").SetValue(app.config.Section("welcome_email").Key("email_html").MustString("jfa-go:" + "welcome.html"))
app.config.Section("welcome_email").Key("email_text").SetValue(app.config.Section("welcome_email").Key("email_text").MustString("jfa-go:" + "welcome.txt"))
app.config.Section("jellyfin").Key("version").SetValue(VERSION)
app.config.Section("jellyfin").Key("device").SetValue("jfa-go")

View File

@ -65,7 +65,7 @@
["emby", "Emby"]
],
"value": "jellyfin",
"description": "Note: Emby integration works is missing some features, such as Password Resets."
"description": "Note: Emby integration works but is missing some features, such as Password Resets."
},
"substitute_jellyfin_strings": {
"name": "Substitute occurrences of \"Jellyfin\"",

View File

@ -162,8 +162,8 @@ func (emailer *Emailer) constructConfirmation(code, username, key string, app *a
inviteLink = fmt.Sprintf("%s/%s?key=%s", inviteLink, code, key)
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("email_confirmation").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("email_confirmation", "email_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -199,8 +199,8 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
inviteLink = fmt.Sprintf("%s/%s", inviteLink, code)
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("invite_emails").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("invite_emails", "email_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -232,8 +232,8 @@ func (emailer *Emailer) constructExpiry(code string, invite Invite, app *appCont
}
expiry := app.formatDatetime(invite.ValidTill)
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("notifications").Key("expiry_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("notifications", "expiry_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -267,8 +267,8 @@ func (emailer *Emailer) constructCreated(code, username, address string, invite
tplAddress = address
}
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("notifications").Key("created_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("notifications", "created_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -302,8 +302,8 @@ func (emailer *Emailer) constructReset(pwr PasswordReset, app *appContext) (*Ema
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
message := app.config.Section("email").Key("message").String()
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("password_resets").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("password_resets", "email_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -335,8 +335,8 @@ func (emailer *Emailer) constructDeleted(reason string, app *appContext) (*Email
subject: app.config.Section("deletion").Key("subject").MustString(emailer.lang.UserDeleted.get("title")),
}
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("deletion").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("deletion", "email_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}
@ -363,8 +363,8 @@ func (emailer *Emailer) constructWelcome(username string, app *appContext) (*Ema
subject: app.config.Section("welcome_email").Key("subject").MustString(emailer.lang.WelcomeEmail.get("title")),
}
for _, key := range []string{"html", "text"} {
fpath := app.config.Section("welcome_email").Key("email_" + key).String()
tpl, err := template.ParseFiles(fpath)
filesystem, fpath := app.GetPath("welcome_email", "email_"+key)
tpl, err := template.ParseFS(filesystem, fpath)
if err != nil {
return nil, err
}

45
go.sum
View File

@ -1,4 +1,6 @@
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
@ -9,30 +11,41 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/dgrijalva/jwt-go v1.0.2 h1:KPldsxuKGsS2FPWsNeg9ZO18aCrGKujPoWXn2yo+KQM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473 h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanw/esbuild v0.8.44 h1:9svHk3MxC3T8ThKkUJ71GcPXYGMhxhO5iCfg2hrU0PU=
github.com/evanw/esbuild v0.8.44/go.mod h1:y2AFBAGVelPqPodpdtxWWqe6n2jYf5FrsJbligmRmuw=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
github.com/gin-contrib/pprof v1.3.0 h1:G9eK6HnbkSqDZBYbzG4wrjCsA4e+cvYAHUZw6W+W9K0=
github.com/gin-contrib/pprof v1.3.0/go.mod h1:waMjT1H9b179t3CxuG1cV3DHpga6ybizwfBaM5OXaB0=
@ -102,6 +115,7 @@ github.com/go-openapi/swag v0.19.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuay
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/go-openapi/swag v0.19.13 h1:233UVgMy1DlmCYYfOiFpta6e2urloh+sEs5id6lyzog=
github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
@ -117,8 +131,11 @@ github.com/go-playground/validator/v10 v10.4.0 h1:72qIR/m8ybvL8L5TIyfgrigqkrw7kV
github.com/go-playground/validator/v10 v10.4.0/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/gofrs/uuid v3.3.0+incompatible h1:8K4tyRfvU1CYPgJsveYFQMhpFd/wXNM7iK6rR7UHz84=
github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -139,7 +156,9 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -164,10 +183,13 @@ github.com/knz/strtime v0.0.0-20200318182718-be999391ffa9 h1:GQE1iatYDRrIidq4Zf/
github.com/knz/strtime v0.0.0-20200318182718-be999391ffa9/go.mod h1:4ZxfWkxwtc7dBeifERVVWRy9F9rTU9p0yCDgeCtlius=
github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e h1:ViPE0JEOvtw5I0EGUiFSr2VNKGNU+3oBT+oHbDXHbxk=
github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e/go.mod h1:4ZxfWkxwtc7dBeifERVVWRy9F9rTU9p0yCDgeCtlius=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg=
github.com/labstack/echo v3.3.10+incompatible/go.mod h1:0INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s=
@ -218,6 +240,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLD
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pdrum/swagger-automation v0.0.0-20190629163613-c8c7c80ba858 h1:lgbJiJQx8bXo+eM88AFdd0VxUvaTLzCBXpK+H9poJ+Y=
github.com/pdrum/swagger-automation v0.0.0-20190629163613-c8c7c80ba858/go.mod h1:y02HeaN0visd95W6cEX2NXDv5sCwyqfzucWTdDGEwYY=
@ -225,20 +248,25 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14 h1:PyYN9JH5jY9j6av01SpfRMb+1DWg/i3MbGOKPxJ2wjM=
github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
@ -286,6 +314,7 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -297,10 +326,13 @@ golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rB
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -335,12 +367,14 @@ golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLD
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -374,6 +408,7 @@ golang.org/x/sys v0.0.0-20210123111255-9b0068b26619 h1:yLLDsUUPDliIQpKl7BjVb1igw
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
@ -422,14 +457,18 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -444,9 +483,13 @@ google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/go-playground/validator.v9 v9.29.1 h1:SvGtYmN60a5CVKTOzMSyfzWDeZRxRuGvRQyEAKbw1xc=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/ini.v1 v1.57.0 h1:9unxIsFcTt4I55uWluz+UmL95q4kdJ0buvQ1ZIqVQww=
gopkg.in/ini.v1 v1.57.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@ -467,6 +510,8 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

63
main.go
View File

@ -7,8 +7,9 @@ import (
"encoding/json"
"flag"
"fmt"
"html/template"
"io"
"io/ioutil"
"io/fs"
"log"
"mime"
"net"
@ -58,7 +59,9 @@ type appContext struct {
configBasePath string
configBase settings
dataPath string
localPath string
systemFS fs.FS
localFS fs.FS
webFS httpFS
cssClass string
jellyfinLogin bool
users []User
@ -82,8 +85,8 @@ type appContext struct {
func (app *appContext) loadHTML(router *gin.Engine) {
customPath := app.config.Section("files").Key("html_templates").MustString("")
templatePath := filepath.Join(app.localPath, "html")
htmlFiles, err := ioutil.ReadDir(templatePath)
templatePath := "html"
htmlFiles, err := fs.ReadDir(app.localFS, templatePath)
if err != nil {
app.err.Fatalf("Couldn't access template directory: \"%s\"", templatePath)
return
@ -98,7 +101,11 @@ func (app *appContext) loadHTML(router *gin.Engine) {
loadFiles[i] = filepath.Join(filepath.Join(customPath, f.Name()))
}
}
router.LoadHTMLFiles(loadFiles...)
tmpl, err := template.ParseFS(app.localFS, loadFiles...)
if err != nil {
app.err.Fatalf("Failed to load templates: %v", err)
}
router.SetHTMLTemplate(tmpl)
}
func generateSecret(length int) (string, error) {
@ -194,7 +201,14 @@ func start(asDaemon, firstCall bool) {
app.dataPath = filepath.Join(userConfigDir, "jfa-go")
app.configPath = filepath.Join(app.dataPath, "config.ini")
executable, _ := os.Executable()
app.localPath = filepath.Join(filepath.Dir(executable), "data")
app.localFS = os.DirFS(filepath.Join(filepath.Dir(executable), "data"))
app.systemFS = os.DirFS("/")
wfs := os.DirFS(filepath.Join(filepath.Dir(executable), "data", "web"))
app.webFS = httpFS{
hfs: http.FS(wfs),
fs: wfs,
}
app.webFS.fs = wfs
app.info = log.New(os.Stdout, "[INFO] ", log.Ltime)
app.err = log.New(os.Stdout, "[ERROR] ", log.Ltime|log.Lshortfile)
@ -251,23 +265,19 @@ func start(asDaemon, firstCall bool) {
}
if _, err := os.Stat(app.configPath); os.IsNotExist(err) {
firstRun = true
dConfigPath := filepath.Join(app.localPath, "config-default.ini")
var dConfig *os.File
dConfig, err = os.Open(dConfigPath)
dConfig, err := fs.ReadFile(app.localFS, "config-default.ini")
if err != nil {
app.err.Fatalf("Couldn't find default config file \"%s\"", dConfigPath)
app.err.Fatalf("Couldn't find default config file")
}
defer dConfig.Close()
var nConfig *os.File
nConfig, err := os.Create(app.configPath)
if err != nil {
app.err.Printf("Couldn't open config file for writing: \"%s\"", app.configPath)
app.err.Fatalf("Error: %s", err)
}
defer nConfig.Close()
_, err = io.Copy(nConfig, dConfig)
_, err = nConfig.Write(dConfig)
if err != nil {
app.err.Fatalf("Couldn't copy default config. To do this manually, copy\n%s\nto\n%s", dConfigPath, app.configPath)
app.err.Fatalf("Couldn't copy default config.")
}
app.info.Printf("Copied default configuration to \"%s\"", app.configPath)
}
@ -288,7 +298,7 @@ func start(asDaemon, firstCall bool) {
app.info.Print(aurora.Magenta("\n\nWARNING: Don't use debug mode in production, as it exposes pprof on the network.\n\n"))
app.debug = log.New(os.Stdout, "[DEBUG] ", log.Ltime|log.Lshortfile)
} else {
app.debug = log.New(ioutil.Discard, "", 0)
app.debug = log.New(io.Discard, "", 0)
}
if asDaemon {
@ -330,10 +340,10 @@ func start(asDaemon, firstCall bool) {
}()
}
app.storage.lang.CommonPath = filepath.Join(app.localPath, "lang", "common")
app.storage.lang.FormPath = filepath.Join(app.localPath, "lang", "form")
app.storage.lang.AdminPath = filepath.Join(app.localPath, "lang", "admin")
app.storage.lang.EmailPath = filepath.Join(app.localPath, "lang", "email")
app.storage.lang.CommonPath = filepath.Join("lang", "common")
app.storage.lang.FormPath = filepath.Join("lang", "form")
app.storage.lang.AdminPath = filepath.Join("lang", "admin")
app.storage.lang.EmailPath = filepath.Join("lang", "email")
err := app.storage.loadLang()
if err != nil {
app.info.Fatalf("Failed to load language files: %+v\n", err)
@ -413,8 +423,8 @@ func start(asDaemon, firstCall bool) {
}
app.configBasePath = filepath.Join(app.localPath, "config-base.json")
configBase, _ := ioutil.ReadFile(app.configBasePath)
app.configBasePath = "config-base.json"
configBase, _ := fs.ReadFile(app.localFS, app.configBasePath)
json.Unmarshal(configBase, &app.configBase)
themes := map[string]string{
@ -562,7 +572,7 @@ func start(asDaemon, firstCall bool) {
} else {
debugMode = false
address = "0.0.0.0:8056"
app.storage.lang.SetupPath = filepath.Join(app.localPath, "lang", "setup")
app.storage.lang.SetupPath = filepath.Join("lang", "setup")
err := app.storage.loadLangSetup()
if err != nil {
app.info.Fatalf("Failed to load language files: %+v\n", err)
@ -583,14 +593,17 @@ func start(asDaemon, firstCall bool) {
setGinLogger(router, debugMode)
router.Use(gin.Recovery())
// Move to router.go
routePrefixes := []string{app.URLBase}
if app.URLBase != "" {
routePrefixes = append(routePrefixes, "")
}
for _, p := range routePrefixes {
router.Use(static.Serve(p+"/", static.LocalFile(filepath.Join(app.localPath, "web"), false)))
router.Use(static.Serve(p+"/", app.webFS))
}
//
app.loadHTML(router)
router.Use(static.Serve("/", app.webFS))
router.NoRoute(app.NoRouteHandler)
if debugMode {
app.debug.Println("Loading pprof")
@ -600,6 +613,7 @@ func start(asDaemon, firstCall bool) {
router.GET(p+"/lang/:page", app.GetLanguages)
}
if !firstRun {
// Move to router
for _, p := range routePrefixes {
router.GET(p+"/", app.AdminPage)
router.GET(p+"/accounts", app.AdminPage)
@ -608,7 +622,7 @@ func start(asDaemon, firstCall bool) {
router.GET(p+"/token/login", app.getTokenLogin)
router.GET(p+"/token/refresh", app.getTokenRefresh)
router.POST(p+"/newUser", app.NewUser)
router.Use(static.Serve(p+"/invite/", static.LocalFile(filepath.Join(app.localPath, "web"), false)))
router.Use(static.Serve(p+"/invite/", app.webFS))
router.GET(p+"/invite/:invCode", app.InviteProxy)
}
if *SWAGGER {
@ -650,7 +664,6 @@ func start(asDaemon, firstCall bool) {
router.POST("/config", app.ModifyConfig)
app.info.Printf("Loading setup @ %s", address)
}
SRV = &http.Server{
Addr: address,
Handler: router,

View File

@ -2,7 +2,6 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"strings"
"time"
@ -53,7 +52,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
}
if event.Op&fsnotify.Write == fsnotify.Write && strings.Contains(event.Name, "passwordreset") {
var pwr PasswordReset
data, err := ioutil.ReadFile(event.Name)
data, err := os.ReadFile(event.Name)
if err != nil {
return
}

View File

@ -2,7 +2,7 @@ package main
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -76,7 +76,7 @@ func (st *Storage) loadLangSetup() error {
load := func(fname string) error {
index := strings.TrimSuffix(fname, filepath.Ext(fname))
lang := setupLang{}
f, err := ioutil.ReadFile(filepath.Join(st.lang.SetupPath, fname))
f, err := os.ReadFile(filepath.Join(st.lang.SetupPath, fname))
if err != nil {
return err
}
@ -112,7 +112,7 @@ func (st *Storage) loadLangSetup() error {
return err
}
english = st.lang.Setup["en-us"]
files, err := ioutil.ReadDir(st.lang.SetupPath)
files, err := os.ReadDir(st.lang.SetupPath)
if err != nil {
return err
}

30
static.go Normal file
View File

@ -0,0 +1,30 @@
package main
import (
"io/fs"
"net/http"
"strings"
)
type httpFS struct {
hfs http.FileSystem
fs fs.FS
}
func (f httpFS) Open(name string) (http.File, error) {
return f.hfs.Open(name)
}
func (f httpFS) Exists(prefix string, filepath string) bool {
if p := strings.TrimPrefix(filepath, prefix); len(p) < len(filepath) {
stats, err := fs.Stat(f.fs, p)
if err != nil {
return false
}
if stats.IsDir() {
return false
}
return true
}
return false
}

View File

@ -2,8 +2,8 @@ package main
import (
"encoding/json"
"io/ioutil"
"log"
"os"
"path/filepath"
"strconv"
"strings"
@ -42,7 +42,7 @@ type Invite struct {
Notify map[string]map[string]bool `json:"notify"`
Profile string `json:"profile"`
Label string `json:"label,omitempty"`
Keys []string `json"keys,omitempty"`
Keys []string `json"keys,omitempty"`
}
type Lang struct {
@ -126,7 +126,7 @@ func (st *Storage) loadLangCommon() error {
load := func(fname string) error {
index := strings.TrimSuffix(fname, filepath.Ext(fname))
lang := commonLang{}
f, err := ioutil.ReadFile(filepath.Join(st.lang.CommonPath, fname))
f, err := os.ReadFile(filepath.Join(st.lang.CommonPath, fname))
if err != nil {
return err
}
@ -148,7 +148,7 @@ func (st *Storage) loadLangCommon() error {
return err
}
english = st.lang.Common["en-us"]
files, err := ioutil.ReadDir(st.lang.CommonPath)
files, err := os.ReadDir(st.lang.CommonPath)
if err != nil {
return err
}
@ -169,7 +169,7 @@ func (st *Storage) loadLangAdmin() error {
load := func(fname string) error {
index := strings.TrimSuffix(fname, filepath.Ext(fname))
lang := adminLang{}
f, err := ioutil.ReadFile(filepath.Join(st.lang.AdminPath, fname))
f, err := os.ReadFile(filepath.Join(st.lang.AdminPath, fname))
if err != nil {
return err
}
@ -199,7 +199,7 @@ func (st *Storage) loadLangAdmin() error {
return err
}
english = st.lang.Admin["en-us"]
files, err := ioutil.ReadDir(st.lang.AdminPath)
files, err := os.ReadDir(st.lang.AdminPath)
if err != nil {
return err
}
@ -220,7 +220,7 @@ func (st *Storage) loadLangForm() error {
load := func(fname string) error {
index := strings.TrimSuffix(fname, filepath.Ext(fname))
lang := formLang{}
f, err := ioutil.ReadFile(filepath.Join(st.lang.FormPath, fname))
f, err := os.ReadFile(filepath.Join(st.lang.FormPath, fname))
if err != nil {
return err
}
@ -255,7 +255,7 @@ func (st *Storage) loadLangForm() error {
return err
}
english = st.lang.Form["en-us"]
files, err := ioutil.ReadDir(st.lang.FormPath)
files, err := os.ReadDir(st.lang.FormPath)
if err != nil {
return err
}
@ -276,7 +276,7 @@ func (st *Storage) loadLangEmail() error {
load := func(fname string) error {
index := strings.TrimSuffix(fname, filepath.Ext(fname))
lang := emailLang{}
f, err := ioutil.ReadFile(filepath.Join(st.lang.EmailPath, fname))
f, err := os.ReadFile(filepath.Join(st.lang.EmailPath, fname))
if err != nil {
return err
}
@ -304,7 +304,7 @@ func (st *Storage) loadLangEmail() error {
return err
}
english = st.lang.Email["en-us"]
files, err := ioutil.ReadDir(st.lang.EmailPath)
files, err := os.ReadDir(st.lang.EmailPath)
if err != nil {
return err
}
@ -329,76 +329,6 @@ func (st *Storage) storeInvites() error {
return storeJSON(st.invite_path, st.invites)
}
// func (st *Storage) loadLang() error {
// loadData := func(path string, stringJson bool) (map[string]string, map[string]map[string]interface{}, error) {
// files, err := ioutil.ReadDir(path)
// outString := map[string]string{}
// out := map[string]map[string]interface{}{}
// if err != nil {
// return nil, nil, err
// }
// for _, f := range files {
// index := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
// var data map[string]interface{}
// var file []byte
// var err error
// file, err = ioutil.ReadFile(filepath.Join(path, f.Name()))
// if err != nil {
// file = []byte("{}")
// }
// // Replace Jellyfin with something if necessary
// if substituteStrings != "" {
// fileString := strings.ReplaceAll(string(file), "Jellyfin", substituteStrings)
// file = []byte(fileString)
// }
// err = json.Unmarshal(file, &data)
// if err != nil {
// log.Printf("ERROR: Failed to read \"%s\": %s", path, err)
// return nil, nil, err
// }
// if stringJson {
// stringJSON, err := json.Marshal(data)
// if err != nil {
// return nil, nil, err
// }
// outString[index] = string(stringJSON)
// }
// out[index] = data
//
// }
// return outString, out, nil
// }
// _, form, err := loadData(st.lang.FormPath, false)
// if err != nil {
// return err
// }
// for index, lang := range form {
// validationStrings := lang["validationStrings"].(map[string]interface{})
// vS, err := json.Marshal(validationStrings)
// if err != nil {
// return err
// }
// lang["validationStrings"] = string(vS)
// form[index] = lang
// }
// st.lang.Form = form
// adminJSON, admin, err := loadData(st.lang.AdminPath, true)
// st.lang.Admin = admin
// st.lang.AdminJSON = adminJSON
//
// _, emails, err := loadData(st.lang.EmailPath, false)
// fixedEmails := map[string]map[string]map[string]interface{}{}
// for lang, e := range emails {
// f := map[string]map[string]interface{}{}
// for field, vals := range e {
// f[field] = vals.(map[string]interface{})
// }
// fixedEmails[lang] = f
// }
// st.lang.Email = fixedEmails
// return err
// }
func (st *Storage) loadEmails() error {
return loadJSON(st.emails_path, &st.emails)
}
@ -495,7 +425,7 @@ func (st *Storage) migrateToProfile() error {
func loadJSON(path string, obj interface{}) error {
var file []byte
var err error
file, err = ioutil.ReadFile(path)
file, err = os.ReadFile(path)
if err != nil {
file = []byte("{}")
}
@ -511,7 +441,7 @@ func storeJSON(path string, obj interface{}) error {
if err != nil {
return err
}
err = ioutil.WriteFile(path, data, 0644)
err = os.WriteFile(path, data, 0644)
if err != nil {
log.Printf("ERROR: Failed to write to \"%s\": %s", path, err)
}