feat: filter uploads using relative time duration

added a new production dependency parse-duration@~1.0.2

read filters help popup in dashboard for usage example
This commit is contained in:
Bobby 2022-09-24 09:54:49 +07:00
parent 94cfb8617c
commit aebeb8e045
No known key found for this signature in database
GPG Key ID: 941839794CBF5A09
4 changed files with 64 additions and 16 deletions

View File

@ -1,6 +1,7 @@
const blake3 = require('blake3')
const contentDisposition = require('content-disposition')
const fs = require('fs')
const parseDuration = require('parse-duration')
const path = require('path')
const randomstring = require('randomstring')
const searchQuery = require('search-query-parser')
@ -1219,6 +1220,12 @@ self.list = async (req, res) => {
const MAX_SORT_KEYS = 2
const MAX_IS_KEYS = 1
// Timezone offset
let timezoneOffset = 0
if (minoffset !== undefined) {
timezoneOffset = 60000 * (utils.timezoneOffset - minoffset)
}
const filterObj = {
uploaders: [],
excludeUploaders: [],
@ -1388,17 +1395,12 @@ self.list = async (req, res) => {
}
}
const parseDate = (date, minoffset, resetMs) => {
let offset = 0
if (minoffset !== undefined) {
offset = 60000 * (utils.timezoneOffset - minoffset)
}
const parseDate = (date, resetMs) => {
// [YYYY][/MM][/DD] [HH][:MM][:SS]
// e.g. 2020/01/01 00:00:00, 2018/01/01 06, 2019/11, 12:34:00
const formattedMatch = date.match(/^(\d{4})?(\/\d{2})?(\/\d{2})?\s?(\d{2})?(:\d{2})?(:\d{2})?$/)
if (formattedMatch) {
const dateObj = new Date(Date.now() + offset)
const dateObj = new Date(Date.now() + timezoneOffset)
if (formattedMatch[1] !== undefined) {
dateObj.setFullYear(Number(formattedMatch[1]), // full year
@ -1417,24 +1419,58 @@ self.list = async (req, res) => {
}
// Calculate timezone differences
return new Date(dateObj.getTime() - offset)
} else if (/^\d+/.test(date)) {
return new Date(dateObj.getTime() - timezoneOffset)
} else if (/^\d+$/.test(date)) {
// Unix timestamps (always assume seconds resolution)
return new Date(parseInt(date) * 1000)
} else {
}
return null
}
const parseRelativeDuration = (operator, duration, resetMs, inverse = false) => {
let milliseconds = parseDuration(duration)
if (isNaN(milliseconds) || typeof milliseconds !== 'number') {
return null
}
let from = operator === '<'
if (inverse) {
// Intended for "expiry" column, as it essentially has to do the opposite
from = !from
milliseconds = -milliseconds
}
const dateObj = new Date(Date.now() + timezoneOffset - milliseconds)
if (resetMs) {
dateObj.setMilliseconds(0)
}
const range = { from: null, to: null }
const offsetDateObj = new Date(dateObj.getTime() - timezoneOffset)
if (from) {
range.from = Math.floor(offsetDateObj / 1000)
} else {
range.to = Math.ceil(offsetDateObj / 1000)
}
return range
}
// Parse dates to timestamps
for (const range of ranges) {
if (filterObj.queries[range]) {
if (filterObj.queries[range].from) {
const parsed = parseDate(filterObj.queries[range].from, minoffset, true)
const relativeMatch = filterObj.queries[range].from.match(/^(<|>)(.*)$/)
if (relativeMatch && relativeMatch[2]) {
// Human-readable relative duration
filterObj.queries[range] = parseRelativeDuration(relativeMatch[1], relativeMatch[2], true, (range === 'expiry'))
continue
} else {
const parsed = parseDate(filterObj.queries[range].from, true)
filterObj.queries[range].from = parsed ? Math.floor(parsed / 1000) : null
}
}
if (filterObj.queries[range].to) {
const parsed = parseDate(filterObj.queries[range].to, minoffset, true)
const parsed = parseDate(filterObj.queries[range].to, true)
filterObj.queries[range].to = parsed ? Math.ceil(parsed / 1000) : null
}
}

View File

@ -53,6 +53,7 @@
"markdown-it": "~13.0.1",
"node-fetch": "~2.6.7",
"nunjucks": "~3.2.3",
"parse-duration": "~1.0.2",
"randomstring": "~1.2.2",
"range-parser": "~1.2.1",
"rate-limiter-flexible": "~2.3.10",

View File

@ -1163,14 +1163,14 @@ page.uploadFiltersHelp = element => {
Negation sign can also be used to exclude uploads with no albums (i.e. <code>-albumid:-</code>).`}
There are 2 range keys: <b>date</b> (upload date) and <b>expiry</b> (expiry date).
Their format is: <code>"YYYY/MM/DD HH:MM:SS-YYYY/MM/DD HH:MM:SS"</code> ("from" date and "to" date respectively),
OR unix timestamps in seconds resolution.
Their formats are: <code>"YYYY/MM/DD HH:MM:SS-YYYY/MM/DD HH:MM:SS"</code> ("from" date and "to" date respectively),
unix timestamps in seconds resolution, OR human-readable relative time duration (<a href="https://github.com/jkroso/parse-duration/tree/50ebcc8a971c753bd1162332ccf5f3ef1e0b3a7e#available-unit-types-are" target="_blank" rel="noopener">available units</a>).
You may choose to specify only either dates.
If "to" date is missing, 'now' will be used. If "from" date is missing, 'beginning of time' will be used.
If any of the subsequent date or time units are not specified, their first value will be used (e.g. January for month, 1 for day, and so on).
If only time is specified, today's date will be used.
If you do not need to specify both date and time, you may omit the double quotes.
In conclusion, the following examples are all valid: <code>date:"2020/01/01 01:23-2018/01/01 06"</code>, <code>expiry:-2020/05</code>, <code>date:12:34:56</code>.
In conclusion, the following examples are all valid: <code>date:"2020/01/01 01:23-2018/01/01 06"</code>, <code>expiry:-2020/05</code>, <code>date:12:34:56</code>, <code>date:1663976000</code>, <code>date:<7days</code>.
<b>date</b> and <b>expiry</b> keys can only be specified once each.
<b>What about timezones?</b>
@ -1224,6 +1224,12 @@ page.uploadFiltersHelp = element => {
<code>date:"2020/04/07 12-2020/04/07 23:59:59"</code>
- Uploads uploaded before "5 February 2020 00:00:00":
<code>date:-2020/02/05</code>
- Uploads uploaded within the last 24 hours (1 day):
<code>date:<1d</code>
- Uploads uploaded before the last 6 months:
<code>date:>6months</code>
- Uploads that will expire within the next 7 days and 12 hours:
<code>expiry:"<7 days 12 hours"</code>
- Uploads which file names match "*.gz" but NOT "*.tar.gz":
<code>*.gz -*.tar.gz</code>
- Sort matches by "size" column in ascending and descending order respectively:

View File

@ -4365,6 +4365,11 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
parse-duration@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-duration/-/parse-duration-1.0.2.tgz#b9aa7d3a1363cc7e8845bea8fd3baf8a11df5805"
integrity sha512-Dg27N6mfok+ow1a2rj/nRjtCfaKrHUZV2SJpEn/s8GaVUSlf4GGRCRP1c13Hj+wfPKVMrFDqLMLITkYKgKxyyg==
parse-filepath@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891"