diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..33afbf4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,33 @@ +--- +name: Bug report +about: Template for bug reports. +title: '' +labels: '' +assignees: '' + +--- + +#### Read the [FAQ](https://github.com/hrfee/jfa-go/wiki/FAQ) first! + +**Describe the bug** + +Describe the problem, and what you would expect if it isn't clear already. + +**To Reproduce** + +What to do to reproduce the problem. + +**Logs** + +When you notice the problem, check the output of `jfa-go`. If the problem is not obvious (e.g a panic (red text) or 'ERROR' log), re-run jfa-go with the `-debug` argument and reproduce the problem. You should then take a screenshot of the output, or paste it here, preferably between \`\`\` tags (e.g \`\`\``Log here`\`\`\`). Remember to censor any personal information. + +If nothing catches your eye in the log, access the admin page via your browser, go into the console (Right click > Inspect Element > Console), refresh, reproduce the problem then paste the output here in the same way as above. + +**Configuration** + +If you see it as necessary, include relevant sections of your `config.ini`, for example, include `[email]` and `[smtp]|[mailgun]` if you have an email issue. + +**Platform/Version** + +Include the platform jfa-go is running on (e.g Windows, Linux, Docker), the version (first line of output by `jfa-go` or Settings>About in web UI), and if necessary the browser version and platform. + diff --git a/email.go b/email.go index b687006..9b13696 100644 --- a/email.go +++ b/email.go @@ -20,6 +20,7 @@ type emailClient interface { send(address, fromName, fromAddr string, email *Email) error } +// Mailgun client implements emailClient. type Mailgun struct { client *mailgun.MailgunImpl } @@ -38,14 +39,15 @@ func (mg *Mailgun) send(address, fromName, fromAddr string, email *Email) error return err } -type Smtp struct { - sslTls bool +// SMTP supports SSL/TLS and STARTTLS; implements emailClient. +type SMTP struct { + sslTLS bool host, server string port int auth smtp.Auth } -func (sm *Smtp) send(address, fromName, fromAddr string, email *Email) error { +func (sm *SMTP) send(address, fromName, fromAddr string, email *Email) error { e := jEmail.NewEmail() e.Subject = email.subject e.From = fmt.Sprintf("%s <%s>", fromName, fromAddr) @@ -58,7 +60,7 @@ func (sm *Smtp) send(address, fromName, fromAddr string, email *Email) error { } server := fmt.Sprintf("%s:%d", sm.server, sm.port) var err error - if sm.sslTls { + if sm.sslTLS { err = e.SendWithTLS(server, sm.auth, tlsConfig) } else { err = e.SendWithStartTLS(server, sm.auth, tlsConfig) @@ -78,27 +80,28 @@ type Email struct { html, text string } -func (email *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expires_in string) { +func (emailer *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expiresIn string) { d, _ = strtime.Strftime(expiry, datePattern) t, _ = strtime.Strftime(expiry, timePattern) - current_time := time.Now() + currentTime := time.Now() if tzaware { - current_time = current_time.UTC() + currentTime = currentTime.UTC() } - _, _, days, hours, minutes, _ := timeDiff(expiry, current_time) + _, _, days, hours, minutes, _ := timeDiff(expiry, currentTime) if days != 0 { - expires_in += fmt.Sprintf("%dd ", days) + expiresIn += fmt.Sprintf("%dd ", days) } if hours != 0 { - expires_in += fmt.Sprintf("%dh ", hours) + expiresIn += fmt.Sprintf("%dh ", hours) } if minutes != 0 { - expires_in += fmt.Sprintf("%dm ", minutes) + expiresIn += fmt.Sprintf("%dm ", minutes) } - expires_in = strings.TrimSuffix(expires_in, " ") + expiresIn = strings.TrimSuffix(expiresIn, " ") return } +// NewEmailer configures and returns a new emailer. func NewEmailer(app *appContext) *Emailer { emailer := &Emailer{ fromAddr: app.config.Section("email").Key("address").String(), @@ -117,6 +120,7 @@ func NewEmailer(app *appContext) *Emailer { return emailer } +// NewMailgun returns a Mailgun emailClient. func (emailer *Emailer) NewMailgun(url, key string) { sender := &Mailgun{ client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key), @@ -130,13 +134,14 @@ func (emailer *Emailer) NewMailgun(url, key string) { emailer.sender = sender } -func (emailer *Emailer) NewSMTP(server string, port int, password, host string, sslTls bool) { - emailer.sender = &Smtp{ +// NewSMTP returns an SMTP emailClient. +func (emailer *Emailer) NewSMTP(server string, port int, password, host string, sslTLS bool) { + emailer.sender = &SMTP{ auth: smtp.PlainAuth("", emailer.fromAddr, password, host), server: server, host: host, port: port, - sslTls: sslTls, + sslTLS: sslTLS, } } @@ -145,10 +150,10 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont subject: app.config.Section("invite_emails").Key("subject").String(), } expiry := invite.ValidTill - d, t, expires_in := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern) + d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern) message := app.config.Section("email").Key("message").String() - invite_link := app.config.Section("invite_emails").Key("url_base").String() - invite_link = fmt.Sprintf("%s/%s", invite_link, code) + inviteLink := app.config.Section("invite_emails").Key("url_base").String() + inviteLink = fmt.Sprintf("%s/%s", inviteLink, code) for _, key := range []string{"html", "text"} { fpath := app.config.Section("invite_emails").Key("email_" + key).String() @@ -160,8 +165,8 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont err = tpl.Execute(&tplData, map[string]string{ "expiry_date": d, "expiry_time": t, - "expires_in": expires_in, - "invite_link": invite_link, + "expires_in": expiresIn, + "invite_link": inviteLink, "message": message, }) if err != nil { @@ -244,7 +249,7 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error) email := &Email{ subject: app.config.Section("password_resets").Key("subject").MustString("Password reset - Jellyfin"), } - d, t, expires_in := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern) + 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() @@ -257,7 +262,7 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error) "username": pwr.Username, "expiry_date": d, "expiry_time": t, - "expires_in": expires_in, + "expires_in": expiresIn, "pin": pwr.Pin, "message": message, }) @@ -273,6 +278,7 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error) return email, nil } +// calls the send method in the underlying emailClient. func (emailer *Emailer) send(address string, email *Email) error { return emailer.sender.send(address, emailer.fromName, emailer.fromAddr, email) } diff --git a/ombi.go b/ombi.go index ece2e6d..deeacbb 100644 --- a/ombi.go +++ b/ombi.go @@ -32,6 +32,7 @@ func newOmbi(server, key string, noFail bool) *Ombi { } } +// does a GET and returns the response as an io.reader. func (ombi *Ombi) _getReader(url string, params map[string]string) (string, int, error) { if ombi.key == "" { return "", 401, fmt.Errorf("No API key provided") @@ -70,6 +71,7 @@ func (ombi *Ombi) _getReader(url string, params map[string]string) (string, int, return buf.String(), resp.StatusCode, nil } +// does a POST and optionally returns response as string. Returns a string instead of an io.reader bcs i couldn't get it working otherwise. func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool) (string, int, error) { responseText := "" params, _ := json.Marshal(data) @@ -105,12 +107,14 @@ func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool) return responseText, resp.StatusCode, nil } +// gets an ombi user by their ID. func (ombi *Ombi) userByID(id string) (result map[string]interface{}, code int, err error) { resp, code, err := ombi._getReader(fmt.Sprintf("%s/api/v1/Identity/User/%s", ombi.server, id), nil) json.Unmarshal([]byte(resp), &result) return } +// gets a list of all users. func (ombi *Ombi) getUsers() (result []map[string]interface{}, code int, err error) { resp, code, err := ombi._getReader(fmt.Sprintf("%s/api/v1/Identity/Users", ombi.server), nil) json.Unmarshal([]byte(resp), &result) @@ -129,6 +133,7 @@ var stripFromOmbi = []string{ "userName", } +// returns a template based on the user corresponding to the provided ID's settings. func (ombi *Ombi) templateByID(id string) (result map[string]interface{}, code int, err error) { result, code, err = ombi.userByID(id) if err != nil || code != 200 { @@ -147,6 +152,7 @@ func (ombi *Ombi) templateByID(id string) (result map[string]interface{}, code i return } +// creates a new user. func (ombi *Ombi) newUser(username, password, email string, template map[string]interface{}) ([]string, int, error) { url := fmt.Sprintf("%s/api/v1/Identity", ombi.server) user := template diff --git a/pwreset.go b/pwreset.go index 01f854f..38ac9d1 100644 --- a/pwreset.go +++ b/pwreset.go @@ -58,7 +58,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) { return } app.info.Printf("New password reset for user \"%s\"", pwr.Username) - if ct := time.Now(); pwr.Expiry.After(ct) { + if currentTime := time.Now(); pwr.Expiry.After(currentTime) { user, status, err := app.jf.userByName(pwr.Username, false) if !(status == 200 || status == 204) || err != nil { app.err.Printf("Failed to get users from Jellyfin: Code %d", status)