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. "49b056f1d64e6c598530043e89c45d88fa0bb7cf" and "5ba40cd6f8dc70b2cdfccacae6290e9318336600" have entirely different histories.

11 changed files with 56 additions and 58 deletions

View File

@ -11,7 +11,6 @@ I chose to rewrite the python [jellyfin-accounts](https://github.com/hrfee/jelly
* 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.
* Account management: Apply settings to your users individually or en masse, and delete users, optionally sending them an email notification with a reason.
* 📨 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.
@ -25,12 +24,12 @@ I chose to rewrite the python [jellyfin-accounts](https://github.com/hrfee/jelly
## Interface
<p align="center">
<img src="https://github.com/hrfee/jfa-go/blob/main/images/demo.gif" width="100%"></img>
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/main/images/jfa.gif" width="100%"></img>
</p>
<p align="center">
<img src="https://github.com/hrfee/jfa-go/blob/main/images/invites.png" width="48%" style="margin-left: 1.5%;" alt="Invites tab"></img>
<img src="https://github.com/hrfee/jfa-go/blob/main/images/accounts.png" width="48%" style="margin-right: 1.5%;" alt="Accounts tab"></img>
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/main/images/admin.png" width="48%" style="margin-right: 1.5%;" alt="Admin page"></img>
<img src="https://raw.githubusercontent.com/hrfee/jellyfin-accounts/main/images/create.png" width="48%" style="margin-left: 1.5%;" alt="Account creation page"></img>
</p>
#### Install
@ -70,8 +69,6 @@ Usage of ./jfa-go:
alternate path to config file. (default "~/.config/jfa-go/config.ini")
-data string
alternate path to data directory. (default "~/.config/jfa-go")
-debug
Enables debug logging and exposes pprof.
-host string
alternate address to host web ui on.
-port int

21
api.go
View File

@ -462,7 +462,7 @@ func (app *appContext) GetInvites(gc *gin.Context) {
}
if _, ok := inv.Notify[address]; ok {
for _, notifyType := range []string{"notify-expiry", "notify-creation"} {
if _, ok = inv.Notify[address][notifyType]; ok {
if _, ok = inv.Notify[notifyType]; ok {
invite[notifyType] = inv.Notify[address][notifyType]
}
}
@ -476,8 +476,13 @@ func (app *appContext) GetInvites(gc *gin.Context) {
gc.JSON(200, resp)
}
type notifySetting struct {
NotifyExpiry bool `json:"notify-expiry"`
NotifyCreation bool `json:"notify-creation"`
}
func (app *appContext) SetNotify(gc *gin.Context) {
var req map[string]map[string]bool
var req map[string]notifySetting
gc.BindJSON(&req)
changed := false
for code, settings := range req {
@ -513,14 +518,14 @@ func (app *appContext) SetNotify(gc *gin.Context) {
} /*else {
if _, ok := invite.Notify[address]["notify-expiry"]; !ok {
*/
if _, ok := settings["notify-expiry"]; ok && invite.Notify[address]["notify-expiry"] != settings["notify-expiry"] {
invite.Notify[address]["notify-expiry"] = settings["notify-expiry"]
app.debug.Printf("%s: Set \"notify-expiry\" to %t for %s", code, settings["notify-expiry"], address)
if invite.Notify[address]["notify-expiry"] != settings.NotifyExpiry {
invite.Notify[address]["notify-expiry"] = settings.NotifyExpiry
app.debug.Printf("%s: Set \"notify-expiry\" to %t for %s", code, settings.NotifyExpiry, address)
changed = true
}
if _, ok := settings["notify-creation"]; ok && invite.Notify[address]["notify-creation"] != settings["notify-creation"] {
invite.Notify[address]["notify-creation"] = settings["notify-creation"]
app.debug.Printf("%s: Set \"notify-creation\" to %t for %s", code, settings["notify-creation"], address)
if invite.Notify[address]["notify-creation"] != settings.NotifyCreation {
invite.Notify[address]["notify-creation"] = settings.NotifyCreation
app.debug.Printf("%s: Set \"notify-creation\" to %t for %s", code, settings.NotifyExpiry, address)
changed = true
}
if changed {

View File

@ -53,16 +53,12 @@ document.getElementById('accountsTabDelete').onclick = function() {
}
}
let title = " user";
let msg = "Notify user";
if (selected.length > 1) {
title += "s";
msg += "s";
}
title = "Delete " + selected.length + title;
msg += " of account deletion";
document.getElementById('deleteModalTitle').textContent = title;
document.getElementById('deleteModalNotify').checked = false;
document.getElementById('deleteModalNotifyLabel').textContent = msg;
document.getElementById('deleteModalReason').value = '';
document.getElementById('deleteModalReasonBox').classList.add('unfocused');
document.getElementById('deleteModalSend').textContent = 'Delete';
@ -204,12 +200,8 @@ function populateUsers() {
if (admin) {
isAdmin = "Yes";
}
let fci = "form-check-input";
if (bsVersion != 5) {
fci = "";
}
return `
<td nowrap="nowrap" class="align-middle" scope="row"><input class="${fci}" type="checkbox" value="" id="select_${id}" onclick="checkCheckboxes();"></td>
<td nowrap="nowrap" class="align-middle" scope="row"><input class="form-check-input" type="checkbox" value="" id="select_${id}" onclick="checkCheckboxes();"></td>
<td nowrap="nowrap" class="align-middle">${username}</td>
<td nowrap="nowrap" class="align-middle">${generateEmail(id, name, email)}</td>
<td nowrap="nowrap" class="align-middle">${lastActive}</td>
@ -254,7 +246,22 @@ document.getElementById('accountsTabSetDefaults').onclick = function() {
if (userIDs.length == 0) {
return;
}
populateRadios();
let radioList = document.getElementById('defaultUserRadios');
radioList.textContent = '';
let first = true;
for (user of jfUsers) {
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="defaultRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
radioList.appendChild(radio);
}
let userstring = 'user';
if (userIDs.length > 1) {
userstring += 's';

View File

@ -647,28 +647,6 @@ document.getElementById('openAbout').onclick = function() {
aboutModal.show();
};
function populateRadios() {
let radioList = document.getElementById('defaultUserRadios');
radioList.textContent = '';
let first = true;
for (user of jfUsers) {
let radio = document.createElement('div');
radio.classList.add('form-check');
let checked = 'checked';
if (first) {
first = false;
} else {
checked = '';
}
// radio.innerHTML =
// `<label><input type="radio" name="defaultRadios" id="default_${user['id']}" style="margin-right: 1rem;" ${checked}>${user['name']}</label>`;
radio.innerHTML = `
<input class="form-check-input" type="radio" name="defaultRadios" id="default_${user['id']}" ${checked}>
<label class="form-check-label" for="default_${user['id']}">${user['name']}</label>`;
radioList.appendChild(radio);
}
}
document.getElementById('openDefaultsWizard').onclick = function() {
this.disabled = true
this.innerHTML =
@ -681,8 +659,23 @@ document.getElementById('openDefaultsWizard').onclick = function() {
req.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
jfUsers = req.response['users'];
populateRadios();
let users = req.response['users'];
let radioList = document.getElementById('defaultUserRadios');
radioList.textContent = '';
let first = true;
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="defaultRadios" id="default_${user['id']}" 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>';

View File

@ -162,9 +162,9 @@
</select>
</div>
<div id="defaultUserRadios"></div>
<div class="form-check" style="margin-top: 1rem;">
<input class="form-check-input" type="checkbox" value="" id="storeDefaultHomescreen" checked>
<label class="form-check-label" for="storeDefaultHomescreen" id="storeHomescreenLabel"></label>
<div class="checkbox">
<input type="checkbox" value="" style="margin-right: 1rem;" id="storeDefaultHomescreen" checked>
<label for="storeDefaultHomescreen" id="storeHomescreenLabel"></label>
</div>
</div>
<div class="modal-footer" id="defaultsFooter">
@ -207,8 +207,8 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-secondary" id="applyRestarts" data-dismiss="modal">Apply, Restart later</button>
<button type="button" class="btn btn-primary" id="applyAndRestart" data-dismiss="modal">Apply &amp; Restart</button>
<button type="button" class="btn btn-secondary" id="applyrestarts" data-dismiss="modal">apply, restart later</button>
<button type="button" class="btn btn-primary" id="applyandrestart" data-dismiss="modal">apply &amp; restart</button>
</div>
</div>
</div>
@ -256,7 +256,7 @@
<div class="modal-body">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="" id="deleteModalNotify">
<label class="form-check-label" for="deleteModalNotify" id="deleteModalNotifyLabel">Notify users of account deletion</label>
<label class="form-check-label" for="deleteModalNotify">Notify users of account deletion</label>
</div>
<div class="mb-3 unfocused" id="deleteModalReasonBox">
<label for="deleteModalReason" class="form-label">Reason for deletion</label>
@ -408,11 +408,7 @@
<table class="table table-hover table-striped table-borderless">
<thead>
<tr>
{{ if .bs5 }}
<th scope="col"><input class="form-check-input" type="checkbox" value="" id="selectAll"></th>
{{ else }}
<th scope="col"><input type="checkbox" value="" id="selectAll"></th>
{{ end }}
<th scope="col">Username</th>
<th scope="col">Email Address</th>
<th scope="col">Last Active</th>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

BIN
images/admin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

BIN
images/create.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

BIN
images/jfa.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB