1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-29 12:30:11 +00:00

Compare commits

..

No commits in common. "67c36dd30134c72a69a3abe7067fca62df0dc7da" and "3dd2dbff159d17f3ce7160706695942252c4c11c" have entirely different histories.

15 changed files with 39 additions and 510 deletions

View File

@ -11,7 +11,7 @@ before:
# You may remove this if you don't use go modules.
- go mod download
- python3 config/fixconfig.py -i config/config-base.json -o data/config-base.json
- python3 config/generate_ini.py -i config/config-base.json -o data/config-default.ini --version {{.Version}}
- python3 config/generate_ini.py -i config/config-base.json -o data/config-default.ini
- python3 -m pip install libsass
- python3 scss/get_node_deps.py
- python3 scss/compile.py -y

View File

@ -2,7 +2,7 @@ configuration:
echo "Fixing config-base"
python3 config/fixconfig.py -i config/config-base.json -o data/config-base.json
echo "Generating config-default.ini"
python3 config/generate_ini.py -i config/config-base.json -o data/config-default.ini --version git
python3 config/generate_ini.py -i config/config-base.json -o data/config-default.ini
sass:
echo "Getting libsass"

View File

@ -10,7 +10,6 @@ I chose to rewrite the python [jellyfin-accounts](https://github.com/hrfee/jelly
* Granular control over invites: Validity period as well as number of uses can be specified.
* Account defaults: Configure an example account to your liking, and its permissions, access rights and homescreen layout can be applied to all new users.
* Password validation: Ensure users choose a strong password.
* 🔗 Ombi Integration: Automatically creates Ombi accounts for new users using their email address and login details, and your own defined set of permissions.
* 📨 Email storage: Add your existing user's email addresses through the UI, and jfa-go will ask new users for them on account creation.
* Email addresses can optionally be used instead of usernames
* 🔑 Password resets: When user's forget their passwords and request a change in Jellyfin, jfa-go reads the PIN from the created file and sends it straight to the user via email.

52
api.go
View File

@ -4,7 +4,6 @@ import (
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
@ -254,18 +253,6 @@ func (app *appContext) NewUser(gc *gin.Context) {
app.storage.emails[id] = req.Email
app.storage.storeEmails()
}
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)
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")
}
}
}
gc.JSON(200, validation)
}
@ -484,30 +471,6 @@ func (app *appContext) GetUsers(gc *gin.Context) {
gc.JSON(200, resp)
}
type ombiUser struct {
Name string `json:"name,omitempty"`
ID string `json:"id"`
}
func (app *appContext) OmbiUsers(gc *gin.Context) {
app.debug.Println("Ombi users requested")
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)
respond(500, "Couldn't get users", gc)
return
}
userlist := make([]ombiUser, len(users))
for i, data := range users {
userlist[i] = ombiUser{
Name: data["userName"].(string),
ID: data["id"].(string),
}
}
gc.JSON(200, map[string][]ombiUser{"users": userlist})
}
func (app *appContext) ModifyEmails(gc *gin.Context) {
var req map[string]string
gc.BindJSON(&req)
@ -529,21 +492,6 @@ func (app *appContext) ModifyEmails(gc *gin.Context) {
gc.JSON(200, map[string]bool{"success": true})
}
func (app *appContext) SetOmbiDefaults(gc *gin.Context) {
var req ombiUser
gc.BindJSON(&req)
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)
return
}
app.storage.ombi_template = template
fmt.Println(app.storage.ombi_path)
app.storage.storeOmbiTemplate()
gc.JSON(200, map[string]bool{"success": true})
}
type defaultsReq struct {
Username string `json:"username"`
Homescreen bool `json:"homescreen"`

View File

@ -48,7 +48,7 @@ func (app *appContext) loadConfig() error {
// }
key.SetValue(key.MustString(filepath.Join(app.data_path, (key.Name() + ".json"))))
}
for _, key := range []string{"user_configuration", "user_displayprefs", "ombi_template"} {
for _, key := range []string{"user_configuration", "user_displayprefs"} {
// if app.config.Section("files").Key(key).MustString("") == "" {
// key.SetValue(filepath.Join(app.data_path, (key.Name() + ".json")))
// }

View File

@ -40,7 +40,7 @@
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go",
"value": "jf-accounts",
"description": "This and below settings will show on the Jellyfin dashboard when the program connects. You may as well leave them alone."
},
"version": {
@ -55,14 +55,14 @@
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go"
"value": "jf-accounts"
},
"device_id": {
"name": "Device ID",
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go-{version}"
"value": "jf-accounts-{version}"
}
},
"ui": {
@ -398,7 +398,7 @@
"depends_true": "enabled",
"type": "text",
"value": "http://accounts.jellyf.in:8056/invite",
"description": "Base URL for jfa-go. This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
"description": "Base URL for jf-accounts. This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
}
},
"notifications": {
@ -511,38 +511,6 @@
"value": "smtp password"
}
},
"ombi": {
"meta": {
"name": "Ombi Integration",
"description": "Connect to Ombi to automatically create a new user's account. You'll need to create an Ombi user template."
},
"enabled": {
"name": "Enabled",
"required": false,
"requires_restart": true,
"type": "bool",
"value": false,
"description": "Enable to create an Ombi account for new Jellyfin users"
},
"server": {
"name": "URL",
"required": false,
"requires_restart": true,
"type": "text",
"value": "localhost:5000",
"depends_true": "enabled",
"description": "Ombi server URL, including http(s)://."
},
"api_key": {
"name": "API Key",
"required": false,
"requires_restart": true,
"type": "text",
"value": "",
"depends_true": "enabled",
"description": "API Key. Get this from the first tab in Ombi settings."
}
},
"files": {
"meta": {
"name": "File Storage",
@ -564,14 +532,6 @@
"value": "",
"description": "Location of stored email addresses (json)."
},
"ombi_template": {
"name": "Ombi user template",
"required": false,
"requires_restart": false,
"type": "text",
"value": "",
"description": "Location of stored Ombi user template."
},
"user_template": {
"name": "User Template",
"required": false,

View File

@ -7,7 +7,6 @@ from pathlib import Path
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--input", help="input config base from jf-accounts")
parser.add_argument("-o", "--output", help="output ini")
parser.add_argument("--version", help="version to include in file")
def generate_ini(base_file, ini_file, version):
@ -44,8 +43,6 @@ def generate_ini(base_file, ini_file, version):
args = parser.parse_args()
version = "git"
if args.version is not None:
version = args.version
print(generate_ini(base_file=args.input, ini_file=args.output, version=version))
print(generate_ini(base_file=args.input,
ini_file=args.output,
version="0.1.0"))

View File

@ -9,7 +9,6 @@
"notifications",
"mailgun",
"smtp",
"ombi",
"files"
],
"jellyfin": {
@ -63,7 +62,7 @@
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go",
"value": "jf-accounts",
"description": "This and below settings will show on the Jellyfin dashboard when the program connects. You may as well leave them alone."
},
"version": {
@ -78,14 +77,14 @@
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go"
"value": "jf-accounts"
},
"device_id": {
"name": "Device ID",
"required": true,
"requires_restart": true,
"type": "text",
"value": "jfa-go-{version}"
"value": "jf-accounts-{version}"
}
},
"ui": {
@ -467,7 +466,7 @@
"depends_true": "enabled",
"type": "text",
"value": "http://accounts.jellyf.in:8056/invite",
"description": "Base URL for jfa-go. This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
"description": "Base URL for jf-accounts. This is necessary because using a reverse proxy means the program has no way of knowing the URL itself."
}
},
"notifications": {
@ -597,48 +596,10 @@
"value": "smtp password"
}
},
"ombi": {
"order": [
"enabled",
"server",
"api_key"
],
"meta": {
"name": "Ombi Integration",
"description": "Connect to Ombi to automatically create a new user's account. You'll need to create an Ombi user template."
},
"enabled": {
"name": "Enabled",
"required": false,
"requires_restart": true,
"type": "bool",
"value": false,
"description": "Enable to create an Ombi account for new Jellyfin users"
},
"server": {
"name": "URL",
"required": false,
"requires_restart": true,
"type": "text",
"value": "localhost:5000",
"depends_true": "enabled",
"description": "Ombi server URL, including http(s)://."
},
"api_key": {
"name": "API Key",
"required": false,
"requires_restart": true,
"type": "text",
"value": "",
"depends_true": "enabled",
"description": "API Key. Get this from the first tab in Ombi settings."
}
},
"files": {
"order": [
"invites",
"emails",
"ombi_template",
"user_template",
"user_configuration",
"user_displayprefs",
@ -664,14 +625,6 @@
"value": "",
"description": "Location of stored email addresses (json)."
},
"ombi_template": {
"name": "Ombi user template",
"required": false,
"requires_restart": false,
"type": "text",
"value": "",
"description": "Location of stored Ombi user template."
},
"user_template": {
"name": "User Template",
"required": false,

View File

@ -627,6 +627,9 @@ document.getElementById('openDefaultsWizard').onclick = function() {
let users = req.response['users'];
let radioList = document.getElementById('defaultUserRadios');
radioList.textContent = '';
if (document.getElementById('setDefaultUser')) {
document.getElementById('setDefaultUser').remove();
}
let first = true;
for (user of users) {
let radio = document.createElement('div');
@ -636,14 +639,14 @@ document.getElementById('openDefaultsWizard').onclick = function() {
first = false;
} else {
checked = '';
}
};
radio.innerHTML =
`<label><input type="radio" name="defaultRadios" id="default_${user['name']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
radioList.appendChild(radio);
}
let button = document.getElementById('openDefaultsWizard');
button.disabled = false;
button.innerHTML = 'New User Defaults <i class="fa fa-user settingIcon"></i>';
button.innerHTML = 'Set new account defaults';
let submitButton = document.getElementById('storeDefaults');
submitButton.disabled = false;
submitButton.textContent = 'Submit';
@ -712,107 +715,6 @@ document.getElementById('storeDefaults').onclick = function () {
}
};
var ombiDefaultsModal = '';
if (ombiEnabled) {
ombiDefaultsModal = createModal('ombiDefaults');
document.getElementById('openOmbiDefaults').onclick = function() {
this.disabled = true;
this.innerHTML =
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
'Loading...';
let req = new XMLHttpRequest();
req.responseType = 'json';
req.open("GET", "/getOmbiUsers", true);
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
req.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
let users = req.response['users'];
let radioList = document.getElementById('ombiUserRadios');
radioList.textContent = '';
let first = true;
// name and id
for (user of users) {
let radio = document.createElement('div');
radio.classList.add('radio');
let checked = 'checked';
if (first) {
first = false;
} else {
checked = '';
}
radio.innerHTML =
`<label><input type="radio" name="ombiRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
radioList.appendChild(radio);
}
let button = document.getElementById('openOmbiDefaults');
button.disabled = false;
button.innerHTML = 'Ombi User Defaults <i class="fa fa-chain-broken settingIcon"></i>';
let submitButton = document.getElementById('storeOmbiDefaults');
submitButton.disabled = false;
submitButton.textContent = 'Submit';
if (submitButton.classList.contains('btn-success')) {
submitButton.classList.remove('btn-success');
submitButton.classList.add('btn-primary');
} else if (submitButton.classList.contains('btn-danger')) {
submitButton.classList.remove('btn-danger');
submitButton.classList.add('btn-primary');
}
settingsModal.hide();
ombiDefaultsModal.show();
}
}
};
req.send();
};
document.getElementById('storeOmbiDefaults').onclick = function() {
this.disabled = true;
this.innerHTML =
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" style="margin-right: 0.5rem;"></span>' +
'Loading...';
let button = document.getElementById('storeOmbiDefaults');
let radios = document.getElementsByName('ombiRadios');
for (let radio of radios) {
if (radio.checked) {
let data = {
'id': radio.id.slice(8),
};
let req = new XMLHttpRequest();
req.open("POST", "/setOmbiDefaults", true);
req.setRequestHeader("Authorization", "Basic " + btoa(window.token + ":"));
req.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
req.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 204) {
button.textContent = "Success";
if (button.classList.contains('btn-danger')) {
button.classList.remove('btn-danger');
} else if (button.classList.contains('btn-primary')) {
button.classList.remove('btn-primary');
}
button.classList.add('btn-success');
button.disabled = false;
setTimeout(function() { ombiDefaultsModal.hide(); }, 1000);
} else {
button.textContent = "Failed";
button.classList.remove('btn-primary');
button.classList.add('btn-danger');
setTimeout(function() {
let button = document.getElementById('storeOmbiDefaults');
button.textContent = "Submit";
button.classList.remove('btn-danger');
button.classList.add('btn-primary');
button.disabled = false;
}, 1000);
}
}
};
req.send(JSON.stringify(data));
}
}
};
}
document.getElementById('openUsers').onclick = function () {
this.disabled = true;
this.innerHTML =

View File

@ -52,8 +52,6 @@
}
css.setAttribute('href', cssFile);
document.head.appendChild(css);
// store whether ombi is enabled, 1 or 0.
var ombiEnabled = {{ .ombiEnabled }}
</script>
{{ if not .bs5 }}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
@ -132,10 +130,6 @@
white-space: nowrap;
width: auto;
}
.settingIcon {
margin-left: 0.2rem;
}
</style>
<title>Admin</title>
</head>
@ -172,22 +166,17 @@
</button>
</div>
<div class="modal-body">
<ul class="list-group list-group-flush" style="margin-bottom: 1rem;">
<ul class="list-group list-group-flush">
<p>Note: <sup class="text-danger">*</sup> Indicates required field, <sup class="text-danger">R</sup> Indicates changes require a restart.</p>
<button type="button" class="list-group-item list-group-item-action" id="openAbout">
About <i class="fa fa-info-circle settingIcon"></i>
About <i class="fa fa-info-circle"></i>
</button>
<button type="button" class="list-group-item list-group-item-action" id="openUsers">
Email Addresses <i class="fa fa-envelope settingIcon"></i>
Users <i class="fa fa-user"></i>
</button>
<button type="button" class="list-group-item list-group-item-action" id="openDefaultsWizard">
New User Defaults <i class="fa fa-user settingIcon"></i>
New account defaults
</button>
{{ if .ombiEnabled }}
<button type="button" class="list-group-item list-group-item-action" id="openOmbiDefaults">
Ombi User Defaults <i class="fa fa-chain-broken settingIcon"></i>
</button>
{{ end }}
</ul>
<div class="list-group list-group-flush" id="settingsList">
</div>
@ -241,28 +230,6 @@
</div>
</div>
</div>
{{ if .ombiEnabled }}
<div class="modal fade" id="ombiDefaults" role="dialog" aria-labelledby="Ombi Users" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="ombiTitle">Ombi user defaults</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>Create an Ombi user and configure it to your liking, then choose it from below to store the settings and permissions as a template for all new users.</p>
<div id="ombiUserRadios"></div>
</div>
<div class="modal-footer" id="ombiFooter">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="storeOmbiDefaults">Submit</button>
</div>
</div>
</div>
</div>
{{ end }}
<div class="modal fade" id="restartModal" role="dialog" aria-labelledby="Restart Warning" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">

View File

@ -43,10 +43,10 @@ type Jellyfin struct {
noFail bool
}
func timeoutHandler(name, addr string, noFail bool) {
func (jf *Jellyfin) timeoutHandler() {
if r := recover(); r != nil {
out := fmt.Sprintf("Failed to authenticate with %s @ %s: Timed out", name, addr)
if noFail {
out := fmt.Sprintf("Failed to authenticate with Jellyfin @ %s: Timed out", jf.server)
if jf.noFail {
log.Printf(out)
} else {
log.Fatalf(out)
@ -78,7 +78,7 @@ func newJellyfin(server, client, version, device, deviceId string) (*Jellyfin, e
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)
@ -106,7 +106,7 @@ func (jf *Jellyfin) authenticate(username, password string) (map[string]interfac
// loginParams, _ := json.Marshal(jf.loginParams)
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
}
@ -148,7 +148,7 @@ func (jf *Jellyfin) _getReader(url string, params map[string]string) (io.Reader,
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
@ -180,7 +180,7 @@ func (jf *Jellyfin) _post(url string, data map[string]interface{}, response bool
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

30
main.go
View File

@ -45,7 +45,6 @@ type appContext struct {
invalidTokens []string
jf *Jellyfin
authJf *Jellyfin
ombi *Ombi
datePattern string
timePattern string
storage Storage
@ -226,32 +225,17 @@ func main() {
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.invite_path = filepath.Join(app.data_path, "invites.json")
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.emails_path = filepath.Join(app.data_path, "emails.json")
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.policy_path = filepath.Join(app.data_path, "user_template.json")
app.storage.loadPolicy()
// app.storage.configuration_path = filepath.Join(app.data_path, "user_configuration.json")
app.storage.policy_path = app.config.Section("files").Key("user_configuration").String()
app.storage.configuration_path = filepath.Join(app.data_path, "user_configuration.json")
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.displayprefs_path = filepath.Join(app.data_path, "user_displayprefs.json")
app.storage.loadDisplayprefs()
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(),
app.config.Section("ombi").Key("api_key").String(),
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)
@ -356,10 +340,6 @@ func main() {
api.POST("/setDefaults", app.SetDefaults)
api.GET("/getConfig", app.GetConfig)
api.POST("/modifyConfig", app.ModifyConfig)
if app.config.Section("ombi").Key("enabled").MustBool(false) {
api.GET("/getOmbiUsers", app.OmbiUsers)
api.POST("/setOmbiDefaults", app.SetOmbiDefaults)
}
app.info.Printf("Starting router @ %s", address)
} else {
router.GET("/", func(gc *gin.Context) {

167
ombi.go
View File

@ -1,167 +0,0 @@
package main
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"time"
)
type Ombi struct {
server, key string
header map[string]string
httpClient *http.Client
noFail bool
}
func newOmbi(server, key string, noFail bool) *Ombi {
return &Ombi{
server: server,
key: key,
noFail: noFail,
httpClient: &http.Client{
Timeout: 10 * time.Second,
},
header: map[string]string{
"ApiKey": key,
},
}
}
func (ombi *Ombi) _getReader(url string, params map[string]string) (string, int, error) {
if ombi.key == "" {
return "", 401, fmt.Errorf("No API key provided")
}
var req *http.Request
if params != nil {
jsonParams, _ := json.Marshal(params)
req, _ = http.NewRequest("GET", url, bytes.NewBuffer(jsonParams))
} else {
req, _ = http.NewRequest("GET", url, nil)
}
for name, value := range ombi.header {
req.Header.Add(name, value)
}
resp, err := ombi.httpClient.Do(req)
defer timeoutHandler("Ombi", ombi.server, ombi.noFail)
if err != nil || resp.StatusCode != 200 {
if resp.StatusCode == 401 {
return "", 401, fmt.Errorf("Invalid API Key")
}
return "", resp.StatusCode, err
}
defer resp.Body.Close()
var data io.Reader
switch resp.Header.Get("Content-Encoding") {
case "gzip":
data, _ = gzip.NewReader(resp.Body)
default:
data = resp.Body
}
buf := new(strings.Builder)
_, err = io.Copy(buf, data)
if err != nil {
return "", 500, err
}
return buf.String(), resp.StatusCode, nil
}
func (ombi *Ombi) _post(url string, data map[string]interface{}, response bool) (string, int, error) {
responseText := ""
params, _ := json.Marshal(data)
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(params))
req.Header.Add("Content-Type", "application/json")
for name, value := range ombi.header {
req.Header.Add(name, value)
}
resp, err := ombi.httpClient.Do(req)
defer timeoutHandler("Ombi", ombi.server, ombi.noFail)
if err != nil || !(resp.StatusCode == 200 || resp.StatusCode == 201) {
if resp.StatusCode == 401 {
return "", 401, fmt.Errorf("Invalid API Key")
}
return responseText, resp.StatusCode, err
}
if response {
defer resp.Body.Close()
var out io.Reader
switch resp.Header.Get("Content-Encoding") {
case "gzip":
out, _ = gzip.NewReader(resp.Body)
default:
out = resp.Body
}
buf := new(strings.Builder)
_, err = io.Copy(buf, out)
if err != nil {
return "", 500, err
}
responseText = buf.String()
}
return responseText, resp.StatusCode, nil
}
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
}
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)
return
}
// Strip these from a user when saving as a template.
// We also need to strip userQualityProfiles{"id", "userId"}
var stripFromOmbi = []string{
"alias",
"emailAddress",
"hasLoggedIn",
"id",
"lastLoggedIn",
"password",
"userName",
}
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
}
for _, key := range stripFromOmbi {
if _, ok := result[key]; ok {
delete(result, key)
}
}
if qp, ok := result["userQualityProfiles"].(map[string]interface{}); ok {
delete(qp, "id")
delete(qp, "userId")
result["userQualityProfiles"] = qp
}
return
}
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)
var data map[string]interface{}
json.Unmarshal([]byte(resp), &data)
if err != nil || code != 200 {
var lst []string
if data["errors"] != nil {
lst = data["errors"].([]string)
}
return lst, code, err
}
return nil, code, err
}

View File

@ -8,9 +8,9 @@ import (
type Storage struct {
timePattern string
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path string
invite_path, emails_path, policy_path, configuration_path, displayprefs_path string
invites Invites
emails, policy, configuration, displayprefs, ombi_template map[string]interface{}
emails, policy, configuration, displayprefs map[string]interface{}
}
// timePattern: %Y-%m-%dT%H:%M:%S.%f
@ -67,14 +67,6 @@ func (st *Storage) storeDisplayprefs() error {
return storeJSON(st.displayprefs_path, st.displayprefs)
}
func (st *Storage) loadOmbiTemplate() error {
return loadJSON(st.ombi_path, &st.ombi_template)
}
func (st *Storage) storeOmbiTemplate() error {
return storeJSON(st.ombi_path, st.ombi_template)
}
func loadJSON(path string, obj interface{}) error {
var file []byte
var err error

View File

@ -10,7 +10,6 @@ func (app *appContext) AdminPage(gc *gin.Context) {
bs5 := app.config.Section("ui").Key("bs5").MustBool(false)
emailEnabled, _ := app.config.Section("invite_emails").Key("enabled").Bool()
notificationsEnabled, _ := app.config.Section("notifications").Key("enabled").Bool()
ombiEnabled := app.config.Section("ombi").Key("enabled").MustBool(false)
gc.HTML(http.StatusOK, "admin.html", gin.H{
"bs5": bs5,
"cssFile": app.cssFile,
@ -19,7 +18,6 @@ func (app *appContext) AdminPage(gc *gin.Context) {
"notifications": notificationsEnabled,
"version": VERSION,
"commit": COMMIT,
"ombiEnabled": ombiEnabled,
})
}