mirror of https://github.com/hrfee/jfa-go.git
Initial Ombi integration
When enabled, an account for the user is created on both Jellyfin and Ombi. Account defaults can be stored similarly to jf.
This commit is contained in:
parent
9850545f1b
commit
ba67fa7536
52
api.go
52
api.go
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
|
@ -253,6 +254,18 @@ 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)
|
||||
}
|
||||
|
||||
|
@ -471,6 +484,30 @@ 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)
|
||||
|
@ -492,6 +529,21 @@ 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"`
|
||||
|
|
|
@ -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"} {
|
||||
for _, key := range []string{"user_configuration", "user_displayprefs", "ombi_template"} {
|
||||
// if app.config.Section("files").Key(key).MustString("") == "" {
|
||||
// key.SetValue(filepath.Join(app.data_path, (key.Name() + ".json")))
|
||||
// }
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "jf-accounts",
|
||||
"value": "jfa-go",
|
||||
"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": "jf-accounts"
|
||||
"value": "jfa-go"
|
||||
},
|
||||
"device_id": {
|
||||
"name": "Device ID",
|
||||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "jf-accounts-{version}"
|
||||
"value": "jfa-go-{version}"
|
||||
}
|
||||
},
|
||||
"ui": {
|
||||
|
@ -398,7 +398,7 @@
|
|||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "http://accounts.jellyf.in:8056/invite",
|
||||
"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."
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
@ -511,6 +511,38 @@
|
|||
"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",
|
||||
|
@ -532,6 +564,14 @@
|
|||
"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,
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"notifications",
|
||||
"mailgun",
|
||||
"smtp",
|
||||
"ombi",
|
||||
"files"
|
||||
],
|
||||
"jellyfin": {
|
||||
|
@ -62,7 +63,7 @@
|
|||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "jf-accounts",
|
||||
"value": "jfa-go",
|
||||
"description": "This and below settings will show on the Jellyfin dashboard when the program connects. You may as well leave them alone."
|
||||
},
|
||||
"version": {
|
||||
|
@ -77,14 +78,14 @@
|
|||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "jf-accounts"
|
||||
"value": "jfa-go"
|
||||
},
|
||||
"device_id": {
|
||||
"name": "Device ID",
|
||||
"required": true,
|
||||
"requires_restart": true,
|
||||
"type": "text",
|
||||
"value": "jf-accounts-{version}"
|
||||
"value": "jfa-go-{version}"
|
||||
}
|
||||
},
|
||||
"ui": {
|
||||
|
@ -466,7 +467,7 @@
|
|||
"depends_true": "enabled",
|
||||
"type": "text",
|
||||
"value": "http://accounts.jellyf.in:8056/invite",
|
||||
"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."
|
||||
"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."
|
||||
}
|
||||
},
|
||||
"notifications": {
|
||||
|
@ -596,10 +597,48 @@
|
|||
"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."
|
||||
},
|
||||
"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",
|
||||
|
@ -625,6 +664,14 @@
|
|||
"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,
|
||||
|
|
|
@ -627,9 +627,6 @@ 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');
|
||||
|
@ -639,14 +636,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 = 'Set new account defaults';
|
||||
button.innerHTML = 'New account defaults';
|
||||
let submitButton = document.getElementById('storeDefaults');
|
||||
submitButton.disabled = false;
|
||||
submitButton.textContent = 'Submit';
|
||||
|
@ -715,6 +712,107 @@ 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';
|
||||
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 =
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
}
|
||||
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>
|
||||
|
@ -177,6 +179,11 @@
|
|||
<button type="button" class="list-group-item list-group-item-action" id="openDefaultsWizard">
|
||||
New account defaults
|
||||
</button>
|
||||
{{ if .ombiEnabled }}
|
||||
<button type="button" class="list-group-item list-group-item-action" id="openOmbiDefaults">
|
||||
Ombi user defaults
|
||||
</button>
|
||||
{{ end }}
|
||||
</ul>
|
||||
<div class="list-group list-group-flush" id="settingsList">
|
||||
</div>
|
||||
|
@ -230,6 +237,28 @@
|
|||
</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">×</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">
|
||||
|
|
14
jfapi.go
14
jfapi.go
|
@ -43,10 +43,10 @@ type Jellyfin struct {
|
|||
noFail bool
|
||||
}
|
||||
|
||||
func (jf *Jellyfin) timeoutHandler() {
|
||||
func timeoutHandler(name, addr string, noFail bool) {
|
||||
if r := recover(); r != nil {
|
||||
out := fmt.Sprintf("Failed to authenticate with Jellyfin @ %s: Timed out", jf.server)
|
||||
if jf.noFail {
|
||||
out := fmt.Sprintf("Failed to authenticate with %s @ %s: Timed out", name, addr)
|
||||
if 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 jf.timeoutHandler()
|
||||
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
|
||||
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 jf.timeoutHandler()
|
||||
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
|
||||
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 jf.timeoutHandler()
|
||||
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
|
||||
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 jf.timeoutHandler()
|
||||
defer timeoutHandler("Jellyfin", jf.server, jf.noFail)
|
||||
if err != nil || resp.StatusCode != 200 {
|
||||
if resp.StatusCode == 401 && jf.authenticated {
|
||||
jf.authenticated = false
|
||||
|
|
30
main.go
30
main.go
|
@ -45,6 +45,7 @@ type appContext struct {
|
|||
invalidTokens []string
|
||||
jf *Jellyfin
|
||||
authJf *Jellyfin
|
||||
ombi *Ombi
|
||||
datePattern string
|
||||
timePattern string
|
||||
storage Storage
|
||||
|
@ -225,17 +226,32 @@ func main() {
|
|||
|
||||
app.debug.Println("Loading storage")
|
||||
|
||||
app.storage.invite_path = filepath.Join(app.data_path, "invites.json")
|
||||
// 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 = 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 = 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 = filepath.Join(app.data_path, "user_configuration.json")
|
||||
app.storage.policy_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 = filepath.Join(app.data_path, "user_displayprefs.json")
|
||||
app.storage.displayprefs_path = app.config.Section("files").Key("user_displayprefs").String()
|
||||
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)
|
||||
|
@ -340,6 +356,10 @@ 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) {
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
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
|
||||
}
|
16
storage.go
16
storage.go
|
@ -7,10 +7,10 @@ import (
|
|||
)
|
||||
|
||||
type Storage struct {
|
||||
timePattern string
|
||||
invite_path, emails_path, policy_path, configuration_path, displayprefs_path string
|
||||
invites Invites
|
||||
emails, policy, configuration, displayprefs map[string]interface{}
|
||||
timePattern string
|
||||
invite_path, emails_path, policy_path, configuration_path, displayprefs_path, ombi_path string
|
||||
invites Invites
|
||||
emails, policy, configuration, displayprefs, ombi_template map[string]interface{}
|
||||
}
|
||||
|
||||
// timePattern: %Y-%m-%dT%H:%M:%S.%f
|
||||
|
@ -67,6 +67,14 @@ 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
|
||||
|
|
2
views.go
2
views.go
|
@ -10,6 +10,7 @@ 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,
|
||||
|
@ -18,6 +19,7 @@ func (app *appContext) AdminPage(gc *gin.Context) {
|
|||
"notifications": notificationsEnabled,
|
||||
"version": VERSION,
|
||||
"commit": COMMIT,
|
||||
"ombiEnabled": ombiEnabled,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue