Merge pull request #361 from TheophileDiot/dev

Changes on jobs in the back and the front
This commit is contained in:
Théophile Diot 2022-11-17 19:15:19 +01:00 committed by GitHub
commit 2087167228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 272 additions and 386 deletions

View File

@ -2,7 +2,7 @@
from ipaddress import ip_address, ip_network
from os import _exit, getenv, makedirs
from re import match
from re import IGNORECASE, compile as re_compile
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
@ -16,6 +16,10 @@ from Database import Database
from logger import setup_logger
from jobs import cache_file, cache_hash, is_cached_file, file_hash
rdns_rx = re_compile(r"^(\.?[a-z\d\-]+)*\.[a-z]{2,}$", IGNORECASE)
asn_rx = re_compile(r"^\d+$")
uri_rx = re_compile(r"^/")
def check_line(kind, line):
if kind == "IP":
@ -33,19 +37,19 @@ def check_line(kind, line):
pass
return False, ""
elif kind == "RDNS":
if match(r"^(\.?[A-Za-z0-9\-]+)*\.[A-Za-z]{2,}$", line):
if rdns_rx.match(line):
return True, line.lower()
return False, ""
elif kind == "ASN":
real_line = line.replace("AS", "")
if match(r"^\d+$", real_line):
real_line = line.replace("AS", "").replace("as", "")
if asn_rx.match(real_line):
return True, real_line
elif kind == "USER_AGENT":
return True, line.replace("\\ ", " ").replace("\\.", "%.").replace(
"\\\\", "\\"
).replace("-", "%-")
elif kind == "URI":
if match(r"^/", line):
if uri_rx.match(line):
return True, line
return False, ""

View File

@ -2,7 +2,7 @@
from ipaddress import ip_address, ip_network
from os import _exit, getenv, makedirs
from re import match
from re import IGNORECASE, compile as re_compile
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
@ -16,6 +16,10 @@ from Database import Database
from logger import setup_logger
from jobs import cache_file, cache_hash, is_cached_file, file_hash
rdns_rx = re_compile(r"^(\.?[a-z\d\-]+)*\.[a-z]{2,}$", IGNORECASE)
asn_rx = re_compile(r"^\d+$")
uri_rx = re_compile(r"^/")
def check_line(kind, line):
if kind == "IP":
@ -33,19 +37,19 @@ def check_line(kind, line):
pass
return False, ""
elif kind == "RDNS":
if match(r"^(\.?[A-Za-z0-9\-]+)*\.[A-Za-z]{2,}$", line):
if rdns_rx.match(line):
return True, line.lower()
return False, ""
elif kind == "ASN":
real_line = line.replace("AS", "")
if match(r"^\d+$", real_line):
real_line = line.replace("AS", "").replace("as", "")
if asn_rx.match(real_line):
return True, real_line
elif kind == "USER_AGENT":
return True, line.replace("\\ ", " ").replace("\\.", "%.").replace(
"\\\\", "\\"
).replace("-", "%-")
elif kind == "URI":
if match(r"^/", line):
if uri_rx.match(line):
return True, line
return False, ""

View File

@ -2,7 +2,7 @@
from ipaddress import ip_address, ip_network
from os import _exit, getenv, makedirs
from re import match
from re import IGNORECASE, compile as re_compile
from sys import exit as sys_exit, path as sys_path
from traceback import format_exc
@ -16,6 +16,10 @@ from Database import Database
from logger import setup_logger
from jobs import cache_file, cache_hash, is_cached_file, file_hash
rdns_rx = re_compile(r"^(\.?[a-z\d\-]+)*\.[a-z]{2,}$", IGNORECASE)
asn_rx = re_compile(r"^\d+$")
uri_rx = re_compile(r"^/")
def check_line(kind, line):
if kind == "IP":
@ -33,19 +37,19 @@ def check_line(kind, line):
pass
return False, ""
elif kind == "RDNS":
if match(r"^(\.?[A-Za-z0-9\-]+)*\.[A-Za-z]{2,}$", line):
if rdns_rx.match(line):
return True, line.lower()
return False, ""
elif kind == "ASN":
real_line = line.replace("AS", "")
if match(r"^\d+$", real_line):
real_line = line.replace("AS", "").replace("as", "")
if asn_rx.match(real_line):
return True, real_line
elif kind == "USER_AGENT":
return True, line.replace("\\ ", " ").replace("\\.", "%.").replace(
"\\\\", "\\"
).replace("-", "%-")
elif kind == "URI":
if match(r"^/", line):
if uri_rx.match(line):
return True, line
return False, ""

