1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-06-18 07:27:49 +02:00

fix/improve parsing of last active dates

parseDT only uses the magic json.Unmarshal method if theres an error
with the better version. Error came from some times being sent without a
"Z" at the end denoting UTC.
This commit is contained in:
Harvey Tindall 2020-12-03 20:49:50 +00:00
parent 3c952d21f7
commit a1e30ff5db
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
7 changed files with 96 additions and 79 deletions

0
%1 Normal file
View File

17
api.go
View File

@ -839,10 +839,20 @@ type dateToParse struct {
Parsed time.Time `json:"parseme"`
}
// json magically parses datetimes so why not
func parseDt(date string) time.Time {
func parseDT(date string) time.Time {
// decent method
dt, err := time.Parse("2006-01-02T15:04:05.000000", date)
if err == nil {
return dt
}
// magic method
// some stored dates from jellyfin have no timezone at the end, if not we assume UTC
if date[len(date)-1] != 'Z' {
date += "Z"
}
timeJSON := []byte("{ \"parseme\": \"" + date + "\" }")
var parsed dateToParse
// Magically turn it into a time.Time
json.Unmarshal(timeJSON, &parsed)
return parsed.Parsed
}
@ -869,8 +879,9 @@ func (app *appContext) GetUsers(gc *gin.Context) {
var user respUser
user.LastActive = "n/a"
if jfUser["LastActivityDate"] != nil {
date := parseDt(jfUser["LastActivityDate"].(string))
date := parseDT(jfUser["LastActivityDate"].(string))
user.LastActive = app.formatDatetime(date)
// fmt.Printf("%s: %s, %s, %+v\n", jfUser["Name"].(string), jfUser["LastActivityDate"].(string), user.LastActive, date)
}
user.ID = jfUser["Id"].(string)
user.Name = jfUser["Name"].(string)

8
nohup.out Normal file
View File

@ -0,0 +1,8 @@
Nov 29 23:37 : exception: Failed to bind to '[::]:6600'; Failed to bind socket: Address already in use
/usr/bin/mpDris2:1367: DeprecationWarning: The SafeConfigParser class has been renamed to ConfigParser in Python 3.2. This alias will be removed in future versions. Use ConfigParser directly instead.
config = configparser.SafeConfigParser()
2020-11-29 23:37:53,454 mpDris2 INFO: Using file:///home/hrfee/Music as music library path.
2020-11-29 23:37:53,454 mpDris2 INFO: Using Mutagen to read covers from music files.
2020-11-29 23:37:53,456 base INFO: Calling MPD connect('localhost', '6600', timeout=None)
2020-11-29 23:37:57,776 mpDris2 INFO: Replaced by :1.24 (PID 1128)
2020-11-29 23:37:57,777 base INFO: Calling MPD disconnect()

View File

@ -1,7 +1,7 @@
import { checkCheckboxes, populateUsers, populateRadios } from "./modules/accounts.js";
import { _post, _get, _delete, rmAttr, addAttr } from "./modules/common.js";
import { checkCheckboxes, populateUsers, populateRadios, changeEmail, validateEmail } from "./modules/accounts.js";
import { _post, _get, _delete, rmAttr, addAttr, createEl } from "./modules/common.js";
import { populateProfiles } from "./modules/settings.js";
import { Focus, Unfocus, createEl, storeDefaults } from "./modules/admin.js";
import { Focus, Unfocus, storeDefaults } from "./modules/admin.js";
interface aWindow extends Window {
changeEmail(icon: HTMLElement, id: string): void;
@ -9,67 +9,7 @@ interface aWindow extends Window {
declare var window: aWindow;
const validateEmail = (email: string): boolean => /\S+@\S+\.\S+/.test(email);
window.changeEmail = (icon: HTMLElement, id: string): void => {
const iconContent = icon.outerHTML;
icon.setAttribute('class', '');
const entry = icon.nextElementSibling as HTMLInputElement;
const ogEmail = entry.value;
entry.readOnly = false;
entry.classList.remove('form-control-plaintext');
entry.classList.add('form-control');
if (ogEmail == "") {
entry.placeholder = 'Address';
}
const tick = createEl(`
<i class="fa fa-check d-inline-block icon-button text-success" style="margin-left: 0.5rem; margin-right: 0.5rem;"></i>
`);
tick.onclick = (): void => {
const newEmail = entry.value;
if (!validateEmail(newEmail) || newEmail == ogEmail) {
return;
}
cross.remove();
const spinner = createEl(`
<div class="spinner-border spinner-border-sm" role="status" style="width: 1rem; height: 1rem; margin-left: 0.5rem;">
<span class="sr-only">Saving...</span>
</div>
`);
tick.replaceWith(spinner);
let send = {};
send[id] = newEmail;
_post("/users/emails", send, function (): void {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 204) {
entry.nextElementSibling.remove();
} else {
entry.value = ogEmail;
}
}
});
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
};
const cross = createEl(`
<i class="fa fa-close d-inline-block icon-button text-danger"></i>
`);
cross.onclick = (): void => {
tick.remove();
cross.remove();
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
entry.value = ogEmail;
};
icon.parentNode.appendChild(tick);
icon.parentNode.appendChild(cross);
};
window.changeEmail = changeEmail;
(<HTMLInputElement>document.getElementById('selectAll')).onclick = function (): void {
const checkboxes: NodeListOf<HTMLInputElement> = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]');

View File

@ -1,4 +1,4 @@
import { _get, _post, _delete } from "../modules/common.js";
import { _get, _post, _delete, createEl } from "../modules/common.js";
import { Focus, Unfocus } from "../modules/admin.js";
interface aWindow extends Window {
@ -7,6 +7,8 @@ interface aWindow extends Window {
declare var window: aWindow;
export const validateEmail = (email: string): boolean => /\S+@\S+\.\S+/.test(email);
export const checkCheckboxes = (): void => {
const defaultsButton = document.getElementById('accountsTabSetDefaults');
const deleteButton = document.getElementById('accountsTabDelete');
@ -28,6 +30,66 @@ export const checkCheckboxes = (): void => {
window.checkCheckboxes = checkCheckboxes;
export function changeEmail(icon: HTMLElement, id: string): void {
const iconContent = icon.outerHTML;
icon.setAttribute('class', '');
const entry = icon.nextElementSibling as HTMLInputElement;
const ogEmail = entry.value;
entry.readOnly = false;
entry.classList.remove('form-control-plaintext');
entry.classList.add('form-control');
if (ogEmail == "") {
entry.placeholder = 'Address';
}
const tick = createEl(`
<i class="fa fa-check d-inline-block icon-button text-success" style="margin-left: 0.5rem; margin-right: 0.5rem;"></i>
`);
tick.onclick = (): void => {
const newEmail = entry.value;
if (!validateEmail(newEmail) || newEmail == ogEmail) {
return;
}
cross.remove();
const spinner = createEl(`
<div class="spinner-border spinner-border-sm" role="status" style="width: 1rem; height: 1rem; margin-left: 0.5rem;">
<span class="sr-only">Saving...</span>
</div>
`);
tick.replaceWith(spinner);
let send = {};
send[id] = newEmail;
_post("/users/emails", send, function (): void {
if (this.readyState == 4) {
if (this.status == 200 || this.status == 204) {
entry.nextElementSibling.remove();
} else {
entry.value = ogEmail;
}
}
});
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
};
const cross = createEl(`
<i class="fa fa-close d-inline-block icon-button text-danger"></i>
`);
cross.onclick = (): void => {
tick.remove();
cross.remove();
icon.outerHTML = iconContent;
entry.readOnly = true;
entry.classList.remove('form-control');
entry.classList.add('form-control-plaintext');
entry.placeholder = '';
entry.value = ogEmail;
};
icon.parentNode.appendChild(tick);
icon.parentNode.appendChild(cross);
};
export function populateUsers(): void {
const acList = document.getElementById('accountsList');
acList.innerHTML = `
@ -53,10 +115,6 @@ export function populateUsers(): void {
return entry.outerHTML;
};
const template = (id: string, username: string, email: string, lastActive: string, admin: boolean): string => {
let isAdmin = "No";
if (admin) {
isAdmin = "Yes";
}
let fci = "form-check-input";
if (window.bsVersion != 5) {
fci = "";

View File

@ -1,14 +1,8 @@
import { rmAttr, addAttr, _post, _get, _delete } from "../modules/common.js";
import { rmAttr, addAttr, _post, _get, _delete, createEl } from "../modules/common.js";
export const Focus = (el: HTMLElement): void => rmAttr(el, 'unfocused');
export const Unfocus = (el: HTMLElement): void => addAttr(el, 'unfocused');
export function createEl(html: string): HTMLElement {
let div = document.createElement('div') as HTMLDivElement;
div.innerHTML = html;
return div.firstElementChild as HTMLElement;
}
export function storeDefaults(users: string | Array<string>): void {
const button = document.getElementById('storeDefaults') as HTMLButtonElement;
button.disabled = true;

View File

@ -1,5 +1,11 @@
declare var window: Window;
export function createEl(html: string): HTMLElement {
let div = document.createElement('div') as HTMLDivElement;
div.innerHTML = html;
return div.firstElementChild as HTMLElement;
}
export function serializeForm(id: string): Object {
const form = document.getElementById(id) as HTMLFormElement;
let formData = {};