From a1e30ff5db86875615548b7f85c81a0fe49b4b0a Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Thu, 3 Dec 2020 20:49:50 +0000 Subject: [PATCH] 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. --- %1 | 0 api.go | 17 +++++++++-- nohup.out | 8 +++++ ts/accounts.ts | 68 +++--------------------------------------- ts/modules/accounts.ts | 68 ++++++++++++++++++++++++++++++++++++++---- ts/modules/admin.ts | 8 +---- ts/modules/common.ts | 6 ++++ 7 files changed, 96 insertions(+), 79 deletions(-) create mode 100644 %1 create mode 100644 nohup.out diff --git a/%1 b/%1 new file mode 100644 index 0000000..e69de29 diff --git a/api.go b/api.go index f8067a4..e01920f 100644 --- a/api.go +++ b/api.go @@ -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) diff --git a/nohup.out b/nohup.out new file mode 100644 index 0000000..8d292ec --- /dev/null +++ b/nohup.out @@ -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() diff --git a/ts/accounts.ts b/ts/accounts.ts index 6f74a7e..5955537 100644 --- a/ts/accounts.ts +++ b/ts/accounts.ts @@ -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(` - - `); - tick.onclick = (): void => { - const newEmail = entry.value; - if (!validateEmail(newEmail) || newEmail == ogEmail) { - return; - } - cross.remove(); - const spinner = createEl(` -
- Saving... -
- `); - 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(` - - `); - 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; (document.getElementById('selectAll')).onclick = function (): void { const checkboxes: NodeListOf = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]'); diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index e72243c..1d5e661 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -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(` + + `); + tick.onclick = (): void => { + const newEmail = entry.value; + if (!validateEmail(newEmail) || newEmail == ogEmail) { + return; + } + cross.remove(); + const spinner = createEl(` +
+ Saving... +
+ `); + 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(` + + `); + 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 = ""; diff --git a/ts/modules/admin.ts b/ts/modules/admin.ts index 7fb983d..ade7cb8 100644 --- a/ts/modules/admin.ts +++ b/ts/modules/admin.ts @@ -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): void { const button = document.getElementById('storeDefaults') as HTMLButtonElement; button.disabled = true; diff --git a/ts/modules/common.ts b/ts/modules/common.ts index c6b25be..39ba0d2 100644 --- a/ts/modules/common.ts +++ b/ts/modules/common.ts @@ -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 = {};