Make it possible to create tasks without timer.
This commit is contained in:
parent
23c74d8447
commit
bdf50d0ed8
5 changed files with 45 additions and 20 deletions
|
|
@ -1007,9 +1007,6 @@ class HttpAPI(Common):
|
||||||
if not item.get("id", None) or not validate_uuid(item.get("id"), version=4):
|
if not item.get("id", None) or not validate_uuid(item.get("id"), version=4):
|
||||||
item["id"] = str(uuid.uuid4())
|
item["id"] = str(uuid.uuid4())
|
||||||
|
|
||||||
if not item.get("timer", None) or str(item.get("timer")).strip() == "":
|
|
||||||
item["timer"] = f"{random.randint(1,59)} */1 * * *" # noqa: S311
|
|
||||||
|
|
||||||
if not item.get("template", None):
|
if not item.get("template", None):
|
||||||
item["template"] = ""
|
item["template"] = ""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ class Tasks(metaclass=Singleton):
|
||||||
self.load()
|
self.load()
|
||||||
self._notify.subscribe(
|
self._notify.subscribe(
|
||||||
Events.TASKS_ADD,
|
Events.TASKS_ADD,
|
||||||
lambda data, _, **kwargs: self.add(**data.data), # noqa: ARG005
|
lambda data, _, **kwargs: self.save(**data.data), # noqa: ARG005
|
||||||
f"{__class__.__name__}.add",
|
f"{__class__.__name__}.add",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -145,6 +145,7 @@ class Tasks(metaclass=Singleton):
|
||||||
for i, task in enumerate(tasks):
|
for i, task in enumerate(tasks):
|
||||||
try:
|
try:
|
||||||
task, task_status = clean_item(task, keys=("cookies", "config"))
|
task, task_status = clean_item(task, keys=("cookies", "config"))
|
||||||
|
self.validate(task)
|
||||||
task = Task(**task)
|
task = Task(**task)
|
||||||
if task_status:
|
if task_status:
|
||||||
need_save = True
|
need_save = True
|
||||||
|
|
@ -152,9 +153,13 @@ class Tasks(metaclass=Singleton):
|
||||||
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
|
LOG.error(f"Failed to parse task at list position '{i}'. '{e!s}'.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
self._tasks.append(task)
|
||||||
|
|
||||||
|
if not task.timer:
|
||||||
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._scheduler.add(timer=task.timer, func=self._runner, args=(task,), id=task.id)
|
self._scheduler.add(timer=task.timer, func=self._runner, args=(task,), id=task.id)
|
||||||
self._tasks.append(task)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from cronsim import CronSim
|
from cronsim import CronSim
|
||||||
|
|
@ -221,14 +226,19 @@ class Tasks(metaclass=Singleton):
|
||||||
msg = "No name found."
|
msg = "No name found."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
if not task.get("timer"):
|
|
||||||
msg = "No timer found."
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
if not task.get("url"):
|
if not task.get("url"):
|
||||||
msg = "No URL found."
|
msg = "No URL found."
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
|
if task.get("timer"):
|
||||||
|
try:
|
||||||
|
from cronsim import CronSim
|
||||||
|
|
||||||
|
CronSim(task.get("timer"), datetime.now(UTC))
|
||||||
|
except Exception as e:
|
||||||
|
msg = f"Invalid timer format. '{e!s}'."
|
||||||
|
raise ValueError(msg) from e
|
||||||
|
|
||||||
if task.get("cli"):
|
if task.get("cli"):
|
||||||
try:
|
try:
|
||||||
from .Utils import arg_converter
|
from .Utils import arg_converter
|
||||||
|
|
|
||||||
|
|
@ -126,16 +126,15 @@
|
||||||
CRON expression timer.
|
CRON expression timer.
|
||||||
</label>
|
</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input type="text" class="input" id="timer" placeholder="leave empty to run once every hour."
|
<input type="text" class="input" id="timer" v-model="form.timer" :disabled="addInProgress"
|
||||||
v-model="form.timer" :disabled="addInProgress">
|
placeholder="0 12 * * 5">
|
||||||
</div>
|
</div>
|
||||||
<span class="help">
|
<span class="help">
|
||||||
<span class="icon"><i class="fa-solid fa-info" /></span>
|
<span class="icon"><i class="fa-solid fa-info" /></span>
|
||||||
<span>
|
<span>
|
||||||
The CRON timer expression to use for this task. If not set, the task will run once an hour in a
|
The CRON timer expression to use for this task. If not set, the task will be disabled. For more
|
||||||
random
|
information on CRON expressions, see <NuxtLink to="https://crontab.guru/" target="_blank">
|
||||||
minute. For more information on CRON expressions, see <NuxtLink to="https://crontab.guru/"
|
crontab.guru</NuxtLink>.
|
||||||
target="_blank">crontab.guru</NuxtLink>.
|
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ div.is-centered {
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p class="is-text-overflow">
|
<p class="is-text-overflow">
|
||||||
<span class="icon"><i class="fa-solid fa-clock" /></span>
|
<span class="icon"><i class="fa-solid fa-clock" /></span>
|
||||||
<span>{{ tryParse(item.timer) }} - {{ item.timer }}</span>
|
<span v-if="item.timer">{{ tryParse(item.timer) }} - {{ item.timer }}</span>
|
||||||
|
<span v-else>No timer is set</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="is-text-overflow" v-if="item.folder">
|
<p class="is-text-overflow" v-if="item.folder">
|
||||||
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
<span class="icon"><i class="fa-solid fa-folder" /></span>
|
||||||
|
|
@ -102,7 +103,7 @@ div.is-centered {
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer-item">
|
<div class="card-footer-item">
|
||||||
<button class="button is-purple is-fullwidth" @click="runNow(item)"
|
<button class="button is-purple is-fullwidth" @click="runNow(item)"
|
||||||
:class="{ 'is-loading': runNowInProgress }">
|
:class="{ 'is-loading': item?.in_progress }">
|
||||||
<span class="icon"><i class="fa-solid fa-up-right-from-square" /></span>
|
<span class="icon"><i class="fa-solid fa-up-right-from-square" /></span>
|
||||||
<span>Run now</span>
|
<span>Run now</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -135,7 +136,6 @@ const toggleForm = ref(false)
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const initialLoad = ref(true)
|
const initialLoad = ref(true)
|
||||||
const addInProgress = ref(false)
|
const addInProgress = ref(false)
|
||||||
const runNowInProgress = ref(false)
|
|
||||||
|
|
||||||
watch(() => config.app.basic_mode, async () => {
|
watch(() => config.app.basic_mode, async () => {
|
||||||
if (!config.app.basic_mode) {
|
if (!config.app.basic_mode) {
|
||||||
|
|
@ -241,6 +241,7 @@ const deleteItem = async item => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateItem = async ({ reference, task }) => {
|
const updateItem = async ({ reference, task }) => {
|
||||||
|
task = cleanObject(task, ['in_progress'])
|
||||||
if (reference) {
|
if (reference) {
|
||||||
// -- find the task index.
|
// -- find the task index.
|
||||||
const index = tasks.value.findIndex((t) => t?.id === reference)
|
const index = tasks.value.findIndex((t) => t?.id === reference)
|
||||||
|
|
@ -297,7 +298,7 @@ const runNow = async item => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
runNowInProgress.value = true
|
item.in_progress = true
|
||||||
|
|
||||||
let data = {
|
let data = {
|
||||||
url: item.url,
|
url: item.url,
|
||||||
|
|
@ -320,7 +321,7 @@ const runNow = async item => {
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
await nextTick()
|
await nextTick()
|
||||||
runNowInProgress.value = false
|
item.in_progress = false
|
||||||
}, 500)
|
}, 500)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,4 +361,5 @@ const exportItem = async item => {
|
||||||
return copyText(base64UrlEncode(JSON.stringify(data)));
|
return copyText(base64UrlEncode(JSON.stringify(data)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -476,6 +476,22 @@ const toggleClass = (target, className) => {
|
||||||
target.classList.add(className)
|
target.classList.add(className)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cleanObject = (item, fields = []) => {
|
||||||
|
if (!item || typeof item !== 'object' || fields.length < 1) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleaned = {}
|
||||||
|
|
||||||
|
for (const key of Object.keys(item)) {
|
||||||
|
if (false === fields.includes(key)) {
|
||||||
|
cleaned[key] = item[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleaned
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ag_set,
|
ag_set,
|
||||||
ag,
|
ag,
|
||||||
|
|
@ -502,4 +518,5 @@ export {
|
||||||
base64UrlDecode,
|
base64UrlDecode,
|
||||||
has_data,
|
has_data,
|
||||||
toggleClass,
|
toggleClass,
|
||||||
|
cleanObject,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue