diff --git a/src/css/dashboard.scss b/src/css/dashboard.scss
index 87955ca..4008e43 100644
--- a/src/css/dashboard.scss
+++ b/src/css/dashboard.scss
@@ -140,7 +140,7 @@ li[data-action="page-ellipsis"] {
}
}
-#statistics {
+.statistics {
thead,
tbody {
> tr {
@@ -179,6 +179,10 @@ li[data-action="page-ellipsis"] {
text-decoration: dotted underline
}
}
+
+ i[data-action] {
+ cursor: pointer
+ }
}
}
diff --git a/src/js/dashboard.js b/src/js/dashboard.js
index 0c65e79..97ae6ea 100644
--- a/src/js/dashboard.js
+++ b/src/js/dashboard.js
@@ -378,6 +378,8 @@ page.domClick = event => {
case 'sort-uploads':
return page.sortUploads(element)
// Statistics
+ case 'reload-stats-category':
+ return page.reloadStatsCategory(element)
case 'filter-uploads-with':
return page.filterUploadsWith(element)
case 'filter-uploads-by-type':
@@ -3129,6 +3131,166 @@ page.paginate = (totalItems, itemsPerPage, currentPage) => {
`
}
+page.buildStatisticTable = (title, stats) => {
+ const meta = []
+ let statsKey = ''
+ let rows = ''
+
+ if (!stats) {
+ rows += `
+
+ Still being generated, please try again later\u2026 |
+ |
+
+ `
+ } else {
+ try {
+ const keys = Object.keys(stats)
+
+ for (let j = 0; j < keys.length; j++) {
+ // Skip meta
+ if (keys[j] === 'meta') {
+ continue
+ }
+
+ const data = stats[keys[j]]
+ const isDataObj = typeof data === 'object' && data
+
+ const type = (isDataObj && data.type) || 'auto'
+ // Skip hidden
+ if (type === 'hidden') {
+ continue
+ }
+
+ const value = isDataObj ? data.value : data
+ let parsed = void 0
+
+ switch (type) {
+ case 'byte':
+ parsed = page.getPrettyBytes(value)
+ break
+ case 'byteUsage': {
+ if (typeof value === 'object') {
+ // Reasoning: https://github.com/sebhildebrandt/systeminformation/issues/464#issuecomment-756406053
+ const totalForPercentage = typeof value.available !== 'undefined'
+ ? (value.used + value.available)
+ : value.total
+ parsed = `${page.getPrettyBytes(value.used)} / ${page.getPrettyBytes(value.total)} (${(value.used / totalForPercentage * 100).toFixed(2)}%)`
+ } else {
+ parsed = value
+ }
+ break
+ }
+ case 'detailed':
+ parsed = `
+
+
+ ${Object.keys(value).map(type => {
+ let detailedValue = void 0
+ switch (typeof value[type]) {
+ case 'number':
+ detailedValue = value[type].toLocaleString()
+ break
+ default:
+ detailedValue = value[type]
+ }
+ return `
+
+ ${type} |
+ ${detailedValue} |
+
+ `
+ }).join('\n')}
+
+
+ `
+ break
+ case 'tempC':
+ // TODO: Unit conversion when required?
+ parsed = typeof value === 'number'
+ ? `${value} C`
+ : value
+ break
+ case 'uptime':
+ parsed = page.getPrettyUptime(value)
+ break
+ case 'unavailable':
+ parsed = 'N/A'
+ break
+ case 'auto':
+ switch (typeof value) {
+ case 'number':
+ parsed = value.toLocaleString()
+ break
+ default:
+ parsed = value
+ }
+ break
+ default:
+ parsed = value
+ }
+
+ let keyAttrs = ''
+ if (isDataObj && data.action) {
+ keyAttrs += ` data-action="${data.action}"`
+ if (data.actionData) {
+ keyAttrs += ` data-action-data="${data.actionData}"`
+ }
+ }
+
+ rows += `
+
+ ${keys[j]} |
+ ${parsed} |
+
+ `
+ }
+
+ if (typeof stats.meta !== 'undefined') {
+ // Reload key
+ if (typeof stats.meta.key === 'string') {
+ statsKey = stats.meta.key
+ meta.push(``)
+ }
+ // generatedOn
+ if (typeof stats.meta.generatedOn !== 'undefined') {
+ meta.push(`Generated on ${page.getPrettyDate(new Date(stats.meta.generatedOn))}`)
+ }
+ // maxAge
+ if (typeof stats.meta.maxAge === 'number') {
+ meta.push(`(${stats.meta.maxAge / 1000}s)`)
+ } else {
+ meta.push('(auto)')
+ }
+ }
+ } catch (error) {
+ rows = `
+
+ Error parsing response. Try again? |
+ |
+
+ `
+ page.onError(error)
+ }
+ }
+
+ return `
+
+
+
+
+ ${title} |
+ ${meta.join(' ')} |
+
+
+
+ ${rows}
+
+
+
+ `
+}
+
page.getStatistics = (params = {}) => {
if (!page.permissions.admin) return swal('An error occurred!', 'You cannot do this!', 'error')
@@ -3148,165 +3310,15 @@ page.getStatistics = (params = {}) => {
}
let content = ''
+
const keys = Object.keys(response.data.stats)
for (let i = 0; i < keys.length; i++) {
- const meta = []
- let rows = ''
-
- if (!response.data.stats[keys[i]]) {
- rows += `
-
- Still being generated, please try again later\u2026 |
- |
-
- `
- } else {
- try {
- const valKeys = Object.keys(response.data.stats[keys[i]])
-
- for (let j = 0; j < valKeys.length; j++) {
- // Skip meta
- if (valKeys[j] === 'meta') {
- continue
- }
-
- const data = response.data.stats[keys[i]][valKeys[j]]
- const isDataObj = typeof data === 'object' && data
-
- const type = (isDataObj && data.type) || 'auto'
- // Skip hidden
- if (type === 'hidden') {
- continue
- }
-
- const value = isDataObj ? data.value : data
- let parsed = void 0
-
- switch (type) {
- case 'byte':
- parsed = page.getPrettyBytes(value)
- break
- case 'byteUsage': {
- if (typeof value === 'object') {
- // Reasoning: https://github.com/sebhildebrandt/systeminformation/issues/464#issuecomment-756406053
- const totalForPercentage = typeof value.available !== 'undefined'
- ? (value.used + value.available)
- : value.total
- parsed = `${page.getPrettyBytes(value.used)} / ${page.getPrettyBytes(value.total)} (${(value.used / totalForPercentage * 100).toFixed(2)}%)`
- } else {
- parsed = value
- }
- break
- }
- case 'detailed':
- parsed = `
-
-
- ${Object.keys(value).map(type => {
- let detailedValue = void 0
- switch (typeof value[type]) {
- case 'number':
- detailedValue = value[type].toLocaleString()
- break
- default:
- detailedValue = value[type]
- }
- return `
-
- ${type} |
- ${detailedValue} |
-
- `
- }).join('\n')}
-
-
- `
- break
- case 'tempC':
- // TODO: Unit conversion when required?
- parsed = typeof value === 'number'
- ? `${value} C`
- : value
- break
- case 'uptime':
- parsed = page.getPrettyUptime(value)
- break
- case 'unavailable':
- parsed = 'N/A'
- break
- case 'auto':
- switch (typeof value) {
- case 'number':
- parsed = value.toLocaleString()
- break
- default:
- parsed = value
- }
- break
- default:
- parsed = value
- }
-
- let keyAttrs = ''
- if (isDataObj && data.action) {
- keyAttrs += ` data-action="${data.action}"`
- if (data.actionData) {
- keyAttrs += ` data-action-data="${data.actionData}"`
- }
- }
-
- rows += `
-
- ${valKeys[j]} |
- ${parsed} |
-
- `
- }
-
- const _meta = response.data.stats[keys[i]].meta
- if (typeof _meta !== 'undefined') {
- // generatedOn
- if (typeof _meta.generatedOn !== 'undefined') {
- meta.push(`Generated on ${page.getPrettyDate(new Date(_meta.generatedOn))}`)
- }
- // maxAge
- if (typeof _meta.maxAge === 'number') {
- meta.push(`(${_meta.maxAge / 1000}s)`)
- } else {
- meta.push('(auto)')
- }
- }
- } catch (error) {
- rows = `
-
- Error parsing response. Try again? |
- |
-
- `
- page.onError(error)
- }
- }
-
- content += `
-
-
-
-
- ${keys[i]} |
- ${meta.join(' ')} |
-
-
-
- ${rows}
-
-
-
- `
+ content += page.buildStatisticTable(keys[i], response.data.stats[keys[i]])
}
if (Array.isArray(response.data.hrtime)) {
content += `
-
+
Time taken: ${response.data.hrtime[0]}s ${Math.ceil(response.data.hrtime[1] / 1000000)}ms.
@@ -3324,6 +3336,68 @@ page.getStatistics = (params = {}) => {
})
}
+page.getStatisticsCategory = (params = {}) => {
+ if (!page.permissions.admin) return swal('An error occurred!', 'You cannot do this!', 'error')
+
+ if (page.isSomethingLoading) return page.warnSomethingLoading()
+
+ if (!params.key) return swal('An error occurred!', 'Missing stats category key!', 'error')
+
+ page.updateTrigger(params.trigger, 'loading')
+
+ const url = `api/stats/${params.key}`
+ axios.get(url).then(response => {
+ if (response.data.success === false) {
+ if (response.data.description === 'No token provided') {
+ return page.verifyToken(page.token)
+ } else {
+ page.updateTrigger(params.trigger)
+ return swal('An error occurred!', response.data.description, 'error')
+ }
+ }
+
+ const [title, stats] = Object.entries(response.data.stats).find(([name, stats]) => {
+ return stats && stats.meta && stats.meta.key === params.key
+ })
+ if (!title) {
+ return swal('An error occurred!', 'Server did not return required stats data.', 'error')
+ }
+
+ const statsTable = document.querySelector(`#stats-${params.key}`)
+ if (!statsTable) return
+
+ statsTable.innerHTML = page.buildStatisticTable(title, stats)
+
+ if (Array.isArray(response.data.hrtime)) {
+ const statsHrTime = document.querySelector('#stats-hrtime')
+ if (statsHrTime) {
+ statsHrTime.innerHTML = `
+
+
+ Time taken: ${response.data.hrtime[0]}s ${Math.ceil(response.data.hrtime[1] / 1000000)}ms.
+
+
+ `
+ }
+ }
+
+ page.updateTrigger(params.trigger, 'active')
+ }).catch(error => {
+ page.updateTrigger(params.trigger)
+ page.onAxiosError(error)
+ })
+}
+
+page.reloadStatsCategory = element => {
+ const key = element.dataset.key
+ if (!key) return
+
+ page.getStatisticsCategory({
+ key,
+ trigger: document.querySelector('#itemStatistics')
+ })
+}
+
window.addEventListener('DOMContentLoaded', () => {
// Polyfill Object.assign()
// eslint-disable-next-line compat/compat