View File

@ -1,8 +1,10 @@
from copy import deepcopy
from glob import glob
from json import loads
from logging import Logger
from os import environ, getenv
from subprocess import DEVNULL, PIPE, STDOUT, run
from threading import Lock, Thread
from schedule import (
clear as schedule_clear,
every as schedule_every,
@ -36,6 +38,8 @@ class JobScheduler(ApiCaller):
self.__env.update(environ)
self.__jobs = self.__get_jobs()
self.__lock = lock
self.__thread_lock = Lock()
self.__job_success = True
def __get_jobs(self):
jobs = {}
@ -101,7 +105,6 @@ class JobScheduler(ApiCaller):
self.__logger.info(
f"Executing job {name} from plugin {plugin} ...",
)
success = True
try:
proc = run(
f"{path}/jobs/{file}",
@ -109,29 +112,31 @@ class JobScheduler(ApiCaller):
stderr=STDOUT,
env=self.__env,
)
except:
self.__logger.error(
f"Exception while executing job {name} from plugin {plugin} :\n{format_exc()}",
)
success = False
if success and proc.returncode >= 2:
self.__logger.error(
f"Error while executing job {name} from plugin {plugin}",
)
success = False
except BaseException:
with self.__thread_lock:
self.__logger.error(
f"Exception while executing job {name} from plugin {plugin} :\n{format_exc()}",
)
self.__job_success = False
err = self.__db.update_job(plugin, name, success)
if self.__job_success and proc.returncode >= 2:
with self.__thread_lock:
self.__logger.error(
f"Error while executing job {name} from plugin {plugin}",
)
self.__job_success = False
if not err:
self.__logger.info(
f"Successfully updated database for the job {name} from plugin {plugin}",
)
else:
self.__logger.warning(
f"Failed to update database for the job {name} from plugin {plugin}: {err}",
)
with self.__thread_lock:
err = self.__db.update_job(plugin, name, self.__job_success)
return success
if not err:
self.__logger.info(
f"Successfully updated database for the job {name} from plugin {plugin}",
)
else:
self.__logger.warning(
f"Failed to update database for the job {name} from plugin {plugin}: {err}",
)
def setup(self):
for plugin, jobs in self.__jobs.items():
@ -184,19 +189,25 @@ class JobScheduler(ApiCaller):
def run_once(self):
ret = True
threads = []
for plugin, jobs in self.__jobs.items():
for job in jobs:
try:
path = job["path"]
name = job["name"]
file = job["file"]
if self.__job_wrapper(path, plugin, name, file) >= 2:
ret = False
except BaseException:
ret = False
self.__logger.error(
f"Exception while running jobs once for plugin {plugin} : {format_exc()}",
)
path = job["path"]
name = job["name"]
file = job["file"]
thread = Thread(
target=self.__job_wrapper, args=(path, plugin, name, file)
)
threads.append(thread)
for thread in threads:
thread.start()
for thread in threads:
thread.join()
ret = self.__job_success
self.__job_success = True
return ret

View File

@ -827,14 +827,18 @@ h6 {
grid-column: span 3 / span 3;
}
.col-span-9 {
grid-column: span 9 / span 9;
}
.col-span-2 {
grid-column: span 2 / span 2;
}
.col-span-4 {
grid-column: span 4 / span 4;
}
.col-span-9 {
grid-column: span 9 / span 9;
}
.col-auto {
grid-column: auto;
}
@ -903,16 +907,16 @@ h6 {
margin-right: 0.25rem;
}
.my-auto {
margin-top: auto;
margin-bottom: auto;
}
.my-1 {
margin-top: 0.25rem;
margin-bottom: 0.25rem;
}
.my-auto {
margin-top: auto;
margin-bottom: auto;
}
.my-4 {
margin-top: 1rem;
margin-bottom: 1rem;
@ -1007,10 +1011,6 @@ h6 {
margin-left: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mb-7 {
margin-bottom: 1.75rem;
}
@ -1019,6 +1019,10 @@ h6 {
margin-top: 0.25rem;
}
.mb-4 {
margin-bottom: 1rem;
}
.mt-0 {
margin-top: 0px;
}
@ -1139,14 +1143,14 @@ h6 {
height: 3rem;
}
.h-16 {
height: 4rem;
}
.h-3 {
height: 0.75rem;
}
.h-16 {
height: 4rem;
}
.h-19 {
height: 4.75rem;
}
@ -1159,6 +1163,10 @@ h6 {
height: calc(100vh - 360px);
}
.h-5\.5 {
height: 1.375rem;
}
.h-30 {
height: 7.5rem;
}
@ -1179,10 +1187,6 @@ h6 {
height: 33.333333%;
}
.h-5\.5 {
height: 1.375rem;
}
.max-h-screen {
max-height: 100vh;
}
@ -1263,10 +1267,6 @@ h6 {
width: 3rem;
}
.w-60 {
width: 15rem;
}
.w-5 {
width: 1.25rem;
}
@ -1275,6 +1275,10 @@ h6 {
width: 0.75rem;
}
.w-60 {
width: 15rem;
}
.w-28 {
width: 7rem;
}
@ -1283,6 +1287,10 @@ h6 {
width: auto;
}
.w-5\.5 {
width: 1.375rem;
}
.w-90 {
width: 22.5rem;
}
@ -1295,10 +1303,6 @@ h6 {
width: 1.75rem;
}
.w-5\.5 {
width: 1.375rem;
}
.min-w-0 {
min-width: 0px;
}
@ -1397,13 +1401,13 @@ h6 {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.translate-y-12 {
--tw-translate-y: 3rem;
.translate-x-1 {
--tw-translate-x: 0.25rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.translate-x-1 {
--tw-translate-x: 0.25rem;
.translate-y-12 {
--tw-translate-y: 3rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
@ -1566,6 +1570,11 @@ h6 {
row-gap: 0.5rem;
}
.gap-x-1 {
-moz-column-gap: 0.25rem;
column-gap: 0.25rem;
}
.overflow-auto {
overflow: auto;
}
@ -1918,6 +1927,10 @@ h6 {
fill: #adb5bd;
}
.fill-green-500 {
fill: #22c55e;
}
.fill-blue-400 {
fill: #60a5fa;
}
@ -1934,26 +1947,14 @@ h6 {
fill: #db2777;
}
.fill-slate-800 {
fill: #3a416f;
}
.fill-green-500 {
fill: #22c55e;
}
.fill-emerald-500 {
fill: #2dce89;
}
.fill-emerald-800 {
fill: #065f46;
}
.fill-emerald-700 {
fill: #047857;
}
.fill-slate-800 {
fill: #3a416f;
}
.stroke-0 {
stroke-width: 0;
}
@ -2104,14 +2105,14 @@ h6 {
padding-left: 0.5rem;
}
.pt-5 {
padding-top: 1.25rem;
}
.pb-2 {
padding-bottom: 0.5rem;
}
.pt-5 {
padding-top: 1.25rem;
}
.pl-6 {
padding-left: 1.5rem;
}
@ -2922,10 +2923,6 @@ h6 {
fill: #adb5bd;
}
.dark .dark\:fill-emerald-500 {
fill: #2dce89;
}
.dark .dark\:text-white {
--tw-text-opacity: 1;
color: rgb(255 255 255 / var(--tw-text-opacity));
@ -3267,6 +3264,22 @@ h6 {
grid-column: span 6 / span 6;
}
.lg\:col-span-12 {
grid-column: span 12 / span 12;
}
.lg\:col-span-9 {
grid-column: span 9 / span 9;
}
.lg\:col-span-3 {
grid-column: span 3 / span 3;
}
.lg\:col-span-8 {
grid-column: span 8 / span 8;
}
.lg\:mx-8 {
margin-left: 2rem;
margin-right: 2rem;

View File

@ -43,7 +43,9 @@ class LogsDropdown {
this.hideDropdown(btnSetting);
this.changeDropBtnStyle(btnSetting, btn);
//show / hide filter
this.hideFilterOnLocal(btn.getAttribute("_type"));
if (btnSetting === "instances") {
this.hideFilterOnLocal(btn.getAttribute("_type"));
}
}
} catch (err) {}
});
@ -150,52 +152,56 @@ class LogsDropdown {
class FetchLogs {
constructor(prefix = "logs") {
this.prefix = prefix;
this.instanceInp = document.querySelector(
`button[${this.prefix}-setting-select='instances']`
this.instance = document.querySelector(
`[${this.prefix}-setting-select-text="instances"]`
);
this.instanceName = "";
this.updateInp = document.querySelector("input#update-date");
this.liveUpdateInp = document.querySelector("input#live-update");
this.updateDelayInp = document.querySelector("input#update-delay");
this.fromDateInp = document.querySelector("input#from-date");
this.toDateInp = document.querySelector("input#to-date");
this.fromDate = "";
this.toDate = "";
this.isLiveUpdate = false;
this.updateDelay = 2000;
this.lastUpdate = Math.round(Date.now() / 1000 - 86400);
this.container = document.querySelector(`[${this.prefix}-settings]`);
this.logListContainer = document.querySelector(`[${this.prefix}-list]`);
this.initFetch();
this.initLiveUpdate();
this.submitSettings = document.querySelector("button#submit-settings");
this.init();
}
initLiveUpdate() {
this.liveUpdateInp.addEventListener("click", (e) => {
this.isLiveUpdate = this.liveUpdateInp.checked;
init() {
this.submitSettings.addEventListener("click", (e) => {
//wait if live update previously
if (this.isLiveUpdate) {
setTimeout(() => {
this.setLiveUpdate();
const isSettings = this.getSettings();
return isSettings ? this.getLogsFromToDate() : "";
}, this.updateDelay);
} else {
const isSettings = this.getSettings();
return isSettings ? this.getLogsFromToDate() : "";
}
});
this.updateDelayInp.addEventListener("input", (e) => {
console.log(this.updateDelayInp.valueAsNumber);
this.updateDelay = Math.round(this.updateDelayInp.valueAsNumber / 1000);
console.log(this.updateDelay + " on change");
});
}
setLiveUpdate() {
//loop
if (this.isLiveUpdate) {
setTimeout(() => {
this.setLiveUpdate();
}, this.updateDelay);
}
//conditions to live update
const btnInstance = document.querySelector(
`[${this.prefix}-setting-select-text]`
);
if (btnInstance.textContent === "none" || this.lastUpdate === "") return;
//get data
this.getInstanceLogs(btnInstance.textContent);
getSettings() {
//get settings
this.instanceName = this.instance.textContent;
if (!this.instanceName || this.instanceName.trim() === "none") return false;
this.formDate = this.fromDateInp.valueAsNumber
? this.fromDateInp.valueAsNumber
: Math.round(Date.now() / 1000 - 86400);
this.toDate = this.toDateInp.valueAsNumber
? this.toDateInp.valueAsNumber
: "";
this.updateDelay =
this.updateDelayInp.value * 1000 ? this.updateDelayInp.value : 2000;
this.isLiveUpdate = this.liveUpdateInp.checked;
return true;
}
goBottomList() {
@ -207,40 +213,24 @@ class FetchLogs {
);
}
initFetch() {
this.container.addEventListener("click", (e) => {
//SELECT INSTANCE
try {
if (
e.target
.closest("button")
.getAttribute(`${this.prefix}-setting-select-dropdown-btn`) ===
"instances"
) {
const btn = e.target.closest("button");
const btnValue = btn.getAttribute("value");
//fetch data
this.getInstanceLogs(btnValue);
}
} catch (err) {}
//SELECT DATE
this.updateInp.addEventListener("input", (e) => {
this.lastUpdate = Math.round(this.updateInp.valueAsNumber / 1000);
console.log(this.lastUpdate);
//check if instance selected
const btnInstance = document.querySelector(
`[${this.prefix}-setting-select-text]`
);
if (btnInstance.textContent.trim() === "none") return;
//fetch data
this.getInstanceLogs(btnInstance.textContent);
});
});
async getLogsFromToDate() {
const response = await fetch(
`${location.href}/${this.lastUpdate}?from_date=${this.fromDate}?to_date=${this.toDate}`
);
if (response.status === 200) {
const res = await response.json();
//last update
return await this.showLogs(res);
} else {
console.log(`Error: ${response.status}`);
}
return null;
}
async getInstanceLogs(instanceName) {
async getLogsSinceLastUpdate() {
const response = await fetch(
`${location.href}/${instanceName}` +
`${location.href}/${this.lastUpdate}` +
(this.lastUpdate ? `?last_update=${this.lastUpdate}` : "")
);
@ -256,7 +246,6 @@ class FetchLogs {
async showLogs(res) {
this.lastUpdate = res.last_update;
console.log(this.lastUpdate);
res.logs.forEach((log) => {
//container
const logContainer = document.createElement("li");
@ -279,9 +268,16 @@ class FetchLogs {
//show on DOM
this.logListContainer.appendChild(logContainer);
});
setTimeout(() => {
this.goBottomList();
}, 100);
//loop if true
if (this.isLiveUpdate) {
setTimeout(() => {
this.getLogsSinceLastUpdate();
}, this.updateDelay);
}
}
}
@ -290,8 +286,6 @@ class FilterLogs {
this.prefix = prefix;
this.container = document.querySelector(`[${this.prefix}-filter]`);
this.keyInp = document.querySelector("input#keyword");
this.fromDateInp = document.querySelector("input#from-date");
this.toDateInp = document.querySelector("input#to-date");
this.lastType = "";
this.initHandler();
}
@ -311,7 +305,7 @@ class FilterLogs {
const btnSetting = btn.getAttribute(
`${this.prefix}-setting-select-dropdown-btn`
);
if (this.lastType === btnValue) return;
this.lastType = btnValue;
//run filter
this.filter();
@ -322,14 +316,6 @@ class FilterLogs {
this.keyInp.addEventListener("input", (e) => {
this.filter();
});
//FROM DATE
this.fromDateInp.addEventListener("input", (e) => {
this.filter();
});
//TO DATE
this.toDateInp.addEventListener("input", (e) => {
this.filter();
});
}
filter() {
@ -343,23 +329,19 @@ class FilterLogs {
//filter type
this.setFilterType(logs);
this.setFilterKeyword(logs);
console.log("start date filtering");
this.setFilterDate(logs);
}
setFilterType(logs) {
const type = document.querySelector(
`[${this.prefix}-setting-select-text="types"]`
).textContent;
if (type === "all") return;
if (this.lastType === "all") return;
for (let i = 0; i < logs.length; i++) {
const el = logs[i];
const typeEl = el.querySelector("[logs-type]");
if (type !== "misc" && typeEl.textContent !== type)
if (this.lastType !== "misc" && typeEl.textContent !== this.lastType)
el.classList.add("hidden");
if (
type === "misc" &&
this.lastType === "misc" &&
typeEl.textContent !== "info" &&
typeEl.textContent !== "message"
)
@ -376,124 +358,6 @@ class FilterLogs {
if (!content.includes(keyword)) el.classList.add("hidden");
}
}
setFilterDate(logs) {
//get date as timestamp to check if valid
const isValidDate = this.isDateIntervalValid(
this.fromDateInp.valueAsNumber,
this.toDateInp.valueAsNumber
);
if (!isValidDate) return console.log("date invalid");
this.setFromDate(logs);
this.setToDate(logs);
}
setToDate(logs) {
//if from date value exist
if (this.toDateInp.valueAsNumber) {
//get it
const toDate = this.toDateInp.value.split("-");
const toMonthStr = this.monthNumToStr(toDate[1]);
//stop if last log is after to date
const isAfter = this.isToDateAfterLastLog();
if (isAfter === true) return console.log("after");
//else loop from bottom to top list
for (let i = 0; i < logs.length; i++) {
const el = logs[logs.length - 1 - i];
const content = el.querySelector("[logs-content]").textContent;
//all date format we can find on logs
if (
content.includes(`${toDate[0]}/${toDate[1]}/${toDate[2]}`) ||
content.includes(`${toDate[0]}-${toDate[1]}-${toDate[2]}`) ||
content.includes(`${toDate[0]}/${toMonthStr}/${toDate[2]}`)
)
break;
el.classList.add("hidden");
}
}
}
isToDateAfterLastLog() {
const lastLogContent = document
.querySelector(`[${this.prefix}-list]`)
.lastElementChild.querySelector("[logs-content]")
.textContent.replaceAll(":", " ")
.replaceAll("[", "[ ");
const lastLogDate = lastLogContent.match(
/(\d{4}([\/.-])(\d{2}|\D{3})([\/.-])\d{2} | \d{2}([\/])(\d{2}|\D{3})([\/])\d{4})/g
);
const lastLogStamp = new Date(lastLogDate).getTime();
console.log(
"last log " + lastLogStamp + " input: " + this.toDateInp.valueAsNumber
);
return this.toDateInp.valueAsNumber > lastLogStamp ? true : false;
}
setFromDate(logs) {
//if from date value exist
if (this.fromDateInp.valueAsNumber) {
//get it
const fromDate = this.fromDateInp.value.split("-");
const fromMonthStr = this.monthNumToStr(fromDate[1]);
//stop if first log is before date
const isBefore = this.isFromDateBeforeFirstlog();
if (isBefore === true) return;
//else loop from top to bottom list
for (let i = 0; i < logs.length; i++) {
const el = logs[i];
const content = el.querySelector("[logs-content]").textContent;
//all date format we can find on logs
if (
content.includes(`${fromDate[0]}/${fromDate[1]}/${fromDate[2]}`) ||
content.includes(`${fromDate[0]}-${fromDate[1]}-${fromDate[2]}`) ||
content.includes(`${fromDate[0]}/${fromMonthStr}/${fromDate[2]}`)
)
break;
el.classList.add("hidden");
}
}
}
isFromDateBeforeFirstlog() {
const firstLogContent = document
.querySelector(`[${this.prefix}-list]`)
.firstElementChild.querySelector("[logs-content]").textContent;
const firstLogDate = firstLogContent.match(
/(\d{4}([\/.-])(\d{2}|\D{3})([\/.-])\d{2} | \d{2}([\/])(\d{2}|\D{3})([\/])\d{4})/g
);
const firstLogStamp = new Date(firstLogDate).getTime();
return this.fromDateInp.valueAsNumber <= firstLogStamp ? true : false;
}
isDateIntervalValid(fromDate, toDate) {
//case one date
if (fromDate === NaN || toDate === NaN) return false;
if (fromDate >= toDate) return false;
return true;
}
monthNumToStr(month) {
const monthStr = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
];
return monthStr[+month - 1];
}
}
const setCheckbox = new Checkbox("[logs-settings]");

View File

@ -3,7 +3,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<!-- filter -->
<div
{{current_endpoint}}-filter
class="col-span-12 lg:col-span-6 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="col-span-12 lg:col-span-12 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white">FILTER</h5>
<div class="grid grid-cols-12 gap-x-4 gap-y-2">
@ -178,25 +178,25 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
<p
class="dark:text-gray-300 h-8 text-sm font-bold col-span-2 m-0 pb-2 border-b border-gray-400"
>
plugin
Last run
</p>
<p
class="dark:text-gray-300 h-8 text-sm font-bold col-span-2 m-0 pb-2 border-b border-gray-400"
>
Date
Every
</p>
<p
class="dark:text-gray-300 h-8 text-sm font-bold col-span-1 m-0 pb-2 border-b border-gray-400"
class="dark:text-gray-300 h-8 text-sm font-bold col-span-2 m-0 pb-2 border-b border-gray-400"
>
Reload
</p>
<p
class="dark:text-gray-300 h-8 text-sm font-bold col-span-1 m-0 pb-2 border-b border-gray-400"
class="dark:text-gray-300 h-8 text-sm font-bold col-span-2 m-0 pb-2 border-b border-gray-400"
>
Success
</p>
<p
class="dark:text-gray-300 h-8 text-sm font-bold col-span-3 m-0 pb-2 border-b border-gray-400"
class="dark:text-gray-300 h-8 text-sm font-bold col-span-1 m-0 pb-2 border-b border-gray-400"
>
Files
</p>
@ -206,20 +206,20 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
class="col-span-12 w-full max-h-100 overflow-y-auto"
{{current_endpoint}}-list
>
{{% for job in jobs %}}
{% for job in jobs %}
<!-- job item-->
<li class="grid grid-cols-12 border-b border-gray-300 py-2">
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-3 m-0"
{{current_endpoint}}-name
>
{{job["name"]}}
{{job}}
</p>
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-2 m-0"
{{current_endpoint}}-plugin-id
{{current_endpoint}}-last_run
>
{{job["plugin_id"]}}
{{job["last_run"]}}
</p>
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-2 m-0"
@ -228,10 +228,10 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
{{job["every"]}}
</p>
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-1 m-0"
class="ml-6 dark:text-gray-400 dark:opacity-80 text-sm col-span-2 m-0"
{{current_endpoint}}-reload
>
{% if job["reload"]%}
{% if job["reload"] %}
<svg
class="fill-green-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
@ -241,7 +241,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"
/>
</svg>
{% else %}
{%endif %} {% if not job["reload"] %}
<svg
class="fill-red-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
@ -251,23 +251,13 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z"
/>
</svg>
{% else %}
<svg
class="fill-sky-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path
d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM288 352c0 17.7-14.3 32-32 32s-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32z"
/>
</svg>
{% endif %}
</p>
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-1 m-0"
class="ml-6 dark:text-gray-400 dark:opacity-80 text-sm col-span-2 m-0"
{{current_endpoint}}-success
>
{% if job["success"] %}
{% if job["success"] == True %}
<svg
class="fill-green-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
@ -300,14 +290,12 @@ url_for(request.endpoint)[1:].split("/")[-1].strip() %}
{% endif %}
</p>
<p
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-3 m-0"
{{current_endpoint}}-cache_file
>
{{job["cache_file"]}}
</p>
class="dark:text-gray-400 dark:opacity-80 text-sm col-span-1 m-0"
{{current_endpoint}}-files
></p>
</li>
<!-- end job item-->
{{% endfor %}}
{% endfor %}
</ul>
<!-- end list-->
</div>

View File

@ -4,12 +4,14 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- settings -->
<div
{{current_endpoint}}-settings
class="col-span-12 lg:col-span-6 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="col-span-12 lg:col-span-8 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white">SETTINGS</h5>
<div class="grid grid-cols-12 gap-x-4 gap-y-2">
<!-- select instance -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div
class="flex flex-col relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
@ -60,26 +62,49 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
</div>
<!-- end select instance -->
<!-- from date input -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div
class="flex flex-col relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
Fetch (default 1 day)
From date
</h5>
<input
datetimepicker
type="date"
id="update-date"
name="update-date"
class="col-span-12 sm:col-span-6 lg:col-span-4 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="update date"
id="from-date"
name="from-date"
class="col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="from date"
pattern="(.*?)"
required
/>
</div>
<!-- end from date input -->
<!-- to date input -->
<div
class="flex flex-col relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
To date
</h5>
<input
type="date"
id="to-date"
name="to-date"
class="col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="to date"
pattern="(.*?)"
required
/>
</div>
<!-- end to date input -->
<!-- refresh input -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div
class="flex flex-col relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
@ -107,7 +132,9 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
</div>
<!-- end refresh input -->
<!-- refresh delay input -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div
class="flex flex-col relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3"
>
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
@ -117,13 +144,22 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
type="number"
id="update-delay"
name="update-delay"
class="col-span-12 sm:col-span-6 lg:col-span-4 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
class="col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="2"
pattern="(.*?)"
required
/>
</div>
<!-- end refresh delay input -->
<div class="col-span-12 w-full justify-center flex">
<button
type="button"
id="submit-settings"
class="dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-gradient-to-tl bg-primary hover:bg-primary/80 focus:bg-primary/80 leading-normal text-sm ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
>
Submit
</button>
</div>
</div>
</div>
<!-- end settings -->
@ -131,12 +167,12 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- filter -->
<div
{{current_endpoint}}-filter
class="col-span-12 lg:col-span-6 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
class="col-span-12 lg:col-span-4 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border"
>
<h5 class="mb-2 font-bold dark:text-white">FILTERS</h5>
<div class="grid grid-cols-12 gap-x-4 gap-y-2">
<!-- search inpt-->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div class="flex flex-col relative col-span-12">
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
@ -146,7 +182,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
type="text"
id="keyword"
name="keyword"
class="col-span-12 sm:col-span-6 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
class="col-span-12 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="key words"
pattern="(.*?)"
required
@ -154,7 +190,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
</div>
<!-- end search inpt-->
<!-- select types -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<div class="flex flex-col relative col-span-12">
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
@ -241,44 +277,6 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- end dropdown-->
</div>
<!-- end select types -->
<!-- from date input -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
From date
</h5>
<input
datetimepicker
type="date"
id="from-date"
name="from-date"
class="col-span-12 sm:col-span-6 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="from date"
pattern="(.*?)"
required
/>
</div>
<!-- end from date input -->
<!-- to date input -->
<div class="flex flex-col relative col-span-12 sm:col-span-6">
<h5
class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300"
>
To date
</h5>
<input
datetimepicker
type="date"
id="to-date"
name="to-date"
class="col-span-12 sm:col-span-6 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="to date"
pattern="(.*?)"
required
/>
</div>
<!-- end to date input -->
</div>
</div>
<!-- end filter -->