refactor; separate jfapi and ombi into modules

This commit is contained in:
Harvey Tindall 2020-11-02 00:53:08 +00:00
parent 0f4e77364b
commit c84ea17af4
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
12 changed files with 333 additions and 313 deletions

233
api.go
View File

@ -108,31 +108,33 @@ func (app *appContext) checkInvites() {
changed := false
for code, data := range app.storage.invites {
expiry := data.ValidTill
if current_time.After(expiry) {
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
notify := data.Notify
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
app.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify {
if settings["notify-expiry"] {
go func() {
msg, err := app.email.constructExpiry(code, data, app)
if err != nil {
app.err.Printf("%s: Failed to construct expiry notification", code)
app.debug.Printf("Error: %s", err)
} else if err := app.email.send(address, msg); err != nil {
app.err.Printf("%s: Failed to send expiry notification", code)
app.debug.Printf("Error: %s", err)
} else {
app.info.Printf("Sent expiry notification to %s", address)
}
}()
}
}
}
changed = true
delete(app.storage.invites, code)
if !current_time.After(expiry) {
continue
}
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
notify := data.Notify
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
app.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify {
if !settings["notify-expiry"] {
continue
}
go func() {
msg, err := app.email.constructExpiry(code, data, app)
if err != nil {
app.err.Printf("%s: Failed to construct expiry notification", code)
app.debug.Printf("Error: %s", err)
} else if err := app.email.send(address, msg); err != nil {
app.err.Printf("%s: Failed to send expiry notification", code)
app.debug.Printf("Error: %s", err)
} else {
app.info.Printf("Sent expiry notification to %s", address)
}
}()
}
}
changed = true
delete(app.storage.invites, code)
}
if changed {
app.storage.storeInvites()
@ -143,63 +145,64 @@ func (app *appContext) checkInvite(code string, used bool, username string) bool
current_time := time.Now()
app.storage.loadInvites()
changed := false
if inv, match := app.storage.invites[code]; match {
expiry := inv.ValidTill
if current_time.After(expiry) {
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
notify := inv.Notify
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
app.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify {
if settings["notify-expiry"] {
go func() {
msg, err := app.email.constructExpiry(code, inv, app)
if err != nil {
app.err.Printf("%s: Failed to construct expiry notification", code)
app.debug.Printf("Error: %s", err)
} else if err := app.email.send(address, msg); err != nil {
app.err.Printf("%s: Failed to send expiry notification", code)
app.debug.Printf("Error: %s", err)
} else {
app.info.Printf("Sent expiry notification to %s", address)
}
}()
}
inv, match := app.storage.invites[code]
if !match {
return false
}
expiry := inv.ValidTill
if current_time.After(expiry) {
app.debug.Printf("Housekeeping: Deleting old invite %s", code)
notify := inv.Notify
if app.config.Section("notifications").Key("enabled").MustBool(false) && len(notify) != 0 {
app.debug.Printf("%s: Expiry notification", code)
for address, settings := range notify {
if settings["notify-expiry"] {
go func() {
msg, err := app.email.constructExpiry(code, inv, app)
if err != nil {
app.err.Printf("%s: Failed to construct expiry notification", code)
app.debug.Printf("Error: %s", err)
} else if err := app.email.send(address, msg); err != nil {
app.err.Printf("%s: Failed to send expiry notification", code)
app.debug.Printf("Error: %s", err)
} else {
app.info.Printf("Sent expiry notification to %s", address)
}
}()
}
}
changed = true
match = false
}
changed = true
match = false
delete(app.storage.invites, code)
} else if used {
changed = true
del := false
newInv := inv
if newInv.RemainingUses == 1 {
del = true
delete(app.storage.invites, code)
} else if used {
changed = true
del := false
newInv := inv
if newInv.RemainingUses == 1 {
del = true
delete(app.storage.invites, code)
} else if newInv.RemainingUses != 0 {
// 0 means infinite i guess?
newInv.RemainingUses -= 1
}
newInv.UsedBy = append(newInv.UsedBy, []string{username, app.formatDatetime(current_time)})
if !del {
app.storage.invites[code] = newInv
}
} else if newInv.RemainingUses != 0 {
// 0 means infinite i guess?
newInv.RemainingUses -= 1
}
if changed {
app.storage.storeInvites()
newInv.UsedBy = append(newInv.UsedBy, []string{username, app.formatDatetime(current_time)})
if !del {
app.storage.invites[code] = newInv
}
return match
}
return false
if changed {
app.storage.storeInvites()
}
return match
}
func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, error) {
ombiUsers, code, err := app.ombi.getUsers()
ombiUsers, code, err := app.ombi.GetUsers()
if err != nil || code != 200 {
return nil, code, err
}
jfUser, code, err := app.jf.userById(jfID, false)
jfUser, code, err := app.jf.UserByID(jfID, false)
if err != nil || code != 200 {
return nil, code, err
}
@ -229,14 +232,14 @@ func (app *appContext) getOmbiUser(jfID string) (map[string]interface{}, int, er
func (app *appContext) NewUserAdmin(gc *gin.Context) {
var req newUserDTO
gc.BindJSON(&req)
existingUser, _, _ := app.jf.userByName(req.Username, false)
existingUser, _, _ := app.jf.UserByName(req.Username, false)
if existingUser != nil {
msg := fmt.Sprintf("User already exists named %s", req.Username)
app.info.Printf("%s New user failed: %s", req.Username, msg)
respond(401, msg, gc)
return
}
user, status, err := app.jf.newUser(req.Username, req.Password)
user, status, err := app.jf.NewUser(req.Username, req.Password)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("%s New user failed: Jellyfin responded with %d", req.Username, status)
respond(401, "Unknown error", gc)
@ -247,15 +250,15 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
id = user["Id"].(string)
}
if len(app.storage.policy) != 0 {
status, err = app.jf.setPolicy(id, app.storage.policy)
status, err = app.jf.SetPolicy(id, app.storage.policy)
if !(status == 200 || status == 204) {
app.err.Printf("%s: Failed to set user policy: Code %d", req.Username, status)
}
}
if len(app.storage.configuration) != 0 && len(app.storage.displayprefs) != 0 {
status, err = app.jf.setConfiguration(id, app.storage.configuration)
status, err = app.jf.SetConfiguration(id, app.storage.configuration)
if (status == 200 || status == 204) && err == nil {
status, err = app.jf.setDisplayPreferences(id, app.storage.displayprefs)
status, err = app.jf.SetDisplayPreferences(id, app.storage.displayprefs)
} else {
app.err.Printf("%s: Failed to set configuration template: Code %d", req.Username, status)
}
@ -267,7 +270,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
if app.config.Section("ombi").Key("enabled").MustBool(false) {
app.storage.loadOmbiTemplate()
if len(app.storage.ombi_template) != 0 {
errors, code, err := app.ombi.newUser(req.Username, req.Password, req.Email, app.storage.ombi_template)
errors, code, err := app.ombi.NewUser(req.Username, req.Password, req.Email, app.storage.ombi_template)
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, ", "))
@ -276,7 +279,7 @@ func (app *appContext) NewUserAdmin(gc *gin.Context) {
}
}
}
app.jf.cacheExpiry = time.Now()
app.jf.CacheExpiry = time.Now()
}
// @Summary Creates a new Jellyfin user via invite code
@ -309,14 +312,14 @@ func (app *appContext) NewUser(gc *gin.Context) {
gc.Abort()
return
}
existingUser, _, _ := app.jf.userByName(req.Username, false)
existingUser, _, _ := app.jf.UserByName(req.Username, false)
if existingUser != nil {
msg := fmt.Sprintf("User already exists named %s", req.Username)
app.info.Printf("%s New user failed: %s", req.Code, msg)
respond(401, msg, gc)
return
}
user, status, err := app.jf.newUser(req.Username, req.Password)
user, status, err := app.jf.NewUser(req.Username, req.Password)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("%s New user failed: Jellyfin responded with %d", req.Code, status)
respond(401, "Unknown error", gc)
@ -355,16 +358,16 @@ func (app *appContext) NewUser(gc *gin.Context) {
}
if len(profile.Policy) != 0 {
app.debug.Printf("Applying policy from profile \"%s\"", invite.Profile)
status, err = app.jf.setPolicy(id, profile.Policy)
status, err = app.jf.SetPolicy(id, profile.Policy)
if !(status == 200 || status == 204) {
app.err.Printf("%s: Failed to set user policy: Code %d", req.Code, status)
}
}
if len(profile.Configuration) != 0 && len(profile.Displayprefs) != 0 {
app.debug.Printf("Applying homescreen from profile \"%s\"", invite.Profile)
status, err = app.jf.setConfiguration(id, profile.Configuration)
status, err = app.jf.SetConfiguration(id, profile.Configuration)
if (status == 200 || status == 204) && err == nil {
status, err = app.jf.setDisplayPreferences(id, profile.Displayprefs)
status, err = app.jf.SetDisplayPreferences(id, profile.Displayprefs)
} else {
app.err.Printf("%s: Failed to set configuration template: Code %d", req.Code, status)
}
@ -377,7 +380,7 @@ func (app *appContext) NewUser(gc *gin.Context) {
if app.config.Section("ombi").Key("enabled").MustBool(false) {
app.storage.loadOmbiTemplate()
if len(app.storage.ombi_template) != 0 {
errors, code, err := app.ombi.newUser(req.Username, req.Password, req.Email, app.storage.ombi_template)
errors, code, err := app.ombi.NewUser(req.Username, req.Password, req.Email, app.storage.ombi_template)
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, ", "))
@ -414,7 +417,7 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
ombiUser, code, err := app.getOmbiUser(userID)
if code == 200 && err == nil {
if id, ok := ombiUser["id"]; ok {
status, err := app.ombi.deleteUser(id.(string))
status, err := app.ombi.DeleteUser(id.(string))
if err != nil || status != 200 {
app.err.Printf("Failed to delete ombi user: %d %s", status, err)
errors[userID] = fmt.Sprintf("Ombi: %d %s, ", status, err)
@ -422,7 +425,7 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
}
}
}
status, err := app.jf.deleteUser(userID)
status, err := app.jf.DeleteUser(userID)
if !(status == 200 || status == 204) || err != nil {
msg := fmt.Sprintf("%d: %s", status, err)
if _, ok := errors[userID]; !ok {
@ -449,7 +452,7 @@ func (app *appContext) DeleteUser(gc *gin.Context) {
}
}
}
app.jf.cacheExpiry = time.Now()
app.jf.CacheExpiry = time.Now()
if len(errors) == len(req.Users) {
respondBool(500, false, gc)
app.err.Printf("Account deletion failed: %s", errors[req.Users[0]])
@ -612,7 +615,7 @@ func (app *appContext) CreateProfile(gc *gin.Context) {
app.info.Println("Profile creation requested")
var req newProfileDTO
gc.BindJSON(&req)
user, status, err := app.jf.userById(req.ID, false)
user, status, err := app.jf.UserByID(req.ID, false)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get user from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -626,7 +629,7 @@ func (app *appContext) CreateProfile(gc *gin.Context) {
app.debug.Printf("Creating profile from user \"%s\"", user["Name"].(string))
if req.Homescreen {
profile.Configuration = user["Configuration"].(map[string]interface{})
profile.Displayprefs, status, err = app.jf.getDisplayPreferences(req.ID)
profile.Displayprefs, status, err = app.jf.GetDisplayPreferences(req.ID)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get DisplayPrefs: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -844,7 +847,7 @@ func (app *appContext) GetUsers(gc *gin.Context) {
app.debug.Println("Users requested")
var resp getUsersDTO
resp.UserList = []respUser{}
users, status, err := app.jf.getUsers(false)
users, status, err := app.jf.GetUsers(false)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -879,7 +882,7 @@ func (app *appContext) GetUsers(gc *gin.Context) {
// @tags Ombi
func (app *appContext) OmbiUsers(gc *gin.Context) {
app.debug.Println("Ombi users requested")
users, status, err := app.ombi.getUsers()
users, status, err := app.ombi.GetUsers()
if err != nil || status != 200 {
app.err.Printf("Failed to get users from Ombi: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -907,7 +910,7 @@ func (app *appContext) OmbiUsers(gc *gin.Context) {
func (app *appContext) SetOmbiDefaults(gc *gin.Context) {
var req ombiUser
gc.BindJSON(&req)
template, code, err := app.ombi.templateByID(req.ID)
template, code, err := app.ombi.TemplateByID(req.ID)
if err != nil || code != 200 || len(template) == 0 {
app.err.Printf("Couldn't get user from Ombi: %d %s", code, err)
respond(500, "Couldn't get user", gc)
@ -930,7 +933,7 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
var req modifyEmailsDTO
gc.BindJSON(&req)
app.debug.Println("Email modification requested")
users, status, err := app.jf.getUsers(false)
users, status, err := app.jf.GetUsers(false)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -946,7 +949,7 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
ombiUser, code, err := app.getOmbiUser(id)
if code == 200 && err == nil {
ombiUser["emailAddress"] = address
code, err = app.ombi.modifyUser(ombiUser)
code, err = app.ombi.ModifyUser(ombiUser)
if code != 200 || err != nil {
app.err.Printf("%s: Failed to change ombi email address: %d %s", ombiUser["userName"].(string), code, err)
}
@ -959,42 +962,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
respondBool(200, true, gc)
}
/*func (app *appContext) SetDefaults(gc *gin.Context) {
var req defaultsReq
gc.BindJSON(&req)
userID := req.ID
user, status, err := app.jf.userById(userID, false)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get user from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)
respond(500, "Couldn't get user", gc)
return
}
app.info.Printf("Getting user defaults from \"%s\"", user["Name"].(string))
policy := user["Policy"].(map[string]interface{})
app.storage.policy = policy
app.storage.storePolicy()
app.debug.Println("User policy template stored")
if req.Homescreen {
configuration := user["Configuration"].(map[string]interface{})
var displayprefs map[string]interface{}
displayprefs, status, err = app.jf.getDisplayPreferences(userID)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get DisplayPrefs: Code %d", status)
app.debug.Printf("Error: %s", err)
respond(500, "Couldn't get displayprefs", gc)
return
}
app.storage.configuration = configuration
app.storage.displayprefs = displayprefs
app.storage.storeConfiguration()
app.debug.Println("Configuration template stored")
app.storage.storeDisplayprefs()
app.debug.Println("DisplayPrefs template stored")
}
gc.JSON(200, map[string]bool{"success": true})
}*/
// @Summary Apply settings to a list of users, either from a profile or from another user.
// @Produce json
// @Param userSettingsDTO body userSettingsDTO true "Parameters for applying settings"
@ -1028,7 +995,7 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
policy = app.storage.profiles[req.Profile].Policy
} else if req.From == "user" {
applyingFrom = "user"
user, status, err := app.jf.userById(req.ID, false)
user, status, err := app.jf.UserByID(req.ID, false)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get user from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -1038,7 +1005,7 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
applyingFrom = "\"" + user["Name"].(string) + "\""
policy = user["Policy"].(map[string]interface{})
if req.Homescreen {
displayprefs, status, err = app.jf.getDisplayPreferences(req.ID)
displayprefs, status, err = app.jf.GetDisplayPreferences(req.ID)
if !(status == 200 || status == 204) || err != nil {
app.err.Printf("Failed to get DisplayPrefs: Code %d", status)
app.debug.Printf("Error: %s", err)
@ -1054,17 +1021,17 @@ func (app *appContext) ApplySettings(gc *gin.Context) {
"homescreen": map[string]string{},
}
for _, id := range req.ApplyTo {
status, err := app.jf.setPolicy(id, policy)
status, err := app.jf.SetPolicy(id, policy)
if !(status == 200 || status == 204) || err != nil {
errors["policy"][id] = fmt.Sprintf("%d: %s", status, err)
}
if req.Homescreen {
status, err = app.jf.setConfiguration(id, configuration)
status, err = app.jf.SetConfiguration(id, configuration)
errorString := ""
if !(status == 200 || status == 204) || err != nil {
errorString += fmt.Sprintf("Configuration %d: %s ", status, err)
} else {
status, err = app.jf.setDisplayPreferences(id, displayprefs)
status, err = app.jf.SetDisplayPreferences(id, displayprefs)
if !(status == 200 || status == 204) || err != nil {
errorString += fmt.Sprintf("Displayprefs %d: %s ", status, err)
}

View File

@ -146,7 +146,7 @@ func (app *appContext) getToken(gc *gin.Context) {
var status int
var err error
var user map[string]interface{}
user, status, err = app.authJf.authenticate(creds[0], creds[1])
user, status, err = app.authJf.Authenticate(creds[0], creds[1])
if status != 200 || err != nil {
if status == 401 || status == 400 {
app.info.Println("Auth denied: Invalid username/password (Jellyfin)")

21
common/common.go Normal file
View File

@ -0,0 +1,21 @@
package common
import (
"fmt"
"log"
)
// TimeoutHandler recovers from an http timeout.
type TimeoutHandler func()
// NewTimeoutHandler returns a new Timeout handler.
func NewTimeoutHandler(name, addr string, noFail bool) TimeoutHandler {
return func() {
out := fmt.Sprintf("Failed to authenticate with %s @ %s: Timed out", name, addr)
if noFail {
log.Print(out)
} else {
log.Fatalf(out)
}
}
}

3
common/go.mod Normal file
View File

@ -0,0 +1,3 @@
module github.com/hrfee/jfa-go/common
go 1.15

9
go.mod
View File

@ -4,6 +4,12 @@ go 1.14
replace github.com/hrfee/jfa-go/docs => ./docs
replace github.com/hrfee/jfa-go/jfapi => ./jfapi
replace github.com/hrfee/jfa-go/common => ./common
replace github.com/hrfee/jfa-go/ombi => ./ombi
require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
@ -17,7 +23,10 @@ require (
github.com/go-playground/validator/v10 v10.4.0 // indirect
github.com/golang/protobuf v1.4.2
github.com/google/uuid v1.1.2 // indirect
github.com/hrfee/jfa-go/common v0.0.0-00010101000000-000000000000
github.com/hrfee/jfa-go/docs v0.0.0-20200927200337-7628e5d71da8
github.com/hrfee/jfa-go/jfapi v0.0.0-00010101000000-000000000000
github.com/hrfee/jfa-go/ombi v0.0.0-00010101000000-000000000000
github.com/jordan-wright/email v4.0.1-0.20200917010138-e1c00e156980+incompatible
github.com/json-iterator/go v1.1.10 // indirect
github.com/knz/strtime v0.0.0-20200924090105-187c67f2bf5e

7
jfapi/go.mod Normal file
View File

@ -0,0 +1,7 @@
module github.com/hrfee/jfa-go/jfapi
go 1.15
replace github.com/hrfee/jfa-go/common => ../common
require github.com/hrfee/jfa-go/common v0.0.0-00010101000000-000000000000

View File

@ -1,4 +1,4 @@
package main
package jfapi
import (
"bytes"
@ -7,63 +7,57 @@ import (
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
"time"
"github.com/hrfee/jfa-go/common"
)
type ServerInfo struct {
type serverInfo struct {
LocalAddress string `json:"LocalAddress"`
Name string `json:"ServerName"`
Version string `json:"Version"`
Os string `json:"OperatingSystem"`
Id string `json:"Id"`
OS string `json:"OperatingSystem"`
ID string `json:"Id"`
}
// Jellyfin represents a running Jellyfin instance.
type Jellyfin struct {
server string
client string
version string
device string
deviceId string
useragent string
auth string
header map[string]string
serverInfo ServerInfo
username string
password string
authenticated bool
accessToken string
userId string
httpClient *http.Client
loginParams map[string]string
userCache []map[string]interface{}
cacheExpiry time.Time
cacheLength int
noFail bool
Server string
client string
version string
device string
deviceID string
useragent string
auth string
header map[string]string
ServerInfo serverInfo
Username string
password string
Authenticated bool
AccessToken string
userID string
httpClient *http.Client
loginParams map[string]string
userCache []map[string]interface{}
CacheExpiry time.Time
cacheLength int
noFail bool
timeoutHandler common.TimeoutHandler
}
func timeoutHandler(name, addr string, noFail bool) {
if r := recover(); r != nil {
out := fmt.Sprintf("Failed to authenticate with %s @ %s: Timed out", name, addr)
if noFail {
log.Printf(out)
} else {
log.Fatalf(out)
}
}
}
func newJellyfin(server, client, version, device, deviceId string) (*Jellyfin, error) {
// NewJellyfin returns a new Jellyfin object.
func NewJellyfin(server, client, version, device, deviceID string, timeoutHandler common.TimeoutHandler) (*Jellyfin, error) {
jf := &Jellyfin{}
jf.server = server
jf.Server = server
jf.client = client
jf.version = version
jf.device = device
jf.deviceId = deviceId
jf.deviceID = deviceID
jf.useragent = fmt.Sprintf("%s/%s", client, version)
jf.auth = fmt.Sprintf("MediaBrowser Client=%s, Device=%s, DeviceId=%s, Version=%s", client, device, deviceId, version)
jf.timeoutHandler = timeoutHandler
jf.auth = fmt.Sprintf("MediaBrowser Client=%s, Device=%s, DeviceId=%s, Version=%s", client, device, deviceID, version)
jf.header = map[string]string{
"Accept": "application/json",
"Content-type": "application/json; charset=UTF-8",
@ -76,21 +70,22 @@ func newJellyfin(server, client, version, device, deviceId string) (*Jellyfin, e
jf.httpClient = &http.Client{
Timeout: 10 * time.Second,
}
infoUrl := fmt.Sprintf("%s/System/Info/Public", server)
req, _ := http.NewRequest("GET", infoUrl, nil)
infoURL := fmt.Sprintf("%s/System/Info/Public", server)
req, _ := http.NewRequest("GET", infoURL, nil)
resp, err := jf.httpClient.Do(req)
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
defer jf.timeoutHandler()
if err == nil {
data, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(data, &jf.serverInfo)
json.Unmarshal(data, &jf.ServerInfo)
}
jf.cacheLength = 30
jf.cacheExpiry = time.Now()
jf.CacheExpiry = time.Now()
return jf, nil
}
func (jf *Jellyfin) authenticate(username, password string) (map[string]interface{}, int, error) {
jf.username = username
// Authenticate attempts to authenticate using a username & password
func (jf *Jellyfin) Authenticate(username, password string) (map[string]interface{}, int, error) {
jf.Username = username
jf.password = password
jf.loginParams = map[string]string{
"Username": username,
@ -105,9 +100,9 @@ func (jf *Jellyfin) authenticate(username, password string) (map[string]interfac
return nil, 0, err
}
// loginParams, _ := json.Marshal(jf.loginParams)
url := fmt.Sprintf("%s/Users/authenticatebyname", jf.server)
url := fmt.Sprintf("%s/Users/authenticatebyname", jf.Server)
req, err := http.NewRequest("POST", url, buffer)
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
defer jf.timeoutHandler()
if err != nil {
return nil, 0, err
}
@ -128,16 +123,16 @@ func (jf *Jellyfin) authenticate(username, password string) (map[string]interfac
}
var respData map[string]interface{}
json.NewDecoder(data).Decode(&respData)
jf.accessToken = respData["AccessToken"].(string)
jf.AccessToken = respData["AccessToken"].(string)
user := respData["User"].(map[string]interface{})
jf.userId = respData["User"].(map[string]interface{})["Id"].(string)
jf.auth = fmt.Sprintf("MediaBrowser Client=\"%s\", Device=\"%s\", DeviceId=\"%s\", Version=\"%s\", Token=\"%s\"", jf.client, jf.device, jf.deviceId, jf.version, jf.accessToken)
jf.userID = respData["User"].(map[string]interface{})["Id"].(string)
jf.auth = fmt.Sprintf("MediaBrowser Client=\"%s\", Device=\"%s\", DeviceId=\"%s\", Version=\"%s\", Token=\"%s\"", jf.client, jf.device, jf.deviceID, jf.version, jf.AccessToken)
jf.header["X-Emby-Authorization"] = jf.auth
jf.authenticated = true
jf.Authenticated = true
return user, resp.StatusCode, nil
}
func (jf *Jellyfin) _get(url string, params map[string]string) (string, int, error) {
func (jf *Jellyfin) get(url string, params map[string]string) (string, int, error) {
var req *http.Request
if params != nil {
jsonParams, _ := json.Marshal(params)
@ -149,13 +144,13 @@ func (jf *Jellyfin) _get(url string, params map[string]string) (string, int, err
req.Header.Add(name, value)
}
resp, err := jf.httpClient.Do(req)
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
defer jf.timeoutHandler()
if err != nil || resp.StatusCode != 200 {
if resp.StatusCode == 401 && jf.authenticated {
jf.authenticated = false
_, _, authErr := jf.authenticate(jf.username, jf.password)
if resp.StatusCode == 401 && jf.Authenticated {
jf.Authenticated = false
_, _, authErr := jf.Authenticate(jf.Username, jf.password)
if authErr == nil {
v1, v2, v3 := jf._get(url, params)
v1, v2, v3 := jf.get(url, params)
return v1, v2, v3
}
}
@ -164,9 +159,6 @@ func (jf *Jellyfin) _get(url string, params map[string]string) (string, int, err
defer resp.Body.Close()
var data io.Reader
encoding := resp.Header.Get("Content-Encoding")
if TEST {
fmt.Println("response encoding:", encoding)
}
switch encoding {
case "gzip":
data, _ = gzip.NewReader(resp.Body)
@ -180,20 +172,20 @@ func (jf *Jellyfin) _get(url string, params map[string]string) (string, int, err
return buf.String(), resp.StatusCode, nil
}
func (jf *Jellyfin) _post(url string, data map[string]interface{}, response bool) (string, int, error) {
func (jf *Jellyfin) post(url string, data map[string]interface{}, response bool) (string, int, error) {
params, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(params))
for name, value := range jf.header {
req.Header.Add(name, value)
}
resp, err := jf.httpClient.Do(req)
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
defer jf.timeoutHandler()
if err != nil || resp.StatusCode != 200 {
if resp.StatusCode == 401 && jf.authenticated {
jf.authenticated = false
_, _, authErr := jf.authenticate(jf.username, jf.password)
if resp.StatusCode == 401 && jf.Authenticated {
jf.Authenticated = false
_, _, authErr := jf.Authenticate(jf.Username, jf.password)
if authErr == nil {
v1, v2, v3 := jf._post(url, data, response)
v1, v2, v3 := jf.post(url, data, response)
return v1, v2, v3
}
}
@ -215,45 +207,48 @@ func (jf *Jellyfin) _post(url string, data map[string]interface{}, response bool
return "", resp.StatusCode, nil
}
func (jf *Jellyfin) deleteUser(id string) (int, error) {
url := fmt.Sprintf("%s/Users/%s", jf.server, id)
// DeleteUser deletes the user corresponding to the provided ID.
func (jf *Jellyfin) DeleteUser(id string) (int, error) {
url := fmt.Sprintf("%s/Users/%s", jf.Server, id)
req, _ := http.NewRequest("DELETE", url, nil)
for name, value := range jf.header {
req.Header.Add(name, value)
}
resp, err := jf.httpClient.Do(req)
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
defer jf.timeoutHandler()
return resp.StatusCode, err
}
func (jf *Jellyfin) getUsers(public bool) ([]map[string]interface{}, int, error) {
// GetUsers returns all (visible) users on the Jellyfin instance.
func (jf *Jellyfin) GetUsers(public bool) ([]map[string]interface{}, int, error) {
var result []map[string]interface{}
var data string
var status int
var err error
if time.Now().After(jf.cacheExpiry) {
if time.Now().After(jf.CacheExpiry) {
if public {
url := fmt.Sprintf("%s/users/public", jf.server)
data, status, err = jf._get(url, nil)
url := fmt.Sprintf("%s/users/public", jf.Server)
data, status, err = jf.get(url, nil)
} else {
url := fmt.Sprintf("%s/users", jf.server)
data, status, err = jf._get(url, jf.loginParams)
url := fmt.Sprintf("%s/users", jf.Server)
data, status, err = jf.get(url, jf.loginParams)
}
if err != nil || status != 200 {
return nil, status, err
}
json.Unmarshal([]byte(data), &result)
jf.userCache = result
jf.cacheExpiry = time.Now().Add(time.Minute * time.Duration(jf.cacheLength))
jf.CacheExpiry = time.Now().Add(time.Minute * time.Duration(jf.cacheLength))
return result, status, nil
}
return jf.userCache, 200, nil
}
func (jf *Jellyfin) userByName(username string, public bool) (map[string]interface{}, int, error) {
// UserByName returns the user corresponding to the provided username.
func (jf *Jellyfin) UserByName(username string, public bool) (map[string]interface{}, int, error) {
var match map[string]interface{}
find := func() (map[string]interface{}, int, error) {
users, status, err := jf.getUsers(public)
users, status, err := jf.GetUsers(public)
if err != nil || status != 200 {
return nil, status, err
}
@ -266,48 +261,49 @@ func (jf *Jellyfin) userByName(username string, public bool) (map[string]interfa
}
match, status, err := find()
if match == nil {
jf.cacheExpiry = time.Now()
jf.CacheExpiry = time.Now()
match, status, err = find()
}
return match, status, err
}
func (jf *Jellyfin) userById(userId string, public bool) (map[string]interface{}, int, error) {
if jf.cacheExpiry.After(time.Now()) {
// UserByID returns the user corresponding to the provided ID.
func (jf *Jellyfin) UserByID(userID string, public bool) (map[string]interface{}, int, error) {
if jf.CacheExpiry.After(time.Now()) {
for _, user := range jf.userCache {
if user["Id"].(string) == userId {
if user["Id"].(string) == userID {
return user, 200, nil
}
}
}
if public {
users, status, err := jf.getUsers(public)
users, status, err := jf.GetUsers(public)
if err != nil || status != 200 {
return nil, status, err
}
for _, user := range users {
if user["Id"].(string) == userId {
if user["Id"].(string) == userID {
return user, status, nil
}
}
return nil, status, err
} else {
var result map[string]interface{}
var data string
var status int
var err error
url := fmt.Sprintf("%s/users/%s", jf.server, userId)
data, status, err = jf._get(url, jf.loginParams)
if err != nil || status != 200 {
return nil, status, err
}
json.Unmarshal([]byte(data), &result)
return result, status, nil
}
var result map[string]interface{}
var data string
var status int
var err error
url := fmt.Sprintf("%s/users/%s", jf.Server, userID)
data, status, err = jf.get(url, jf.loginParams)
if err != nil || status != 200 {
return nil, status, err
}
json.Unmarshal([]byte(data), &result)
return result, status, nil
}
func (jf *Jellyfin) newUser(username, password string) (map[string]interface{}, int, error) {
url := fmt.Sprintf("%s/Users/New", jf.server)
// NewUser creates a new user with the provided username and password.
func (jf *Jellyfin) NewUser(username, password string) (map[string]interface{}, int, error) {
url := fmt.Sprintf("%s/Users/New", jf.Server)
stringData := map[string]string{
"Name": username,
"Password": password,
@ -316,7 +312,7 @@ func (jf *Jellyfin) newUser(username, password string) (map[string]interface{},
for key, value := range stringData {
data[key] = value
}
response, status, err := jf._post(url, data, true)
response, status, err := jf.post(url, data, true)
var recv map[string]interface{}
json.Unmarshal([]byte(response), &recv)
if err != nil || !(status == 200 || status == 204) {
@ -325,24 +321,27 @@ func (jf *Jellyfin) newUser(username, password string) (map[string]interface{},
return recv, status, nil
}
func (jf *Jellyfin) setPolicy(userId string, policy map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/Users/%s/Policy", jf.server, userId)
_, status, err := jf._post(url, policy, false)
// SetPolicy sets the access policy for the user corresponding to the provided ID.
func (jf *Jellyfin) SetPolicy(userID string, policy map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/Users/%s/Policy", jf.Server, userID)
_, status, err := jf.post(url, policy, false)
if err != nil || status != 200 {
return status, err
}
return status, nil
}
func (jf *Jellyfin) setConfiguration(userId string, configuration map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/Users/%s/Configuration", jf.server, userId)
_, status, err := jf._post(url, configuration, false)
// SetConfiguration sets the configuration (part of homescreen layout) for the user corresponding to the provided ID.
func (jf *Jellyfin) SetConfiguration(userID string, configuration map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/Users/%s/Configuration", jf.Server, userID)
_, status, err := jf.post(url, configuration, false)
return status, err
}
func (jf *Jellyfin) getDisplayPreferences(userId string) (map[string]interface{}, int, error) {
url := fmt.Sprintf("%s/DisplayPreferences/usersettings?userId=%s&client=emby", jf.server, userId)
data, status, err := jf._get(url, nil)
// GetDisplayPreferences gets the displayPreferences (part of homescreen layout) for the user corresponding to the provided ID.
func (jf *Jellyfin) GetDisplayPreferences(userID string) (map[string]interface{}, int, error) {
url := fmt.Sprintf("%s/DisplayPreferences/usersettings?userId=%s&client=emby", jf.Server, userID)
data, status, err := jf.get(url, nil)
if err != nil || !(status == 204 || status == 200) {
return nil, status, err
}
@ -354,9 +353,10 @@ func (jf *Jellyfin) getDisplayPreferences(userId string) (map[string]interface{}
return displayprefs, status, nil
}
func (jf *Jellyfin) setDisplayPreferences(userId string, displayprefs map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/DisplayPreferences/usersettings?userId=%s&client=emby", jf.server, userId)
_, status, err := jf._post(url, displayprefs, false)
// SetDisplayPreferences sets the displayPreferences (part of homescreen layout) for the user corresponding to the provided ID.
func (jf *Jellyfin) SetDisplayPreferences(userID string, displayprefs map[string]interface{}) (int, error) {
url := fmt.Sprintf("%s/DisplayPreferences/usersettings?userId=%s&client=emby", jf.Server, userID)
_, status, err := jf.post(url, displayprefs, false)
if err != nil || !(status == 204 || status == 200) {
return status, err
}

53
main.go
View File

@ -23,7 +23,10 @@ import (
"github.com/gin-contrib/pprof"
"github.com/gin-contrib/static"
"github.com/gin-gonic/gin"
"github.com/hrfee/jfa-go/common"
_ "github.com/hrfee/jfa-go/docs"
"github.com/hrfee/jfa-go/jfapi"
"github.com/hrfee/jfa-go/ombi"
"github.com/lithammer/shortuuid/v3"
"github.com/logrusorgru/aurora/v3"
swaggerFiles "github.com/swaggo/files"
@ -51,9 +54,9 @@ type appContext struct {
jellyfinLogin bool
users []User
invalidTokens []string
jf *Jellyfin
authJf *Jellyfin
ombi *Ombi
jf *jfapi.Jellyfin
authJf *jfapi.Jellyfin
ombi *ombi.Ombi
datePattern string
timePattern string
storage Storage
@ -139,18 +142,18 @@ var (
func test(app *appContext) {
fmt.Printf("\n\n----\n\n")
settings := map[string]interface{}{
"server": app.jf.server,
"server version": app.jf.serverInfo.Version,
"server name": app.jf.serverInfo.Name,
"authenticated?": app.jf.authenticated,
"access token": app.jf.accessToken,
"username": app.jf.username,
"server": app.jf.Server,
"server version": app.jf.ServerInfo.Version,
"server name": app.jf.ServerInfo.Name,
"authenticated?": app.jf.Authenticated,
"access token": app.jf.AccessToken,
"username": app.jf.Username,
}
for n, v := range settings {
fmt.Println(n, ":", v)
}
users, status, err := app.jf.getUsers(false)
fmt.Printf("getUsers: code %d err %s maplength %d\n", status, err, len(users))
users, status, err := app.jf.GetUsers(false)
fmt.Printf("GetUsers: code %d err %s maplength %d\n", status, err, len(users))
fmt.Printf("View output? [y/n]: ")
var choice string
fmt.Scanln(&choice)
@ -161,8 +164,8 @@ func test(app *appContext) {
fmt.Printf("Enter a user to grab: ")
var username string
fmt.Scanln(&username)
user, status, err := app.jf.userByName(username, false)
fmt.Printf("userByName (%s): code %d err %s", username, status, err)
user, status, err := app.jf.UserByName(username, false)
fmt.Printf("UserByName (%s): code %d err %s", username, status, err)
out, err := json.MarshalIndent(user, "", " ")
fmt.Print(string(out))
}
@ -358,19 +361,14 @@ func start(asDaemon, firstCall bool) {
app.debug.Println("Loading storage")
// app.storage.invite_path = filepath.Join(app.data_path, "invites.json")
app.storage.invite_path = app.config.Section("files").Key("invites").String()
app.storage.loadInvites()
// app.storage.emails_path = filepath.Join(app.data_path, "emails.json")
app.storage.emails_path = app.config.Section("files").Key("emails").String()
app.storage.loadEmails()
// app.storage.policy_path = filepath.Join(app.data_path, "user_template.json")
app.storage.policy_path = app.config.Section("files").Key("user_template").String()
app.storage.loadPolicy()
// app.storage.configuration_path = filepath.Join(app.data_path, "user_configuration.json")
app.storage.configuration_path = app.config.Section("files").Key("user_configuration").String()
app.storage.loadConfiguration()
// app.storage.displayprefs_path = filepath.Join(app.data_path, "user_displayprefs.json")
app.storage.displayprefs_path = app.config.Section("files").Key("user_displayprefs").String()
app.storage.loadDisplayprefs()
@ -397,16 +395,18 @@ func start(asDaemon, firstCall bool) {
if app.config.Section("ombi").Key("enabled").MustBool(false) {
app.storage.ombi_path = app.config.Section("files").Key("ombi_template").String()
app.storage.loadOmbiTemplate()
app.ombi = newOmbi(
app.config.Section("ombi").Key("server").String(),
ombiServer := app.config.Section("ombi").Key("server").String()
app.ombi = ombi.NewOmbi(
ombiServer,
app.config.Section("ombi").Key("api_key").String(),
true,
common.NewTimeoutHandler("Ombi", ombiServer, true),
)
}
app.configBase_path = filepath.Join(app.local_path, "config-base.json")
config_base, _ := ioutil.ReadFile(app.configBase_path)
json.Unmarshal(config_base, &app.configBase)
configBase, _ := ioutil.ReadFile(app.configBase_path)
json.Unmarshal(configBase, &app.configBase)
themes := map[string]string{
"Jellyfin (Dark)": fmt.Sprintf("bs%d-jf.css", app.bsVersion),
@ -435,20 +435,21 @@ func start(asDaemon, firstCall bool) {
}
server := app.config.Section("jellyfin").Key("server").String()
app.jf, _ = newJellyfin(
app.jf, _ = jfapi.NewJellyfin(
server,
app.config.Section("jellyfin").Key("client").String(),
app.config.Section("jellyfin").Key("version").String(),
app.config.Section("jellyfin").Key("device").String(),
app.config.Section("jellyfin").Key("device_id").String(),
common.NewTimeoutHandler("Jellyfin", server, true),
)
var status int
_, status, err = app.jf.authenticate(app.config.Section("jellyfin").Key("username").String(), app.config.Section("jellyfin").Key("password").String())
_, status, err = app.jf.Authenticate(app.config.Section("jellyfin").Key("username").String(), app.config.Section("jellyfin").Key("password").String())
if status != 200 || err != nil {
app.err.Fatalf("Failed to authenticate with Jellyfin @ %s: Code %d", server, status)
}
app.info.Printf("Authenticated with %s", server)
app.authJf, _ = newJellyfin(server, "jfa-go", app.version, "auth", "auth")
app.authJf, _ = jfapi.NewJellyfin(server, "jfa-go", app.version, "auth", "auth", common.NewTimeoutHandler("Jellyfin", server, true))
app.loadStrftime()

5
ombi/go.mod Normal file
View File

@ -0,0 +1,5 @@
module github.com/hrfee/jfa-go/ombi
replace github.com/hrfee/jfa-go/common => ../common
go 1.15

View File

@ -1,4 +1,4 @@
package main
package ombi
import (
"bytes"
@ -9,36 +9,40 @@ import (
"net/http"
"strings"
"time"
"github.com/hrfee/jfa-go/common"
)
// Ombi represents a running Ombi instance.
type Ombi struct {
server, key string
header map[string]string
httpClient *http.Client
noFail bool
userCache []map[string]interface{}
cacheExpiry time.Time
cacheLength int
server, key string
header map[string]string
httpClient *http.Client
userCache []map[string]interface{}
cacheExpiry time.Time
cacheLength int
timeoutHandler common.TimeoutHandler
}
func newOmbi(server, key string, noFail bool) *Ombi {
// NewOmbi returns an Ombi object.
func NewOmbi(server, key string, timeoutHandler common.TimeoutHandler) *Ombi {
return &Ombi{
server: server,
key: key,
noFail: noFail,
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
header: map[string]string{
"ApiKey": key,
},
cacheLength: 30,
cacheExpiry: time.Now(),
cacheLength: 30,
cacheExpiry: time.Now(),
timeoutHandler: timeoutHandler,
}
}
// does a GET and returns the response as a string.
func (ombi *Ombi) _getJSON(url string, params map[string]string) (string, int, error) {
func (ombi *Ombi) getJSON(url string, params map[string]string) (string, int, error) {
if ombi.key == "" {
return "", 401, fmt.Errorf("No API key provided")
}
@ -53,7 +57,7 @@ func (ombi *Ombi) _getJSON(url string, params map[string]string) (string, int, e
req.Header.Add(name, value)
}
resp, err := ombi.httpClient.Do(req)
defer timeoutHandler("Ombi", ombi.server, ombi.noFail)
defer ombi.timeoutHandler()
if err != nil || resp.StatusCode != 200 {
if resp.StatusCode == 401 {
return "", 401, fmt.Errorf("Invalid API Key")
@ -77,7 +81,7 @@ func (ombi *Ombi) _getJSON(url string, params map[string]string) (string, int, e
}
// 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) _send(mode string, url string, data map[string]interface{}, response bool) (string, int, error) {
func (ombi *Ombi) send(mode string, url string, data map[string]interface{}, response bool) (string, int, error) {
responseText := ""
params, _ := json.Marshal(data)
req, _ := http.NewRequest(mode, url, bytes.NewBuffer(params))
@ -86,7 +90,7 @@ func (ombi *Ombi) _send(mode string, url string, data map[string]interface{}, re
req.Header.Add(name, value)
}
resp, err := ombi.httpClient.Do(req)
defer timeoutHandler("Ombi", ombi.server, ombi.noFail)
defer ombi.timeoutHandler()
if err != nil || !(resp.StatusCode == 200 || resp.StatusCode == 201) {
if resp.StatusCode == 401 {
return "", 401, fmt.Errorf("Invalid API Key")
@ -112,24 +116,26 @@ func (ombi *Ombi) _send(mode string, url string, data map[string]interface{}, re
return responseText, resp.StatusCode, nil
}
func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool) (string, int, error) {
return ombi._send("POST", url, data, response)
func (ombi *Ombi) post(url string, data map[string]interface{}, response bool) (string, int, error) {
return ombi.send("POST", url, data, response)
}
func (ombi *Ombi) _put(url string, data map[string]interface{}, response bool) (string, int, error) {
return ombi._send("PUT", url, data, response)
func (ombi *Ombi) put(url string, data map[string]interface{}, response bool) (string, int, error) {
return ombi.send("PUT", url, data, response)
}
func (ombi *Ombi) modifyUser(user map[string]interface{}) (status int, err error) {
// ModifyUser applies the given modified user object to the corresponding user.
func (ombi *Ombi) ModifyUser(user map[string]interface{}) (status int, err error) {
if _, ok := user["id"]; !ok {
err = fmt.Errorf("No ID provided")
return
}
_, status, err = ombi._put(ombi.server+"/api/v1/Identity", user, false)
_, status, err = ombi.put(ombi.server+"/api/v1/Identity", user, false)
return
}
func (ombi *Ombi) deleteUser(id string) (code int, err error) {
// DeleteUser deletes the user corresponding to the given ID.
func (ombi *Ombi) DeleteUser(id string) (code int, err error) {
url := fmt.Sprintf("%s/api/v1/Identity/%s", ombi.server, id)
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Content-Type", "application/json")
@ -137,21 +143,21 @@ func (ombi *Ombi) deleteUser(id string) (code int, err error) {
req.Header.Add(name, value)
}
resp, err := ombi.httpClient.Do(req)
defer timeoutHandler("Ombi", ombi.server, ombi.noFail)
defer ombi.timeoutHandler()
return resp.StatusCode, err
}
// 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._getJSON(fmt.Sprintf("%s/api/v1/Identity/User/%s", ombi.server, id), nil)
// UserByID returns the user corresponding to the provided ID.
func (ombi *Ombi) UserByID(id string) (result map[string]interface{}, code int, err error) {
resp, code, err := ombi.getJSON(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() ([]map[string]interface{}, int, error) {
// GetUsers returns all users on the Ombi instance.
func (ombi *Ombi) GetUsers() ([]map[string]interface{}, int, error) {
if time.Now().After(ombi.cacheExpiry) {
resp, code, err := ombi._getJSON(fmt.Sprintf("%s/api/v1/Identity/Users", ombi.server), nil)
resp, code, err := ombi.getJSON(fmt.Sprintf("%s/api/v1/Identity/Users", ombi.server), nil)
var result []map[string]interface{}
json.Unmarshal([]byte(resp), &result)
ombi.userCache = result
@ -175,9 +181,9 @@ 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)
// TemplateByID 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 {
return
}
@ -194,14 +200,14 @@ 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) {
// NewUser creates a new user with the given username, password and email address.
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
user["userName"] = username
user["password"] = password
user["emailAddress"] = email
resp, code, err := ombi._post(url, user, true)
resp, code, err := ombi.post(url, user, true)
var data map[string]interface{}
json.Unmarshal([]byte(resp), &data)
if err != nil || code != 200 {

View File

@ -59,7 +59,7 @@ func pwrMonitor(app *appContext, watcher *fsnotify.Watcher) {
}
app.info.Printf("New password reset for user \"%s\"", pwr.Username)
if currentTime := time.Now(); pwr.Expiry.After(currentTime) {
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 {
app.err.Printf("Failed to get users from Jellyfin: Code %d", status)
app.debug.Printf("Error: %s", err)

View File

@ -2,6 +2,8 @@ package main
import (
"github.com/gin-gonic/gin"
"github.com/hrfee/jfa-go/common"
"github.com/hrfee/jfa-go/jfapi"
)
type testReq struct {
@ -13,9 +15,8 @@ type testReq struct {
func (app *appContext) TestJF(gc *gin.Context) {
var req testReq
gc.BindJSON(&req)
tempjf, _ := newJellyfin(req.Host, "jfa-go-setup", app.version, "auth", "auth")
tempjf.noFail = true
_, status, err := tempjf.authenticate(req.Username, req.Password)
tempjf, _ := jfapi.NewJellyfin(req.Host, "jfa-go-setup", app.version, "auth", "auth", common.NewTimeoutHandler("authJF", req.Host, true))
_, status, err := tempjf.Authenticate(req.Username, req.Password)
if !(status == 200 || status == 204) || err != nil {
app.info.Printf("Auth failed with code %d (%s)", status, err)
gc.JSON(401, map[string]bool{"success": false})