From 5a2d3d2ee284545060f7603b018bc0895e3ffa94 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 2 Oct 2023 09:40:19 +0100 Subject: [PATCH 01/19] admin: My Account button respects URL Base --- html/admin.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/admin.html b/html/admin.html index 1919cb3..4cd41d7 100644 --- a/html/admin.html +++ b/html/admin.html @@ -465,7 +465,7 @@ {{ if .userPageEnabled }}
- {{ .strings.myAccount }} + {{ .strings.myAccount }}
{{ end }}
From ff1ea8549a9740dcf16da2699eb2791710682eca Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 2 Oct 2023 09:45:38 +0100 Subject: [PATCH 02/19] userpage: register routes on reverse proxy subfolder fixes #289. --- router.go | 39 +++++++++++++++++++-------------------- ts/user.ts | 1 - 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/router.go b/router.go index e2426cc..875ad20 100644 --- a/router.go +++ b/router.go @@ -160,12 +160,11 @@ func (app *appContext) loadRoutes(router *gin.Engine) { api := router.Group("/", app.webAuth()) - var user *gin.RouterGroup - if userPageEnabled { - user = router.Group("/my", app.userAuth()) - } - for _, p := range routePrefixes { + var user *gin.RouterGroup + if userPageEnabled { + user = router.Group(p+"/my", app.userAuth()) + } router.POST(p+"/logout", app.Logout) api.DELETE(p+"/users", app.DeleteUsers) api.GET(p+"/users", app.GetUsers) @@ -234,22 +233,22 @@ func (app *appContext) loadRoutes(router *gin.Engine) { } if userPageEnabled { - user.GET(p+"/details", app.MyDetails) - user.POST(p+"/contact", app.SetMyContactMethods) - user.POST(p+"/logout", app.LogoutUser) - user.POST(p+"/email", app.ModifyMyEmail) - user.GET(p+"/discord/invite", app.MyDiscordServerInvite) - user.GET(p+"/pin/:service", app.GetMyPIN) - user.GET(p+"/discord/verified/:pin", app.MyDiscordVerifiedInvite) - user.GET(p+"/telegram/verified/:pin", app.MyTelegramVerifiedInvite) - user.POST(p+"/matrix/user", app.MatrixSendMyPIN) - user.GET(p+"/matrix/verified/:userID/:pin", app.MatrixCheckMyPIN) - user.DELETE(p+"/discord", app.UnlinkMyDiscord) - user.DELETE(p+"/telegram", app.UnlinkMyTelegram) - user.DELETE(p+"/matrix", app.UnlinkMyMatrix) - user.POST(p+"/password", app.ChangeMyPassword) + user.GET("/details", app.MyDetails) + user.POST("/contact", app.SetMyContactMethods) + user.POST("/logout", app.LogoutUser) + user.POST("/email", app.ModifyMyEmail) + user.GET("/discord/invite", app.MyDiscordServerInvite) + user.GET("/pin/:service", app.GetMyPIN) + user.GET("/discord/verified/:pin", app.MyDiscordVerifiedInvite) + user.GET("/telegram/verified/:pin", app.MyTelegramVerifiedInvite) + user.POST("/matrix/user", app.MatrixSendMyPIN) + user.GET("/matrix/verified/:userID/:pin", app.MatrixCheckMyPIN) + user.DELETE("/discord", app.UnlinkMyDiscord) + user.DELETE("/telegram", app.UnlinkMyTelegram) + user.DELETE("/matrix", app.UnlinkMyMatrix) + user.POST("/password", app.ChangeMyPassword) if app.config.Section("user_page").Key("referrals").MustBool(false) { - user.GET(p+"/referral", app.GetMyReferral) + user.GET("/referral", app.GetMyReferral) } } } diff --git a/ts/user.ts b/ts/user.ts index d11dfeb..00b105c 100644 --- a/ts/user.ts +++ b/ts/user.ts @@ -556,7 +556,6 @@ changePasswordButton.addEventListener("click", () => { } }); }); -// FIXME: Submit & Validate document.addEventListener("details-reload", () => { _get("/my/details", null, (req: XMLHttpRequest) => { From 9956bbd974149db9d515a2737743ff1fe50c2839 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 2 Oct 2023 10:34:07 +0100 Subject: [PATCH 03/19] admin: add setting to hide background on login for #288. --- config/config-base.json | 12 ++++++++++++ css/base.css | 7 +++++-- css/modal.css | 18 ++++++++++++++++++ html/admin.html | 1 + ts/admin.ts | 2 +- ts/modules/login.ts | 11 ++++++++++- ts/typings/d.ts | 1 + ts/user.ts | 2 +- views.go | 1 + 9 files changed, 50 insertions(+), 5 deletions(-) diff --git a/config/config-base.json b/config/config-base.json index 01c4852..c0c59ff 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -274,6 +274,18 @@ "value": false, "advanced": true, "description": "Navigate directly to the above URL instead of needing the user to click \"Continue\"." + }, + "login_appearance": { + "name": "Login screen appearance", + "required": false, + "requires_restart": false, + "type": "select", + "options": [ + ["clear", "Transparent"], + ["opaque", "Opaque"] + ], + "value": "clear", + "description": "Appearance of the Admin login screen." } } }, diff --git a/css/base.css b/css/base.css index 2c60e94..9519951 100644 --- a/css/base.css +++ b/css/base.css @@ -15,6 +15,9 @@ --border-width-4: 5px; --border-width-8: 8px; font-family: 'Hanken Grotesk', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + + --bg-light: #fff; + --bg-dark: #101010; } .light { @@ -26,11 +29,11 @@ } .dark body { - background-color: #101010; + background-color: var(--bg-dark); } html:not(.dark) body { - background-color: #fff; + background-color: var(--bg-light); } .dark select, .dark option, .dark input { diff --git a/css/modal.css b/css/modal.css index ef6536e..11a0e27 100644 --- a/css/modal.css +++ b/css/modal.css @@ -10,6 +10,24 @@ background-color: rgba(0,0,0,40%); } +.wall { + position: fixed; + z-index: 11; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: auto; +} + +html.dark .wall { + background-color: var(--bg-dark); +} + +html:not(.dark) .wall { + background-color: var(--bg-light); +} + .modal-close { float: right; color: #aaa; diff --git a/html/admin.html b/html/admin.html index 4cd41d7..3c7ae1b 100644 --- a/html/admin.html +++ b/html/admin.html @@ -18,6 +18,7 @@ window.jfAdminOnly = {{ .jfAdminOnly }}; window.jfAllowAll = {{ .jfAllowAll }}; window.referralsEnabled = {{ .referralsEnabled }}; + window.loginAppearance = "{{ .loginAppearance }}"; Admin - jfa-go {{ template "header.html" . }} diff --git a/ts/admin.ts b/ts/admin.ts index f29fe2b..2fe99c7 100644 --- a/ts/admin.ts +++ b/ts/admin.ts @@ -163,7 +163,7 @@ window.onpopstate = (event: PopStateEvent) => { window.tabs.switch(event.state); } -const login = new Login(window.modals.login as Modal, "/"); +const login = new Login(window.modals.login as Modal, "/", window.loginAppearance); login.onLogin = () => { console.log("Logged in."); window.updater = new Updater(); diff --git a/ts/modules/login.ts b/ts/modules/login.ts index 1527e10..12e6aba 100644 --- a/ts/modules/login.ts +++ b/ts/modules/login.ts @@ -8,13 +8,21 @@ export class Login { private _endpoint: string; private _onLogin: (username: string, password: string) => void; private _logoutButton: HTMLElement = null; + private _wall: HTMLElement; + private _hasOpacityWall: boolean = false; - constructor(modal: Modal, endpoint: string) { + constructor(modal: Modal, endpoint: string, appearance: string) { this._endpoint = endpoint; this._url = window.URLBase + endpoint; if (this._url[this._url.length-1] != '/') this._url += "/"; this._modal = modal; + if (appearance == "opaque") { + this._hasOpacityWall = true; + this._wall = document.createElement("div"); + this._wall.classList.add("wall"); + this._modal.asElement().parentElement.appendChild(this._wall); + } this._form = this._modal.asElement().querySelector(".form-login") as HTMLFormElement; this._form.onsubmit = (event: SubmitEvent) => { event.preventDefault(); @@ -86,6 +94,7 @@ export class Login { if (this._onLogin) { this._onLogin(username, password); } + if (this._hasOpacityWall) this._wall.remove(); this._modal.close(); if (this._logoutButton != null) this._logoutButton.classList.remove("unfocused"); diff --git a/ts/typings/d.ts b/ts/typings/d.ts index 9aed65d..8d97410 100644 --- a/ts/typings/d.ts +++ b/ts/typings/d.ts @@ -41,6 +41,7 @@ declare interface Window { jfAdminOnly: boolean; jfAllowAll: boolean; referralsEnabled: boolean; + loginAppearance: string; } declare interface Update { diff --git a/ts/user.ts b/ts/user.ts index 00b105c..f8e4e2b 100644 --- a/ts/user.ts +++ b/ts/user.ts @@ -644,7 +644,7 @@ document.addEventListener("details-reload", () => { }); }); -const login = new Login(window.modals.login as Modal, "/my/"); +const login = new Login(window.modals.login as Modal, "/my/", "opaque"); login.onLogin = () => { console.log("Logged in."); document.querySelector(".page-container").classList.remove("unfocused"); diff --git a/views.go b/views.go index f1f7fdd..f388c35 100644 --- a/views.go +++ b/views.go @@ -176,6 +176,7 @@ func (app *appContext) AdminPage(gc *gin.Context) { "jfAllowAll": jfAllowAll, "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false), + "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"), }) } From 4f78b7c33b45970ef597e28a0709c7a65a9181d0 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 2 Oct 2023 10:56:50 +0100 Subject: [PATCH 04/19] admin: option link to my account page on login screen --- config/config-base.json | 8 ++++++++ html/login-modal.html | 8 ++++++++ lang/admin/en-us.json | 3 ++- views.go | 1 + 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/config/config-base.json b/config/config-base.json index c0c59ff..b12d081 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -418,6 +418,14 @@ "required": "false", "description": "Click the edit icon next to the \"User Page\" Setting to add custom Markdown messages that will be shown to the user. Note message cards are not private, little effort is required for anyone to view them." }, + "show_link": { + "name": "Show Link on Admin Login page", + "required": false, + "requires_restart": false, + "type": "bool", + "value": true, + "description": "Whether or not to show a link to the \"My Account\" page on the admin login screen, to direct lost users." + }, "referrals": { "name": "User Referrals", "required": false, diff --git a/html/login-modal.html b/html/login-modal.html index 2f9c3fb..e3c4a5d 100644 --- a/html/login-modal.html +++ b/html/login-modal.html @@ -7,6 +7,14 @@
{{ end }} {{ end }} + {{ if index . "userPageEnabled" }} + {{ if and .userPageEnabled .showUserPageLink }} +
+ {{ .strings.loginNotAdmin }} + {{ .strings.myAccount }} +
+ {{ end }} + {{ end }}
{{ .strings.login }} diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json index 9a3bd22..e22efe8 100644 --- a/lang/admin/en-us.json +++ b/lang/admin/en-us.json @@ -123,7 +123,8 @@ "userPageLogin": "User Page: Login", "userPagePage": "User Page: Page", "buildTime": "Build Time", - "builtBy": "Built By" + "builtBy": "Built By", + "loginNotAdmin": "Not an Admin?" }, "notifications": { "changedEmailAddress": "Changed email address of {n}.", diff --git a/views.go b/views.go index f388c35..7e56900 100644 --- a/views.go +++ b/views.go @@ -175,6 +175,7 @@ func (app *appContext) AdminPage(gc *gin.Context) { "jfAdminOnly": jfAdminOnly, "jfAllowAll": jfAllowAll, "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), + "showUserPageLink": app.config.Section("user_page").Key("show_link").MustBool(true), "referralsEnabled": app.config.Section("user_page").Key("enabled").MustBool(false) && app.config.Section("user_page").Key("referrals").MustBool(false), "loginAppearance": app.config.Section("ui").Key("login_appearance").MustString("clear"), }) From f6fdd41b35ec30b56f79690a288eff9575f8fa07 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 3 Oct 2023 09:33:56 +0100 Subject: [PATCH 05/19] jellyfin: retry initial connection (configurable) retries initial connection to Jellyfin 6 times, with a 10s gap between, before failing. SHould help with issues of jfa-go starting before Jellyfin. Configurable in Settings > Advanced > "Initial auth retry count/gap". --- config/config-base.json | 19 ++++++++++++++++- go.mod | 2 +- go.sum | 46 ++++++----------------------------------- main.go | 7 ++++++- 4 files changed, 31 insertions(+), 43 deletions(-) diff --git a/config/config-base.json b/config/config-base.json index b12d081..93cea87 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -293,7 +293,8 @@ "order": [], "meta": { "name": "Advanced", - "description": "Advanced settings." + "description": "Advanced settings.", + "advanced": true }, "settings": { "tls": { @@ -330,6 +331,22 @@ "type": "text", "value": "", "description": "Path to .key file. See jfa-go wiki for more info." + }, + "auth_retry_count": { + "name": "Initial auth retry count", + "required": false, + "requires_restart": true, + "type": "number", + "value": 6, + "description": "Number of times to retry initial connection to Jellyfin before failing." + }, + "auth_retry_gap": { + "name": "Initial auth retry gap (seconds)", + "required": false, + "requires_restart": true, + "type": "number", + "value": 10, + "description": "Duration in seconds to wait between connection retries." } } }, diff --git a/go.mod b/go.mod index 2a228e1..db66fcc 100644 --- a/go.mod +++ b/go.mod @@ -31,7 +31,7 @@ require ( github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/logger v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/ombi v0.0.0-20230626224816-f72960635dc3 - github.com/hrfee/mediabrowser v0.3.10 + github.com/hrfee/mediabrowser v0.3.11 github.com/itchyny/timefmt-go v0.1.5 github.com/lithammer/shortuuid/v3 v3.0.7 github.com/mailgun/mailgun-go/v4 v4.9.1 diff --git a/go.sum b/go.sum index 9192581..3e3a432 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= 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.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= @@ -14,14 +15,11 @@ github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZx github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= -github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM= github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -40,16 +38,14 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 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/dgraph-io/badger/v3 v3.2103.1 h1:zaX53IRg7ycxVlkd5pYdCeFp1FynD6qBGQoQql3R3Hk= github.com/dgraph-io/badger/v3 v3.2103.1/go.mod h1:dULbq6ehJ5K0cGW/1TQ9iSfUk0gbSiToDWmWmTsJ53E= github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg= github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug= github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -174,7 +170,6 @@ github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v0.0.0-20210429001901-424d2337a529 h1:2voWjNECnrZRbfwXxHB1/j8wa6xdKn85B5NzgVL/pTU= github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.1.1 h1:jxpi2eWoU84wbX9iIEyAeeoac3FLuifZpY9tcNUD9kw= github.com/golang/glog v1.1.1/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= @@ -195,7 +190,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= @@ -206,7 +200,6 @@ github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a h1:AWZzzFrqyjY github.com/gomarkdown/markdown v0.0.0-20230322041520-c84983bdbf2a/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= -github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI= github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/flatbuffers v23.5.26+incompatible h1:M9dgRyhJemaM4Sw8+66GHBu8ioaQmyPLg1b8VwK5WJg= github.com/google/flatbuffers v23.5.26+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= @@ -231,12 +224,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hrfee/mediabrowser v0.3.8 h1:y0iBCb6jE3QKcsiCJSYva2fFPHRn4UA+sGRzoPuJ/Dk= -github.com/hrfee/mediabrowser v0.3.8/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= -github.com/hrfee/mediabrowser v0.3.9 h1:ecBUd7LMjQrh+9SFRen2T2DzQqI7W8J7vV2lGExD0YU= -github.com/hrfee/mediabrowser v0.3.9/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= -github.com/hrfee/mediabrowser v0.3.10 h1:MUrgZQVY3mk76Bhn7PsZ4LFRhtGitkZA4FP+1qg1HFo= -github.com/hrfee/mediabrowser v0.3.10/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/hrfee/mediabrowser v0.3.11 h1:k7sVOwYT6kUv7+lEY+vcS3bNncCycLrW/YB2nUX625E= +github.com/hrfee/mediabrowser v0.3.11/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= @@ -251,7 +240,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= -github.com/klauspost/compress v1.13.1 h1:wXr2uRxZTJXHLly6qhJabee5JqIhTRoLBhDOA74hDEQ= github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.16.6 h1:91SKEy4K37vkp255cJ8QesJhjyRO0hn9i9G0GoUwLsk= github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= @@ -276,8 +264,6 @@ github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaK github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailgun/mailgun-go/v4 v4.9.0 h1:wRbxvVQ5QObFewLxc1uVvipA16D8gxeiO+cBOca51Iw= -github.com/mailgun/mailgun-go/v4 v4.9.0/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40= github.com/mailgun/mailgun-go/v4 v4.9.1 h1:D/jhJXYod4RqRsNOOSrjrtAcMEnz8mPYJmeA5cueHKY= github.com/mailgun/mailgun-go/v4 v4.9.1/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -298,7 +284,6 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -337,6 +322,7 @@ github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdh github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -359,7 +345,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= @@ -384,6 +369,7 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/timshannon/badgerhold/v3 v3.0.0-20210909134927-2b6764d68c1e h1:zWSVsQaifg0cVH9VvR+cMguV7exK6U+SoW8YD1cZpR4= github.com/timshannon/badgerhold/v3 v3.0.0-20210909134927-2b6764d68c1e/go.mod h1:/Seq5xGNo8jLhSbDX3jdbeZrp4yFIpQ6/7n4TjziEWs= github.com/timshannon/badgerhold/v4 v4.0.2 h1:83OLY/NFnEaMnHEPd84bYtkLipVkjTsMbzQRYbk47g4= github.com/timshannon/badgerhold/v4 v4.0.2/go.mod h1:rh6RyXLQFsvrvcKondPQQFZnNovpRzu+gS0FlLxYuHY= @@ -414,7 +400,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= @@ -448,18 +433,12 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/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.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= -golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= -golang.org/x/image v0.7.0 h1:gzS29xtG1J5ybQlv0PuyfE3nmc6R4qB73m6LUUmvFuw= -golang.org/x/image v0.7.0/go.mod h1:nd/q4ef1AKKYl/4kft7g+6UyGbdiqWqTP1ZAbRoV7Rg= golang.org/x/image v0.8.0 h1:agUcRXV/+w6L9ryntYYsF2x9fQTMd4T8fiiYXAVW6Jg= golang.org/x/image v0.8.0/go.mod h1:PwLxp3opCYg4WR2WO9P0L6ESnsD6bLTWcw8zanLMVFM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -471,7 +450,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= 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= @@ -494,8 +472,6 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.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.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -539,8 +515,6 @@ golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -552,8 +526,6 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -570,8 +542,6 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.10.0 h1:tvDr/iQoUqNdohiYm0LmmKcBk+q86lb9EprIUFhHHGg= golang.org/x/tools v0.10.0/go.mod h1:UJwyiVBsOA2uwvK/e5OY3GTpDUJriEd+/YlqAwLPmyM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -603,8 +573,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E= @@ -633,8 +601,6 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8= maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho= -maunium.net/go/mautrix v0.15.2 h1:fUiVajeoOR92uJoSShHbCvh7uG6lDY4ZO4Mvt90LbjU= -maunium.net/go/mautrix v0.15.2/go.mod h1:h4NwfKqE4YxGTLSgn/gawKzXAb2sF4qx8agL6QEFtGg= maunium.net/go/mautrix v0.15.3 h1:C9BHSUM0gYbuZmAtopuLjIcH5XHLb/ZjTEz7nN+0jN0= maunium.net/go/mautrix v0.15.3/go.mod h1:zLrQqdxJlLkurRCozTc9CL6FySkgZlO/kpCYxBILSLE= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go index bfc8e8f..5af0818 100644 --- a/main.go +++ b/main.go @@ -391,7 +391,12 @@ func start(asDaemon, firstCall bool) { app.jf.Verbose = true } var status int - _, status, err = app.jf.Authenticate(app.config.Section("jellyfin").Key("username").String(), app.config.Section("jellyfin").Key("password").String()) + retryOpts := mediabrowser.MustAuthenticateOptions{ + RetryCount: app.config.Section("advanced").Key("auth_retry_count").MustInt(6), + RetryGap: time.Duration(app.config.Section("advanced").Key("auth_retry_gap").MustInt(10)) * time.Second, + LogFailures: true, + } + _, status, err = app.jf.MustAuthenticate(app.config.Section("jellyfin").Key("username").String(), app.config.Section("jellyfin").Key("password").String(), retryOpts) if status != 200 || err != nil { app.err.Fatalf("Failed to authenticate with Jellyfin @ \"%s\" (%d): %v", server, status, err) } From 543f23c8ef9dd1165b20acec170b4d8fa5bf2f84 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 3 Oct 2023 09:44:05 +0100 Subject: [PATCH 06/19] userpage: make refresh token work w/ reverse proxy potentially for #290. --- user-auth.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/user-auth.go b/user-auth.go index 40ea057..53c9ae0 100644 --- a/user-auth.go +++ b/user-auth.go @@ -1,6 +1,10 @@ package main -import "github.com/gin-gonic/gin" +import ( + "strings" + + "github.com/gin-gonic/gin" +) func (app *appContext) userAuth() gin.HandlerFunc { return app.userAuthenticate @@ -60,7 +64,11 @@ func (app *appContext) getUserTokenLogin(gc *gin.Context) { } app.debug.Printf("Token generated for non-admin user \"%s\"", username) - gc.SetCookie("user-refresh", refresh, REFRESH_TOKEN_VALIDITY_SEC, "/my", gc.Request.URL.Hostname(), true, true) + uri := "/my" + if strings.HasPrefix(gc.Request.RequestURI, app.URLBase) { + uri = "/accounts/my" + } + gc.SetCookie("user-refresh", refresh, REFRESH_TOKEN_VALIDITY_SEC, uri, gc.Request.URL.Hostname(), true, true) gc.JSON(200, getTokenDTO{token}) } From 523902f951b27dd7c9d1be5a4b039acf48049ea6 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Thu, 5 Oct 2023 11:25:58 +0100 Subject: [PATCH 07/19] proxy: add http/socks5 support, use for Jellyfin can be found in advanced. Currently only used for the main Jellyfin client. --- .gitignore | 1 + config/config-base.json | 48 +++++++++++++++++++++++++++++++++++++++++ easyproxy/easyproxy.go | 42 ++++++++++++++++++++++++++++++++++++ easyproxy/go.mod | 5 +++++ easyproxy/go.sum | 2 ++ go.mod | 13 ++++++----- go.sum | 12 +++++++++-- main.go | 18 ++++++++++++++++ ombi/ombi.go | 4 ++-- 9 files changed, 136 insertions(+), 9 deletions(-) create mode 100644 easyproxy/easyproxy.go create mode 100644 easyproxy/go.mod create mode 100644 easyproxy/go.sum diff --git a/.gitignore b/.gitignore index 06a5ef1..0bd63e2 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ matacc.txt scripts/langmover/lang scripts/langmover/lang2 scripts/langmover/out +tinyproxy.conf diff --git a/config/config-base.json b/config/config-base.json index 93cea87..8001812 100644 --- a/config/config-base.json +++ b/config/config-base.json @@ -347,6 +347,54 @@ "type": "number", "value": 10, "description": "Duration in seconds to wait between connection retries." + }, + "proxy": { + "name": "Use Proxy", + "required": false, + "requires_restart": true, + "type": "bool", + "value": false, + "description": "Whether or not to use a HTTP/SOCKS5 Proxy." + }, + "proxy_protocol": { + "name": "Proxy Protocol", + "depends_true": "proxy", + "required": false, + "requires_restart": true, + "type": "select", + "options": [ + ["http", "HTTP"], + ["socks", "SOCKS5"] + ], + "value": "http", + "description": "Protocol to use for proxy connection." + }, + "proxy_address": { + "name": "Proxy Address", + "depends_true": "proxy", + "required": false, + "requires_restart": true, + "type": "text", + "value": "", + "description": "Proxy address, including port." + }, + "proxy_user": { + "name": "Proxy Username", + "depends_true": "proxy", + "required": false, + "requires_restart": true, + "type": "text", + "value": "", + "description": "Leave blank for no Authentication." + }, + "proxy_password": { + "name": "Proxy Password", + "depends_true": "proxy", + "required": false, + "requires_restart": true, + "type": "password", + "value": "", + "description": "Leave blank for no Authentication." } } }, diff --git a/easyproxy/easyproxy.go b/easyproxy/easyproxy.go new file mode 100644 index 0000000..b9c9bba --- /dev/null +++ b/easyproxy/easyproxy.go @@ -0,0 +1,42 @@ +// Package easyproxy provides a method to quickly create a http.Transport using given proxy details (SOCKS5 or HTTP). +package easyproxy + +import ( + "net/http" + "net/url" + + "golang.org/x/net/proxy" +) + +type Protocol int + +const ( + SOCKS5 Protocol = iota // SOCKS5 + HTTP // HTTP +) + +// NewTransport returns a http.Transport using the given proxy details. Leave user/pass blank if not needed. +func NewTransport(p Protocol, addr, user, pass string) (*http.Transport, error) { + t := &http.Transport{} + if p == HTTP { + u := &url.URL{ + Scheme: "http", + Host: addr, + } + if user != "" && pass != "" { + u.User = url.UserPassword(user, pass) + } + t.Proxy = http.ProxyURL(u) + return t, nil + } + var auth *proxy.Auth = nil + if user != "" && pass != "" { + auth = &proxy.Auth{User: user, Password: pass} + } + dialer, err := proxy.SOCKS5("tcp", addr, auth, proxy.Direct) + if err != nil { + return nil, nil + } + t.Dial = dialer.Dial + return t, nil +} diff --git a/easyproxy/go.mod b/easyproxy/go.mod new file mode 100644 index 0000000..d2c531f --- /dev/null +++ b/easyproxy/go.mod @@ -0,0 +1,5 @@ +module github.com/hrfee/jfa-go/easyproxy + +go 1.20 + +require golang.org/x/net v0.15.0 diff --git a/easyproxy/go.sum b/easyproxy/go.sum new file mode 100644 index 0000000..9746d02 --- /dev/null +++ b/easyproxy/go.sum @@ -0,0 +1,2 @@ +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= diff --git a/go.mod b/go.mod index db66fcc..c4650b4 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,8 @@ replace github.com/hrfee/jfa-go/linecache => ./linecache replace github.com/hrfee/jfa-go/api => ./api +replace github.com/hrfee/jfa-go/easyproxy => ./easyproxy + require ( github.com/bwmarrin/discordgo v0.27.1 github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a @@ -31,7 +33,7 @@ require ( github.com/hrfee/jfa-go/linecache v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/logger v0.0.0-20230626224816-f72960635dc3 github.com/hrfee/jfa-go/ombi v0.0.0-20230626224816-f72960635dc3 - github.com/hrfee/mediabrowser v0.3.11 + github.com/hrfee/mediabrowser v0.3.12 github.com/itchyny/timefmt-go v0.1.5 github.com/lithammer/shortuuid/v3 v3.0.7 github.com/mailgun/mailgun-go/v4 v4.9.1 @@ -85,6 +87,7 @@ require ( github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect + github.com/hrfee/jfa-go/easyproxy v0.0.0-00010101000000-000000000000 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/compress v1.16.6 // indirect @@ -116,12 +119,12 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.10.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/image v0.8.0 // indirect - golang.org/x/net v0.11.0 // indirect - golang.org/x/sys v0.9.0 // indirect - golang.org/x/text v0.10.0 // indirect + golang.org/x/net v0.15.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.10.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 3e3a432..85f8f14 100644 --- a/go.sum +++ b/go.sum @@ -224,8 +224,8 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hrfee/mediabrowser v0.3.11 h1:k7sVOwYT6kUv7+lEY+vcS3bNncCycLrW/YB2nUX625E= -github.com/hrfee/mediabrowser v0.3.11/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= +github.com/hrfee/mediabrowser v0.3.12 h1:fqDxt1be3e+ZNjAtlKc8MTqg7peo6fuGCrk2wOXo20k= +github.com/hrfee/mediabrowser v0.3.12/go.mod h1:PnHZbdxmbv1wCVdAQyM7nwPwpVj9fdKx2EcET7sAk+U= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/itchyny/timefmt-go v0.1.5 h1:G0INE2la8S6ru/ZI5JecgyzbbJNs5lG1RcBqa7Jm6GE= github.com/itchyny/timefmt-go v0.1.5/go.mod h1:nEP7L+2YmAbT2kZ2HfSs1d8Xtw9LY8D2stDBckWakZ8= @@ -435,6 +435,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= @@ -474,6 +476,8 @@ golang.org/x/net v0.6.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.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= 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= @@ -517,6 +521,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -528,6 +534,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= diff --git a/main.go b/main.go index 5af0818..45ed009 100644 --- a/main.go +++ b/main.go @@ -24,6 +24,7 @@ import ( "github.com/fatih/color" "github.com/hrfee/jfa-go/common" _ "github.com/hrfee/jfa-go/docs" + "github.com/hrfee/jfa-go/easyproxy" "github.com/hrfee/jfa-go/logger" "github.com/hrfee/jfa-go/ombi" "github.com/hrfee/mediabrowser" @@ -390,6 +391,23 @@ func start(asDaemon, firstCall bool) { if debugMode { app.jf.Verbose = true } + + if app.config.Section("advanced").Key("proxy").MustBool(false) { + // FIXME: Use Proxy + protocol := easyproxy.HTTP + if strings.Contains(app.config.Section("advanced").Key("proxy_protocol").MustString("http"), "socks") { + protocol = easyproxy.SOCKS5 + } + addr := app.config.Section("advanced").Key("proxy_address").MustString("") + user := app.config.Section("advanced").Key("proxy_user").MustString("") + password := app.config.Section("advanced").Key("proxy_password").MustString("") + transport, err := easyproxy.NewTransport(protocol, addr, user, password) + if err != nil { + app.err.Printf("Failed to initialize Proxy: %v\n", err) + } + app.jf.SetTransport(transport) + } + var status int retryOpts := mediabrowser.MustAuthenticateOptions{ RetryCount: app.config.Section("advanced").Key("auth_retry_count").MustInt(6), diff --git a/ombi/ombi.go b/ombi/ombi.go index 29a6e1c..38fa369 100644 --- a/ombi/ombi.go +++ b/ombi/ombi.go @@ -239,9 +239,9 @@ type NotificationPref struct { func (ombi *Ombi) SetNotificationPrefs(user map[string]interface{}, discordID, telegramUser string) (result string, code int, err error) { id := user["id"].(string) url := fmt.Sprintf("%s/api/v1/Identity/NotificationPreferences", ombi.server) - var data []NotificationPref + data := []NotificationPref{} if discordID != "" { - data = []NotificationPref{NotificationPref{NotifAgentDiscord, id, discordID, true}} + data = append(data, NotificationPref{NotifAgentDiscord, id, discordID, true}) } if telegramUser != "" { data = append(data, NotificationPref{NotifAgentTelegram, id, telegramUser, true}) From 5702e8012cebbf3c823834d85d66662652928a0a Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Thu, 5 Oct 2023 12:32:25 +0100 Subject: [PATCH 08/19] proxy: use for updater & SMTP imports a new package to create a HTTP proxy dialer for the SMTP client. --- config.go | 20 ++++++++++++++ easyproxy/easyproxy.go | 61 +++++++++++++++++++++++++++++++++++------- easyproxy/go.mod | 2 ++ easyproxy/go.sum | 2 ++ email.go | 16 +++++++++-- go.mod | 3 ++- go.sum | 4 +++ main.go | 19 ++++--------- updater.go | 5 ++++ 9 files changed, 105 insertions(+), 27 deletions(-) diff --git a/config.go b/config.go index 7053c12..c92bd63 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( "strconv" "strings" + "github.com/hrfee/jfa-go/easyproxy" "gopkg.in/ini.v1" ) @@ -135,6 +136,22 @@ func (app *appContext) loadConfig() error { messagesEnabled = false } + if app.proxyEnabled = app.config.Section("advanced").Key("proxy").MustBool(false); app.proxyEnabled { + app.proxyConfig = easyproxy.ProxyConfig{} + app.proxyConfig.Protocol = easyproxy.HTTP + if strings.Contains(app.config.Section("advanced").Key("proxy_protocol").MustString("http"), "socks") { + app.proxyConfig.Protocol = easyproxy.SOCKS5 + } + app.proxyConfig.Addr = app.config.Section("advanced").Key("proxy_address").MustString("") + app.proxyConfig.User = app.config.Section("advanced").Key("proxy_user").MustString("") + app.proxyConfig.Password = app.config.Section("advanced").Key("proxy_password").MustString("") + app.proxyTransport, err = easyproxy.NewTransport(app.proxyConfig) + if err != nil { + app.err.Printf("Failed to initialize Proxy: %v\n", err) + } + app.proxyEnabled = true + } + app.MustSetValue("updates", "enabled", "true") releaseChannel := app.config.Section("updates").Key("channel").String() if app.config.Section("updates").Key("enabled").MustBool(false) { @@ -147,6 +164,9 @@ func (app *appContext) loadConfig() error { v = "git" } app.updater = newUpdater(baseURL, namespace, repo, v, commit, updater) + if app.proxyEnabled { + app.updater.SetTransport(app.proxyTransport) + } } if releaseChannel == "" { if version == "git" { diff --git a/easyproxy/easyproxy.go b/easyproxy/easyproxy.go index b9c9bba..ef6557b 100644 --- a/easyproxy/easyproxy.go +++ b/easyproxy/easyproxy.go @@ -1,10 +1,12 @@ -// Package easyproxy provides a method to quickly create a http.Transport using given proxy details (SOCKS5 or HTTP). +// Package easyproxy provides a method to quickly create a http.Transport or net.Conn using given proxy details (SOCKS5 or HTTP). package easyproxy import ( + "crypto/tls" "net/http" "net/url" + "github.com/magisterquis/connectproxy" "golang.org/x/net/proxy" ) @@ -15,28 +17,67 @@ const ( HTTP // HTTP ) +type ProxyConfig struct { + Protocol Protocol + Addr string + User string + Password string +} + // NewTransport returns a http.Transport using the given proxy details. Leave user/pass blank if not needed. -func NewTransport(p Protocol, addr, user, pass string) (*http.Transport, error) { +func NewTransport(c ProxyConfig) (*http.Transport, error) { t := &http.Transport{} - if p == HTTP { + if c.Protocol == HTTP { u := &url.URL{ Scheme: "http", - Host: addr, + Host: c.Addr, } - if user != "" && pass != "" { - u.User = url.UserPassword(user, pass) + if c.User != "" && c.Password != "" { + u.User = url.UserPassword(c.User, c.Password) } t.Proxy = http.ProxyURL(u) return t, nil } var auth *proxy.Auth = nil - if user != "" && pass != "" { - auth = &proxy.Auth{User: user, Password: pass} + if c.User != "" && c.Password != "" { + auth = &proxy.Auth{User: c.User, Password: c.Password} } - dialer, err := proxy.SOCKS5("tcp", addr, auth, proxy.Direct) + dialer, err := proxy.SOCKS5("tcp", c.Addr, auth, proxy.Direct) if err != nil { - return nil, nil + return nil, err } t.Dial = dialer.Dial return t, nil } + +// NewConn returns a tls.Conn to "addr" using the given proxy details. Leave user/pass blank if not needed. +func NewConn(c ProxyConfig, addr string, tlsConf *tls.Config) (*tls.Conn, error) { + var proxyDialer proxy.Dialer + var err error + if c.Protocol == SOCKS5 { + var auth *proxy.Auth = nil + if c.User != "" && c.Password != "" { + auth = &proxy.Auth{User: c.User, Password: c.Password} + } + proxyDialer, err = proxy.SOCKS5("tcp", c.Addr, auth, proxy.Direct) + if err != nil { + return nil, err + } + } else { + u := &url.URL{ + Scheme: "http", + Host: c.Addr, + } + if c.User != "" && c.Password != "" { + u.User = url.UserPassword(c.User, c.Password) + } + proxyDialer, err = connectproxy.New(u, proxy.Direct) + } + + dialer, err := proxyDialer.Dial("tcp", addr) + if err != nil { + return nil, err + } + conn := tls.Client(dialer, tlsConf) + return conn, nil +} diff --git a/easyproxy/go.mod b/easyproxy/go.mod index d2c531f..f88550a 100644 --- a/easyproxy/go.mod +++ b/easyproxy/go.mod @@ -3,3 +3,5 @@ module github.com/hrfee/jfa-go/easyproxy go 1.20 require golang.org/x/net v0.15.0 + +require github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b // indirect diff --git a/easyproxy/go.sum b/easyproxy/go.sum index 9746d02..a131b72 100644 --- a/easyproxy/go.sum +++ b/easyproxy/go.sum @@ -1,2 +1,4 @@ +github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI= +github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg= golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= diff --git a/email.go b/email.go index 0f517ca..9671ee4 100644 --- a/email.go +++ b/email.go @@ -19,6 +19,7 @@ import ( "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" + "github.com/hrfee/jfa-go/easyproxy" "github.com/hrfee/mediabrowser" "github.com/itchyny/timefmt-go" "github.com/mailgun/mailgun-go/v4" @@ -87,7 +88,11 @@ func NewEmailer(app *appContext) *Emailer { if username == "" && password != "" { username = emailer.fromAddr } - err := emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, password, sslTLS, app.config.Section("smtp").Key("ssl_cert").MustString(""), app.config.Section("smtp").Key("hello_hostname").String(), app.config.Section("smtp").Key("cert_validation").MustBool(true)) + var proxyConf *easyproxy.ProxyConfig = nil + if app.proxyEnabled { + proxyConf = &app.proxyConfig + } + err := emailer.NewSMTP(app.config.Section("smtp").Key("server").String(), app.config.Section("smtp").Key("port").MustInt(465), username, password, sslTLS, app.config.Section("smtp").Key("ssl_cert").MustString(""), app.config.Section("smtp").Key("hello_hostname").String(), app.config.Section("smtp").Key("cert_validation").MustBool(true), proxyConf) if err != nil { app.err.Printf("Error while initiating SMTP mailer: %v", err) } @@ -113,7 +118,7 @@ type SMTP struct { } // NewSMTP returns an SMTP emailClient. -func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool, certPath string, helloHostname string, validateCertificate bool) (err error) { +func (emailer *Emailer) NewSMTP(server string, port int, username, password string, sslTLS bool, certPath string, helloHostname string, validateCertificate bool, proxy *easyproxy.ProxyConfig) (err error) { sender := &SMTP{} sender.Client = sMail.NewSMTPClient() if sslTLS { @@ -131,12 +136,16 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri sender.Client.Host = server sender.Client.Port = port sender.Client.KeepAlive = false + // x509.SystemCertPool is unavailable on windows if PLATFORM == "windows" { sender.Client.TLSConfig = &tls.Config{ InsecureSkipVerify: !validateCertificate, ServerName: server, } + if proxy != nil { + sender.Client.CustomConn, err = easyproxy.NewConn(*proxy, fmt.Sprintf("%s:%d", server, port), sender.Client.TLSConfig) + } emailer.sender = sender return } @@ -156,6 +165,9 @@ func (emailer *Emailer) NewSMTP(server string, port int, username, password stri ServerName: server, RootCAs: rootCAs, } + if proxy != nil { + sender.Client.CustomConn, err = easyproxy.NewConn(*proxy, fmt.Sprintf("%s:%d", server, port), sender.Client.TLSConfig) + } emailer.sender = sender return } diff --git a/go.mod b/go.mod index c4650b4..f2866a2 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/swaggo/gin-swagger v1.6.0 github.com/timshannon/badgerhold/v4 v4.0.2 github.com/writeas/go-strip-markdown v2.0.1+incompatible - github.com/xhit/go-simple-mail/v2 v2.13.0 + github.com/xhit/go-simple-mail/v2 v2.16.0 gopkg.in/ini.v1 v1.67.0 maunium.net/go/mautrix v0.15.3 ) @@ -93,6 +93,7 @@ require ( github.com/klauspost/compress v1.16.6 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/leodido/go-urn v1.2.4 // indirect + github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect diff --git a/go.sum b/go.sum index 85f8f14..07de685 100644 --- a/go.sum +++ b/go.sum @@ -264,6 +264,8 @@ github.com/lithammer/shortuuid/v3 v3.0.7/go.mod h1:vMk8ke37EmiewwolSO1NLW8vP4ZaK github.com/lxn/walk v0.0.0-20210112085537-c389da54e794/go.mod h1:E23UucZGqpuUANJooIbHWCufXvOcT6E7Stq81gU+CSQ= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI= +github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg= github.com/mailgun/mailgun-go/v4 v4.9.1 h1:D/jhJXYod4RqRsNOOSrjrtAcMEnz8mPYJmeA5cueHKY= github.com/mailgun/mailgun-go/v4 v4.9.1/go.mod h1:FJlF9rI5cQT+mrwujtJjPMbIVy3Ebor9bKTVsJ0QU40= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -394,6 +396,8 @@ github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6Fk github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE= github.com/xhit/go-simple-mail/v2 v2.13.0 h1:OANWU9jHZrVfBkNkvLf8Ww0fexwpQVF/v/5f96fFTLI= github.com/xhit/go-simple-mail/v2 v2.13.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= +github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA= +github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/main.go b/main.go index 45ed009..8156edf 100644 --- a/main.go +++ b/main.go @@ -114,6 +114,9 @@ type appContext struct { newUpdate bool // Whether whatever's in update is new. tag Tag update Update + proxyEnabled bool + proxyTransport *http.Transport + proxyConfig easyproxy.ProxyConfig internalPWRs map[string]InternalPWR ConfirmationKeys map[string]map[string]newUserDTO // Map of invite code to jwt to request confirmationKeysLock sync.Mutex @@ -392,20 +395,8 @@ func start(asDaemon, firstCall bool) { app.jf.Verbose = true } - if app.config.Section("advanced").Key("proxy").MustBool(false) { - // FIXME: Use Proxy - protocol := easyproxy.HTTP - if strings.Contains(app.config.Section("advanced").Key("proxy_protocol").MustString("http"), "socks") { - protocol = easyproxy.SOCKS5 - } - addr := app.config.Section("advanced").Key("proxy_address").MustString("") - user := app.config.Section("advanced").Key("proxy_user").MustString("") - password := app.config.Section("advanced").Key("proxy_password").MustString("") - transport, err := easyproxy.NewTransport(protocol, addr, user, password) - if err != nil { - app.err.Printf("Failed to initialize Proxy: %v\n", err) - } - app.jf.SetTransport(transport) + if app.proxyEnabled { + app.jf.SetTransport(app.proxyTransport) } var status int diff --git a/updater.go b/updater.go index c6c8c62..1e0e78a 100644 --- a/updater.go +++ b/updater.go @@ -185,6 +185,11 @@ type BuildDTO struct { Tags map[string]Tag } +// SetTransport sets the http.Transport to use for requests. Can be used to set a proxy. +func (ud *Updater) SetTransport(t *http.Transport) { + ud.httpClient.Transport = t +} + func (ud *Updater) GetTag() (Tag, int, error) { if ud.buildType == off { return Tag{}, -1, nil From 4864c6c53c5bf0cdad9180a9ee71d9fa38490ef1 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Fri, 6 Oct 2023 14:41:33 +0100 Subject: [PATCH 09/19] ombi: implement getOmbiImportedUser --- api-ombi.go | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/api-ombi.go b/api-ombi.go index 0113885..13610f4 100644 --- a/api-ombi.go +++ b/api-ombi.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/gin-gonic/gin" + "github.com/hrfee/mediabrowser" ) func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, error) { @@ -29,7 +30,31 @@ func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, er return ombiUser, code, err } } - return nil, 400, fmt.Errorf("Couldn't find user") + return nil, 400, fmt.Errorf("couldn't find user") +} + +// Returns a user with the given name who has been imported from Jellyfin/Emby by Ombi +func (app *appContext) getOmbiImportedUser(name string) (map[string]interface{}, int, error) { + // Ombi User Types: 3/4 = Emby, 5 = Jellyfin + ombiUsers, code, err := app.ombi.GetUsers() + if err != nil || code != 200 { + return nil, code, err + } + for _, ombiUser := range ombiUsers { + if ombiUser["userName"].(string) == name { + uType, ok := ombiUser["userType"].(int) + if !ok { // Don't know if Ombi somehow allows duplicate usernames + continue + } + if serverType == mediabrowser.JellyfinServer && uType != 5 { // Jellyfin + continue + } else if uType != 3 && uType != 4 { // Emby + continue + } + return ombiUser, code, err + } + } + return nil, 400, fmt.Errorf("couldn't find user") } // @Summary Get a list of Ombi users. From cf94fdb2f049c3ac81a3c1e23f8aa3acb3b731ff Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Mon, 9 Oct 2023 10:39:22 +0100 Subject: [PATCH 10/19] ombi: fix password reset on default route the ombi password wasn't being changed w/ password resets initiated through the admin page (and probably by other routes, too), as the code was considering a HTTP 204 from Jellyfin as a failure, causing it to skip anything with Ombi. Also added a little check for Ombi-imported accounts that probably won't help with anything, but whatever. --- api-ombi.go | 15 +++++++++++ api-users.go | 73 ++++++++++++++++++++++++++++------------------------ api.go | 2 +- 3 files changed, 56 insertions(+), 34 deletions(-) diff --git a/api-ombi.go b/api-ombi.go index 13610f4..ca148aa 100644 --- a/api-ombi.go +++ b/api-ombi.go @@ -132,3 +132,18 @@ func (app *appContext) DeleteOmbiProfile(gc *gin.Context) { app.storage.SetProfileKey(profileName, profile) respondBool(204, true, gc) } + +func (app *appContext) applyOmbiProfile(user map[string]interface{}, profile map[string]interface{}) (status int, err error) { + for k, v := range profile { + switch v.(type) { + case map[string]interface{}, []interface{}: + user[k] = v + default: + if v != user[k] { + user[k] = v + } + } + } + status, err = app.ombi.ModifyUser(user) + return +} diff --git a/api-users.go b/api-users.go index f30930d..ff2a0bd 100644 --- a/api-users.go +++ b/api-users.go @@ -377,30 +377,47 @@ func (app *appContext) newUser(req newUserDTO, confirmed bool) (f errorFunc, suc if profile.Ombi != nil && len(profile.Ombi) != 0 { template := profile.Ombi errors, code, err := app.ombi.NewUser(req.Username, req.Password, req.Email, template) + accountExists := false + var ombiUser map[string]interface{} if err != nil || code != 200 { - app.info.Printf("Failed to create Ombi user (%d): %s", code, err) - app.debug.Printf("Errors reported by Ombi: %s", strings.Join(errors, ", ")) - } else { - app.info.Println("Created Ombi user") - if discordVerified || telegramVerified { - ombiUser, status, err := app.getOmbiUser(id) + // Check if on the off chance, Ombi's user importer has already added the account. + ombiUser, status, err = app.getOmbiImportedUser(req.Username) + if status == 200 && err == nil { + app.info.Println("Found existing Ombi user, applying changes") + accountExists = true + template["password"] = req.Password + status, err = app.applyOmbiProfile(ombiUser, template) if status != 200 || err != nil { - app.err.Printf("Failed to get Ombi user (%d): %v", status, err) - } else { - dID := "" - tUser := "" - if discordVerified { - dID = discordUser.ID - } - if telegramVerified { - u, _ := app.storage.GetTelegramKey(user.ID) - tUser = u.Username - } - resp, status, err := app.ombi.SetNotificationPrefs(ombiUser, dID, tUser) - if !(status == 200 || status == 204) || err != nil { - app.err.Printf("Failed to link Telegram/Discord to Ombi (%d): %v", status, err) - app.debug.Printf("Response: %v", resp) - } + app.err.Printf("Failed to modify existing Ombi user (%d): %v\n", status, err) + } + } else { + app.info.Printf("Failed to create Ombi user (%d): %s", code, err) + app.debug.Printf("Errors reported by Ombi: %s", strings.Join(errors, ", ")) + } + } else { + ombiUser, status, err = app.getOmbiUser(id) + if status != 200 || err != nil { + app.err.Printf("Failed to get Ombi user (%d): %v", status, err) + } else { + app.info.Println("Created Ombi user") + accountExists = true + } + } + if accountExists { + if discordVerified || telegramVerified { + dID := "" + tUser := "" + if discordVerified { + dID = discordUser.ID + } + if telegramVerified { + u, _ := app.storage.GetTelegramKey(user.ID) + tUser = u.Username + } + resp, status, err := app.ombi.SetNotificationPrefs(ombiUser, dID, tUser) + if !(status == 200 || status == 204) || err != nil { + app.err.Printf("Failed to link Telegram/Discord to Ombi (%d): %v", status, err) + app.debug.Printf("Response: %v", resp) } } } @@ -1214,17 +1231,7 @@ func (app *appContext) ApplySettings(gc *gin.Context) { // newUser["userName"] = user["userName"] // newUser["alias"] = user["alias"] // newUser["emailAddress"] = user["emailAddress"] - for k, v := range ombi { - switch v.(type) { - case map[string]interface{}, []interface{}: - user[k] = v - default: - if v != user[k] { - user[k] = v - } - } - } - status, err = app.ombi.ModifyUser(user) + status, err = app.applyOmbiProfile(user, ombi) if status != 200 || err != nil { errorString += fmt.Sprintf("Apply %d: %v ", status, err) } diff --git a/api.go b/api.go index 53344eb..d9d44e4 100644 --- a/api.go +++ b/api.go @@ -182,7 +182,7 @@ func (app *appContext) ResetSetPassword(gc *gin.Context) { } if app.config.Section("ombi").Key("enabled").MustBool(false) { // Silently fail for changing ombi passwords - if status != 200 || err != nil { + if (status != 200 && status != 204) || err != nil { app.err.Printf("Failed to get user \"%s\" from jellyfin/emby (%d): %v", username, status, err) respondBool(200, true, gc) return From 4bc51570c256bd3b468aaaf8f1097f09e6769b7c Mon Sep 17 00:00:00 2001 From: Anton B Date: Mon, 2 Oct 2023 17:12:38 +0000 Subject: [PATCH 11/19] Translated using Weblate (Swedish) Currently translated at 82.3% (42 of 51 strings) Translation: jfa-go/Common Strings Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/common-strings/sv/ --- lang/common/sv-se.json | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/lang/common/sv-se.json b/lang/common/sv-se.json index ff602c8..46d887a 100644 --- a/lang/common/sv-se.json +++ b/lang/common/sv-se.json @@ -23,7 +23,22 @@ "expiry": "Löper ut", "edit": "Redigera", "delete": "Radera", - "inviteRemainingUses": "Återstående användningar" + "inviteRemainingUses": "Återstående användningar", + "send": "Skicka", + "linkDiscord": "Länka Discord", + "copied": "Kopierat", + "linkTelegram": "Länka Telegram", + "contactEmail": "Kontakta via e-post", + "contactTelegram": "Kontakta via Telegram", + "refresh": "Uppdatera", + "required": "Obligatoriskt", + "contactDiscord": "Kontakt via Discord", + "linkMatrix": "Länka Matrix", + "reEnable": "Återaktivera", + "disable": "Inaktivera", + "contactMethods": "Kontaktmetoder", + "accountStatus": "Kontostatus", + "notSet": "Inte inställt" }, "notifications": { "errorLoginBlank": "Användarnamnet och/eller lösenordet lämnades tomt.", @@ -31,6 +46,5 @@ "errorUnknown": "Okänt fel.", "error401Unauthorized": "Obehörig. Prova att uppdatera sidan.", "errorSaveSettings": "Det gick inte att spara inställningarna." - }, - "quantityStrings": {} -} \ No newline at end of file + } +} From 07d738006f4ba862662c33c2fc769b83fed105cd Mon Sep 17 00:00:00 2001 From: Killianbe Date: Mon, 9 Oct 2023 21:45:06 +0000 Subject: [PATCH 12/19] translation from Weblate (French) Currently translated at 96.8% (182 of 188 strings) Translation: jfa-go/Admin Page Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/admin/fr/ --- lang/admin/fr-fr.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lang/admin/fr-fr.json b/lang/admin/fr-fr.json index 78aa356..b4d1bea 100644 --- a/lang/admin/fr-fr.json +++ b/lang/admin/fr-fr.json @@ -118,7 +118,13 @@ "userPagePage": "Page utilisateur : Page", "after": "Après", "before": "Avant", - "unlink": "Délier le compte" + "unlink": "Délier le compte", + "enableReferrals": "Activer Parrainage", + "enableReferralsDescription": "Offrez aux utilisateurs un lien de parrainage personnel semblable à une invitation, à envoyer à vos amis/famille. Peut provenir modèle de profil ou d’une invitation existante.", + "invite": "Inviter", + "userLabel": "Étiquette", + "userLabelDescription": "Étiquette à appliquer aux utilisateurs créés avec cette invitation.", + "disableReferrals": "Désactiver Parrainage" }, "notifications": { "changedEmailAddress": "Adresse e-mail modifiée de {n}.", From 42264f0547deffa1769cd0cf00197bd319ad3c70 Mon Sep 17 00:00:00 2001 From: Killianbe Date: Mon, 9 Oct 2023 21:37:19 +0000 Subject: [PATCH 13/19] translation from Weblate (French) Currently translated at 100.0% (62 of 62 strings) Translation: jfa-go/Account Creation Form Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/form/fr/ --- lang/form/fr-fr.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lang/form/fr-fr.json b/lang/form/fr-fr.json index ffd9826..e3cc4a1 100644 --- a/lang/form/fr-fr.json +++ b/lang/form/fr-fr.json @@ -7,11 +7,11 @@ "pageTitle": "Créer un compte Jellyfin", "createAccountHeader": "Création du compte", "accountDetails": "Détails", - "emailAddress": "Courriel", + "emailAddress": "Email", "username": "Nom d'utilisateur", "password": "Mot de passe", "reEnterPassword": "Confirmez mot de passe", - "reEnterPasswordInvalid": "Les mots de passe ne correspondent pas.", + "reEnterPasswordInvalid": "Les mots de passe ne sont pas pareils.", "createAccountButton": "Créer le compte", "passwordRequirementsHeader": "Mot de passe requis", "successHeader": "Succès !", @@ -35,7 +35,10 @@ "editContactMethod": "Modifier le moyen de contact", "joinTheServer": "Rejoindre le serveur :", "customMessagePlaceholderHeader": "Personnaliser cette carte", - "resetPassword": "Réinitialisation de mot de passe" + "resetPassword": "Réinitialisation mot de passe", + "referralsDescription": "Invitez vos amis et votre famille à Jellyfin avec ce lien. Revenez ici pour en obtenir un nouveau s'il expire.", + "copyReferral": "Copier le lien", + "invitedBy": "Vous avez été invité par l'utilisateur {user}." }, "validationStrings": { "length": { From 638be18ea89636f3721e66a86b5d82c07eb2bde2 Mon Sep 17 00:00:00 2001 From: Killianbe Date: Mon, 9 Oct 2023 21:38:45 +0000 Subject: [PATCH 14/19] Translated using Weblate (French) Currently translated at 100.0% (51 of 51 strings) Translation: jfa-go/Common Strings Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/common-strings/fr/ --- lang/common/fr-fr.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lang/common/fr-fr.json b/lang/common/fr-fr.json index 86ae13b..e3a06c5 100644 --- a/lang/common/fr-fr.json +++ b/lang/common/fr-fr.json @@ -5,7 +5,7 @@ "strings": { "username": "Nom d'utilisateur", "password": "Mot de passe", - "emailAddress": "Adresse courriel", + "emailAddress": "Adresse Email", "name": "Nom", "submit": "Soumettre", "send": "Envoyer", @@ -29,7 +29,7 @@ "logout": "Se déconnecter", "admin": "Administrateur", "enabled": "Activé", - "disabled": "Désactivé", + "disabled": "Désactiver", "reEnable": "Ré-activé", "disable": "Désactivé", "expiry": "Expiration", @@ -40,7 +40,8 @@ "accountStatus": "Statut du compte", "notSet": "Non défini", "myAccount": "Mon compte", - "contactMethods": "Moyens de contact" + "contactMethods": "Moyens de contact", + "referrals": "Programme de parrainage" }, "notifications": { "errorLoginBlank": "Le nom d'utilisateur et/ou le mot de passe sont vides.", From 4aa095d46614e6e9c9a367d6961ada6b3eda2f12 Mon Sep 17 00:00:00 2001 From: Killianbe Date: Mon, 9 Oct 2023 21:39:52 +0000 Subject: [PATCH 15/19] Translated using Weblate (French) Currently translated at 92.5% (111 of 120 strings) Translation: jfa-go/Setup Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/setup/fr/ --- lang/setup/fr-fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/setup/fr-fr.json b/lang/setup/fr-fr.json index 7ad7a19..d7b8058 100644 --- a/lang/setup/fr-fr.json +++ b/lang/setup/fr-fr.json @@ -8,7 +8,7 @@ "back": "Retour", "optional": "Optionnel", "serverType": "Type de serveur", - "disabled": "Désactivé", + "disabled": "Désactiver", "enabled": "Activé", "port": "Port", "message": "Message", From f37451021f750eb9cda8a9a249212b406c630465 Mon Sep 17 00:00:00 2001 From: Killianbe Date: Mon, 9 Oct 2023 21:47:23 +0000 Subject: [PATCH 16/19] translation from Weblate (French) Currently translated at 100.0% (188 of 188 strings) Translation: jfa-go/Admin Page Translate-URL: https://weblate.jfa-go.com/projects/jfa-go/admin/fr/ --- lang/admin/fr-fr.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lang/admin/fr-fr.json b/lang/admin/fr-fr.json index b4d1bea..2b08529 100644 --- a/lang/admin/fr-fr.json +++ b/lang/admin/fr-fr.json @@ -124,7 +124,8 @@ "invite": "Inviter", "userLabel": "Étiquette", "userLabelDescription": "Étiquette à appliquer aux utilisateurs créés avec cette invitation.", - "disableReferrals": "Désactiver Parrainage" + "disableReferrals": "Désactiver Parrainage", + "enableReferralsProfileDescription": "Donnez aux utilisateurs créés avec ce profil un lien de parrainage personnel semblable à une invitation, à envoyer à vos amis/famille. Créez une invitation avec les paramètres souhaités, puis sélectionnez-la ici. Chaque référence sera alors basée sur cette invitation. Vous pouvez supprimer l'invitation une fois terminée." }, "notifications": { "changedEmailAddress": "Adresse e-mail modifiée de {n}.", @@ -162,7 +163,9 @@ "accountConnected": "Compte connecté.", "savedAnnouncement": "Annonce enregistrée.", "setOmbiProfile": "Profil ombi enregistré.", - "errorSetOmbiProfile": "Echec de la sauvegarde du profil ombi." + "errorSetOmbiProfile": "Echec de la sauvegarde du profil ombi.", + "errorNoReferralTemplate": "Le profil ne contient pas de modèle de référence, ajoutez-en un dans les paramètres.", + "referralsEnabled": "Parrainage activer." }, "quantityStrings": { "modifySettingsFor": { @@ -220,6 +223,10 @@ "setExpiry": { "singular": "Définir l'expiration pour {n} utilisateur", "plural": "Définir l'expiration pour {n} utilisateurs" + }, + "enableReferralsFor": { + "singular": "Activer les parrainages pour {n} utilisateur", + "plural": "Activer les parrainages pour {n} utilisateur" } } } From 9c771e193e90d422317e91062008be07697e81b4 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Tue, 10 Oct 2023 10:22:22 +0100 Subject: [PATCH 17/19] lang: fix typo in french `{n]` instead of `{n}` meant expiry times on the user page weren't being rendered. --- lang/common/fr-fr.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lang/common/fr-fr.json b/lang/common/fr-fr.json index e3a06c5..e3aeace 100644 --- a/lang/common/fr-fr.json +++ b/lang/common/fr-fr.json @@ -52,16 +52,16 @@ }, "quantityStrings": { "year": { - "plural": "{n] années", - "singular": "{n] année" + "plural": "{n} années", + "singular": "{n} année" }, "day": { - "singular": "{n] jour", - "plural": "{n] jours" + "singular": "{n} jour", + "plural": "{n} jours" }, "month": { - "singular": "{n] mois", - "plural": "{n] mois" + "singular": "{n} mois", + "plural": "{n} mois" } } } From 0366e5116d79df4d59985df8ae8c106d0fbfc365 Mon Sep 17 00:00:00 2001 From: Violet Scheen Date: Tue, 10 Oct 2023 11:14:57 -0400 Subject: [PATCH 18/19] Update discord.go Cleaning up a bit --- discord.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/discord.go b/discord.go index fc7a74f..cf1eb1b 100644 --- a/discord.go +++ b/discord.go @@ -550,15 +550,17 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s //if mins > 0 { // expmin = mins //} - // Need to check whether requestor is linked to the admin account *possibly add Admin bool to DiscordUser struct + // Check whether requestor is linked to the admin account requestoremail, ok := d.app.storage.GetEmailsKey(requestor.JellyfinID) if !ok { d.app.err.Printf("Failed to verify admin") } if !requestoremail.Admin { d.app.err.Printf("User is not admin") + //add response message + return } - // variation of app.GenerateInvite, some parts commented to potentially add back in later with the other options + // variation of app.GenerateInvite, some parts commented to potentially add back in later with the other command options //d.app.debug.Println("Generating new invite with options: %s: %i: %s: %s", invuser, expmin, profile, label) currentTime := time.Now() validTill := currentTime.Add(time.Minute*time.Duration(expmin)) @@ -593,14 +595,17 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s if err != nil { invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser) d.app.err.Printf("%s: Failed to construct invite message: %v", inviteCode, err) + //add response message } else { var err error err = d.app.discord.SendDM(msg, discord) if err != nil { invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser) d.app.err.Printf("%s: %s: %v", inviteCode, invite.SendTo, err) + //add response message } else { d.app.info.Printf("%s: Sent invite email to \"%s\"", inviteCode, invuser) + //add response message } } } From 525c13ff6ab9fe503932a3927a1d9994a4ea20a4 Mon Sep 17 00:00:00 2001 From: Violet Scheen Date: Tue, 10 Oct 2023 12:55:56 -0400 Subject: [PATCH 19/19] Update discord.go --- discord.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/discord.go b/discord.go index cf1eb1b..fd3de07 100644 --- a/discord.go +++ b/discord.go @@ -586,11 +586,9 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s }*/ invite.ValidTill = validTill if invuser != "" && d.app.config.Section("invite_emails").Key("enabled").MustBool(false) { - discord := "" d.app.debug.Printf("%s: Sending invite message", inviteCode) invname, err := d.bot.GuildMember(d.guildID, invuser) invite.SendTo = invname.User.Username - msg, err := d.app.email.constructInvite(inviteCode, invite, d.app, false) if err != nil { invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser) @@ -598,7 +596,7 @@ func (d *DiscordDaemon) cmdInvite(s *dg.Session, i *dg.InteractionCreate, lang s //add response message } else { var err error - err = d.app.discord.SendDM(msg, discord) + err = d.app.discord.SendDM(msg, invuser) if err != nil { invite.SendTo = fmt.Sprintf("Failed to send to %s", invuser) d.app.err.Printf("%s: %s: %v", inviteCode, invite.SendTo, err)