mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-29 12:30:11 +00:00
Compare commits
No commits in common. "d7fcfe94162b685939a8193860edf74e81fd7ef3" and "4ca14675e6627d51b7535e5ea63f5da06ec4e452" have entirely different histories.
d7fcfe9416
...
4ca14675e6
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
33
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,33 +0,0 @@
|
|||||||
---
|
|
||||||
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.
|
|
||||||
|
|
2
Makefile
2
Makefile
@ -29,7 +29,7 @@ mail:
|
|||||||
python3 mail/generate.py
|
python3 mail/generate.py
|
||||||
|
|
||||||
version:
|
version:
|
||||||
python3 version.py auto version.go
|
python3 version.py git version.go
|
||||||
|
|
||||||
compile:
|
compile:
|
||||||
echo "Downloading deps"
|
echo "Downloading deps"
|
||||||
|
@ -50,7 +50,7 @@ docker create \
|
|||||||
-v /path/to/.config/jfa-go:/data \ # Path to wherever you want to store the config file and other data
|
-v /path/to/.config/jfa-go:/data \ # Path to wherever you want to store the config file and other data
|
||||||
-v /path/to/jellyfin:/jf \ # Path to jellyfin config directory
|
-v /path/to/jellyfin:/jf \ # Path to jellyfin config directory
|
||||||
-v /etc/localtime:/etc/localtime:ro \ # Makes sure time is correct
|
-v /etc/localtime:/etc/localtime:ro \ # Makes sure time is correct
|
||||||
hrfee/jfa-go # hrfee/jfa-go:unstable for latest build from git
|
hrfee/jfa-go
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Build from source
|
#### Build from source
|
||||||
|
50
email.go
50
email.go
@ -20,7 +20,6 @@ type emailClient interface {
|
|||||||
send(address, fromName, fromAddr string, email *Email) error
|
send(address, fromName, fromAddr string, email *Email) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mailgun client implements emailClient.
|
|
||||||
type Mailgun struct {
|
type Mailgun struct {
|
||||||
client *mailgun.MailgunImpl
|
client *mailgun.MailgunImpl
|
||||||
}
|
}
|
||||||
@ -39,15 +38,14 @@ func (mg *Mailgun) send(address, fromName, fromAddr string, email *Email) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SMTP supports SSL/TLS and STARTTLS; implements emailClient.
|
type Smtp struct {
|
||||||
type SMTP struct {
|
sslTls bool
|
||||||
sslTLS bool
|
|
||||||
host, server string
|
host, server string
|
||||||
port int
|
port int
|
||||||
auth smtp.Auth
|
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 := jEmail.NewEmail()
|
||||||
e.Subject = email.subject
|
e.Subject = email.subject
|
||||||
e.From = fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
e.From = fmt.Sprintf("%s <%s>", fromName, fromAddr)
|
||||||
@ -60,7 +58,7 @@ func (sm *SMTP) send(address, fromName, fromAddr string, email *Email) error {
|
|||||||
}
|
}
|
||||||
server := fmt.Sprintf("%s:%d", sm.server, sm.port)
|
server := fmt.Sprintf("%s:%d", sm.server, sm.port)
|
||||||
var err error
|
var err error
|
||||||
if sm.sslTLS {
|
if sm.sslTls {
|
||||||
err = e.SendWithTLS(server, sm.auth, tlsConfig)
|
err = e.SendWithTLS(server, sm.auth, tlsConfig)
|
||||||
} else {
|
} else {
|
||||||
err = e.SendWithStartTLS(server, sm.auth, tlsConfig)
|
err = e.SendWithStartTLS(server, sm.auth, tlsConfig)
|
||||||
@ -80,28 +78,27 @@ type Email struct {
|
|||||||
html, text string
|
html, text string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (emailer *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expiresIn string) {
|
func (email *Emailer) formatExpiry(expiry time.Time, tzaware bool, datePattern, timePattern string) (d, t, expires_in string) {
|
||||||
d, _ = strtime.Strftime(expiry, datePattern)
|
d, _ = strtime.Strftime(expiry, datePattern)
|
||||||
t, _ = strtime.Strftime(expiry, timePattern)
|
t, _ = strtime.Strftime(expiry, timePattern)
|
||||||
currentTime := time.Now()
|
current_time := time.Now()
|
||||||
if tzaware {
|
if tzaware {
|
||||||
currentTime = currentTime.UTC()
|
current_time = current_time.UTC()
|
||||||
}
|
}
|
||||||
_, _, days, hours, minutes, _ := timeDiff(expiry, currentTime)
|
_, _, days, hours, minutes, _ := timeDiff(expiry, current_time)
|
||||||
if days != 0 {
|
if days != 0 {
|
||||||
expiresIn += fmt.Sprintf("%dd ", days)
|
expires_in += fmt.Sprintf("%dd ", days)
|
||||||
}
|
}
|
||||||
if hours != 0 {
|
if hours != 0 {
|
||||||
expiresIn += fmt.Sprintf("%dh ", hours)
|
expires_in += fmt.Sprintf("%dh ", hours)
|
||||||
}
|
}
|
||||||
if minutes != 0 {
|
if minutes != 0 {
|
||||||
expiresIn += fmt.Sprintf("%dm ", minutes)
|
expires_in += fmt.Sprintf("%dm ", minutes)
|
||||||
}
|
}
|
||||||
expiresIn = strings.TrimSuffix(expiresIn, " ")
|
expires_in = strings.TrimSuffix(expires_in, " ")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEmailer configures and returns a new emailer.
|
|
||||||
func NewEmailer(app *appContext) *Emailer {
|
func NewEmailer(app *appContext) *Emailer {
|
||||||
emailer := &Emailer{
|
emailer := &Emailer{
|
||||||
fromAddr: app.config.Section("email").Key("address").String(),
|
fromAddr: app.config.Section("email").Key("address").String(),
|
||||||
@ -120,7 +117,6 @@ func NewEmailer(app *appContext) *Emailer {
|
|||||||
return emailer
|
return emailer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMailgun returns a Mailgun emailClient.
|
|
||||||
func (emailer *Emailer) NewMailgun(url, key string) {
|
func (emailer *Emailer) NewMailgun(url, key string) {
|
||||||
sender := &Mailgun{
|
sender := &Mailgun{
|
||||||
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
client: mailgun.NewMailgun(strings.Split(emailer.fromAddr, "@")[1], key),
|
||||||
@ -134,14 +130,13 @@ func (emailer *Emailer) NewMailgun(url, key string) {
|
|||||||
emailer.sender = sender
|
emailer.sender = sender
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSMTP returns an SMTP emailClient.
|
func (emailer *Emailer) NewSMTP(server string, port int, password, host string, sslTls bool) {
|
||||||
func (emailer *Emailer) NewSMTP(server string, port int, password, host string, sslTLS bool) {
|
emailer.sender = &Smtp{
|
||||||
emailer.sender = &SMTP{
|
|
||||||
auth: smtp.PlainAuth("", emailer.fromAddr, password, host),
|
auth: smtp.PlainAuth("", emailer.fromAddr, password, host),
|
||||||
server: server,
|
server: server,
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
sslTLS: sslTLS,
|
sslTls: sslTls,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,10 +145,10 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
|||||||
subject: app.config.Section("invite_emails").Key("subject").String(),
|
subject: app.config.Section("invite_emails").Key("subject").String(),
|
||||||
}
|
}
|
||||||
expiry := invite.ValidTill
|
expiry := invite.ValidTill
|
||||||
d, t, expiresIn := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
d, t, expires_in := emailer.formatExpiry(expiry, false, app.datePattern, app.timePattern)
|
||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
inviteLink := app.config.Section("invite_emails").Key("url_base").String()
|
invite_link := app.config.Section("invite_emails").Key("url_base").String()
|
||||||
inviteLink = fmt.Sprintf("%s/%s", inviteLink, code)
|
invite_link = fmt.Sprintf("%s/%s", invite_link, code)
|
||||||
|
|
||||||
for _, key := range []string{"html", "text"} {
|
for _, key := range []string{"html", "text"} {
|
||||||
fpath := app.config.Section("invite_emails").Key("email_" + key).String()
|
fpath := app.config.Section("invite_emails").Key("email_" + key).String()
|
||||||
@ -165,8 +160,8 @@ func (emailer *Emailer) constructInvite(code string, invite Invite, app *appCont
|
|||||||
err = tpl.Execute(&tplData, map[string]string{
|
err = tpl.Execute(&tplData, map[string]string{
|
||||||
"expiry_date": d,
|
"expiry_date": d,
|
||||||
"expiry_time": t,
|
"expiry_time": t,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expires_in,
|
||||||
"invite_link": inviteLink,
|
"invite_link": invite_link,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -249,7 +244,7 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error)
|
|||||||
email := &Email{
|
email := &Email{
|
||||||
subject: app.config.Section("password_resets").Key("subject").MustString("Password reset - Jellyfin"),
|
subject: app.config.Section("password_resets").Key("subject").MustString("Password reset - Jellyfin"),
|
||||||
}
|
}
|
||||||
d, t, expiresIn := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
d, t, expires_in := emailer.formatExpiry(pwr.Expiry, true, app.datePattern, app.timePattern)
|
||||||
message := app.config.Section("email").Key("message").String()
|
message := app.config.Section("email").Key("message").String()
|
||||||
for _, key := range []string{"html", "text"} {
|
for _, key := range []string{"html", "text"} {
|
||||||
fpath := app.config.Section("password_resets").Key("email_" + key).String()
|
fpath := app.config.Section("password_resets").Key("email_" + key).String()
|
||||||
@ -262,7 +257,7 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error)
|
|||||||
"username": pwr.Username,
|
"username": pwr.Username,
|
||||||
"expiry_date": d,
|
"expiry_date": d,
|
||||||
"expiry_time": t,
|
"expiry_time": t,
|
||||||
"expires_in": expiresIn,
|
"expires_in": expires_in,
|
||||||
"pin": pwr.Pin,
|
"pin": pwr.Pin,
|
||||||
"message": message,
|
"message": message,
|
||||||
})
|
})
|
||||||
@ -278,7 +273,6 @@ func (emailer *Emailer) constructReset(pwr Pwr, app *appContext) (*Email, error)
|
|||||||
return email, nil
|
return email, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// calls the send method in the underlying emailClient.
|
|
||||||
func (emailer *Emailer) send(address string, email *Email) error {
|
func (emailer *Emailer) send(address string, email *Email) error {
|
||||||
return emailer.sender.send(address, emailer.fromName, emailer.fromAddr, email)
|
return emailer.sender.send(address, emailer.fromName, emailer.fromAddr, email)
|
||||||
}
|
}
|
||||||
|
6
ombi.go
6
ombi.go
@ -32,7 +32,6 @@ 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) {
|
func (ombi *Ombi) _getReader(url string, params map[string]string) (string, int, error) {
|
||||||
if ombi.key == "" {
|
if ombi.key == "" {
|
||||||
return "", 401, fmt.Errorf("No API key provided")
|
return "", 401, fmt.Errorf("No API key provided")
|
||||||
@ -71,7 +70,6 @@ func (ombi *Ombi) _getReader(url string, params map[string]string) (string, int,
|
|||||||
return buf.String(), resp.StatusCode, nil
|
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) {
|
func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool) (string, int, error) {
|
||||||
responseText := ""
|
responseText := ""
|
||||||
params, _ := json.Marshal(data)
|
params, _ := json.Marshal(data)
|
||||||
@ -107,14 +105,12 @@ func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool)
|
|||||||
return responseText, resp.StatusCode, nil
|
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) {
|
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)
|
resp, code, err := ombi._getReader(fmt.Sprintf("%s/api/v1/Identity/User/%s", ombi.server, id), nil)
|
||||||
json.Unmarshal([]byte(resp), &result)
|
json.Unmarshal([]byte(resp), &result)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets a list of all users.
|
|
||||||
func (ombi *Ombi) getUsers() (result []map[string]interface{}, code int, err error) {
|
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)
|
resp, code, err := ombi._getReader(fmt.Sprintf("%s/api/v1/Identity/Users", ombi.server), nil)
|
||||||
json.Unmarshal([]byte(resp), &result)
|
json.Unmarshal([]byte(resp), &result)
|
||||||
@ -133,7 +129,6 @@ var stripFromOmbi = []string{
|
|||||||
"userName",
|
"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) {
|
func (ombi *Ombi) templateByID(id string) (result map[string]interface{}, code int, err error) {
|
||||||
result, code, err = ombi.userByID(id)
|
result, code, err = ombi.userByID(id)
|
||||||
if err != nil || code != 200 {
|
if err != nil || code != 200 {
|
||||||
@ -152,7 +147,6 @@ func (ombi *Ombi) templateByID(id string) (result map[string]interface{}, code i
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new user.
|
|
||||||
func (ombi *Ombi) newUser(username, password, email string, template map[string]interface{}) ([]string, int, error) {
|
func (ombi *Ombi) newUser(username, password, email string, template map[string]interface{}) ([]string, int, error) {
|
||||||
url := fmt.Sprintf("%s/api/v1/Identity", ombi.server)
|
url := fmt.Sprintf("%s/api/v1/Identity", ombi.server)
|
||||||
user := template
|
user := template
|
||||||
|
@ -58,7 +58,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
app.info.Printf("New password reset for user \"%s\"", pwr.Username)
|
app.info.Printf("New password reset for user \"%s\"", pwr.Username)
|
||||||
if currentTime := time.Now(); pwr.Expiry.After(currentTime) {
|
if ct := time.Now(); pwr.Expiry.After(ct) {
|
||||||
user, status, err := app.jf.userByName(pwr.Username, false)
|
user, status, err := app.jf.userByName(pwr.Username, false)
|
||||||
if !(status == 200 || status == 204) || err != nil {
|
if !(status == 200 || status == 204) || err != nil {
|
||||||
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
|
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
|
||||||
|
@ -6,13 +6,6 @@ try:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
version = "git"
|
version = "git"
|
||||||
|
|
||||||
if version == "auto":
|
|
||||||
try:
|
|
||||||
version = subprocess.check_output("git describe --exact-match HEAD".split()).decode("utf-8").rstrip().replace('v', '')
|
|
||||||
except subprocess.CalledProcessError as e:
|
|
||||||
if e.returncode == 128:
|
|
||||||
version = "git"
|
|
||||||
|
|
||||||
commit = subprocess.check_output("git rev-parse --short HEAD".split()).decode("utf-8").rstrip()
|
commit = subprocess.check_output("git rev-parse --short HEAD".split()).decode("utf-8").rstrip()
|
||||||
|
|
||||||
file = f'package main; const VERSION = "{version}"; const COMMIT = "{commit}";'
|
file = f'package main; const VERSION = "{version}"; const COMMIT = "{commit}";'
|
||||||
|
Loading…
Reference in New Issue
Block a user