mirror of
https://github.com/seriousm4x/UpSnap.git
synced 2026-07-28 08:23:11 -04:00
feat: improve cron syntax validation and force 6 digits with seconds
This commit is contained in:
@@ -11,8 +11,8 @@ import (
|
||||
var (
|
||||
PingRunning = false
|
||||
WakeShutdownRunning = false
|
||||
CronPing = cron.New()
|
||||
CronWakeShutdown = cron.New()
|
||||
CronPing = cron.New(cron.WithSeconds())
|
||||
CronWakeShutdown = cron.New(cron.WithSeconds())
|
||||
)
|
||||
|
||||
func SetPingJobs(app *pocketbase.PocketBase) {
|
||||
@@ -113,15 +113,18 @@ func SetWakeShutdownJobs(app *pocketbase.PocketBase) {
|
||||
|
||||
if wake_cron_enabled && wake_cron != "" {
|
||||
_, err := CronWakeShutdown.AddFunc(wake_cron, func() {
|
||||
isOnline := networking.PingDevice(dev)
|
||||
if isOnline {
|
||||
return
|
||||
}
|
||||
d, err := app.FindRecordById("devices", dev.Id)
|
||||
if err != nil {
|
||||
logger.Error.Println(err)
|
||||
return
|
||||
}
|
||||
if d.GetString("status") == "pending" {
|
||||
return
|
||||
}
|
||||
isOnline := networking.PingDevice(dev)
|
||||
if isOnline {
|
||||
return
|
||||
}
|
||||
d.Set("status", "pending")
|
||||
if err := app.Save(d); err != nil {
|
||||
logger.Error.Println("Failed to save record:", err)
|
||||
@@ -144,15 +147,18 @@ func SetWakeShutdownJobs(app *pocketbase.PocketBase) {
|
||||
|
||||
if shutdown_cron_enabled && shutdown_cron != "" {
|
||||
_, err := CronWakeShutdown.AddFunc(shutdown_cron, func() {
|
||||
isOnline := networking.PingDevice(dev)
|
||||
if !isOnline {
|
||||
return
|
||||
}
|
||||
d, err := app.FindRecordById("devices", dev.Id)
|
||||
if err != nil {
|
||||
logger.Error.Println(err)
|
||||
return
|
||||
}
|
||||
if d.GetString("status") == "pending" {
|
||||
return
|
||||
}
|
||||
isOnline := networking.PingDevice(dev)
|
||||
if !isOnline {
|
||||
return
|
||||
}
|
||||
status := d.GetString("status")
|
||||
if status != "online" {
|
||||
return
|
||||
@@ -187,8 +193,7 @@ func StartWakeShutdown() {
|
||||
func StopWakeShutdown() {
|
||||
if WakeShutdownRunning {
|
||||
logger.Info.Println("Stopping wake/shutdown cronjob")
|
||||
ctx := CronWakeShutdown.Stop()
|
||||
<-ctx.Done()
|
||||
CronWakeShutdown.Stop()
|
||||
}
|
||||
WakeShutdownRunning = false
|
||||
}
|
||||
@@ -201,8 +206,7 @@ func StartPing() {
|
||||
func StopPing() {
|
||||
if PingRunning {
|
||||
logger.Info.Println("Stopping wake/shutdown cronjob")
|
||||
ctx := CronPing.Stop()
|
||||
<-ctx.Done()
|
||||
CronPing.Stop()
|
||||
}
|
||||
PingRunning = false
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/plugins/migratecmd"
|
||||
"github.com/robfig/cron/v3"
|
||||
"github.com/seriousm4x/upsnap/cronjobs"
|
||||
"github.com/seriousm4x/upsnap/logger"
|
||||
_ "github.com/seriousm4x/upsnap/migrations"
|
||||
@@ -212,7 +213,8 @@ func importSettings(app *pocketbase.PocketBase) error {
|
||||
// 1st: env var
|
||||
// 2nd: database entry
|
||||
// 3rd: default values
|
||||
interval := "@every 3s"
|
||||
defaultInterval := "*/3 * * * * *"
|
||||
interval := defaultInterval
|
||||
if settingsPrivate.GetString("interval") != "" {
|
||||
interval = settingsPrivate.GetString("interval")
|
||||
}
|
||||
@@ -220,6 +222,13 @@ func importSettings(app *pocketbase.PocketBase) error {
|
||||
interval = os.Getenv("UPSNAP_INTERVAL")
|
||||
}
|
||||
|
||||
// validate interval before saving
|
||||
p := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow)
|
||||
if _, err := p.Parse(interval); err != nil {
|
||||
interval = defaultInterval
|
||||
logger.Warning.Println(errors.New("Ping interval is not valid."))
|
||||
}
|
||||
|
||||
settingsPrivate.Set("interval", interval)
|
||||
if scanRange := os.Getenv("UPSNAP_SCAN_RANGE"); scanRange != "" {
|
||||
settingsPrivate.Set("scan_range", scanRange)
|
||||
|
||||
2466
frontend/pnpm-lock.yaml
generated
2466
frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import LL, { locale } from '$lib/i18n/i18n-svelte';
|
||||
import { parseCron } from '$lib/helpers/cron';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { dateFnsLocale } from '$lib/stores/locale';
|
||||
import { backendUrl, permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device } from '$lib/types/device';
|
||||
import { type Device } from '$lib/types/device';
|
||||
import {
|
||||
faBed,
|
||||
faCircleArrowDown,
|
||||
@@ -11,20 +13,7 @@
|
||||
faPen,
|
||||
faRotateLeft
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import cronParser from 'cron-parser';
|
||||
import type { Locale } from 'date-fns';
|
||||
import { formatDistance, formatRelative, parseISO } from 'date-fns';
|
||||
import { de } from 'date-fns/locale/de';
|
||||
import { enUS } from 'date-fns/locale/en-US';
|
||||
import { es } from 'date-fns/locale/es';
|
||||
import { fr } from 'date-fns/locale/fr';
|
||||
import { it } from 'date-fns/locale/it';
|
||||
import { ja } from 'date-fns/locale/ja';
|
||||
import { nl } from 'date-fns/locale/nl';
|
||||
import { pl } from 'date-fns/locale/pl';
|
||||
import { pt } from 'date-fns/locale/pt';
|
||||
import { zhCN } from 'date-fns/locale/zh-CN';
|
||||
import { zhTW } from 'date-fns/locale/zh-TW';
|
||||
import { formatDistance, parseISO } from 'date-fns';
|
||||
import Fa from 'svelte-fa';
|
||||
import toast from 'svelte-french-toast';
|
||||
import { scale } from 'svelte/transition';
|
||||
@@ -71,62 +60,6 @@
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// dynamically import locales
|
||||
// it's ugly but the only working solution i found in ~ 3 hours
|
||||
var dateFnsLocale: Locale;
|
||||
$: if ($locale !== undefined) {
|
||||
(async () => {
|
||||
switch ($locale) {
|
||||
case 'de':
|
||||
case 'de-DE':
|
||||
dateFnsLocale = de;
|
||||
break;
|
||||
case 'en':
|
||||
case 'en-US':
|
||||
dateFnsLocale = enUS;
|
||||
break;
|
||||
case 'fr':
|
||||
case 'fr-FR':
|
||||
dateFnsLocale = fr;
|
||||
break;
|
||||
case 'it':
|
||||
case 'it-IT':
|
||||
dateFnsLocale = it;
|
||||
break;
|
||||
case 'ja':
|
||||
case 'ja-JP':
|
||||
dateFnsLocale = ja;
|
||||
break;
|
||||
case 'es':
|
||||
case 'es-ES':
|
||||
dateFnsLocale = es;
|
||||
break;
|
||||
case 'nl':
|
||||
case 'nl-NL':
|
||||
dateFnsLocale = nl;
|
||||
break;
|
||||
case 'pl':
|
||||
case 'pl-PL':
|
||||
dateFnsLocale = pl;
|
||||
break;
|
||||
case 'pt':
|
||||
case 'pt-PT':
|
||||
dateFnsLocale = pt;
|
||||
break;
|
||||
case 'zh':
|
||||
case 'zh-CN':
|
||||
dateFnsLocale = zhCN;
|
||||
break;
|
||||
case 'zh-TW':
|
||||
dateFnsLocale = zhTW;
|
||||
break;
|
||||
default:
|
||||
dateFnsLocale = enUS;
|
||||
break;
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function sleep() {
|
||||
fetch(`${backendUrl}api/upsnap/sleep/${device.id}`, {
|
||||
headers: {
|
||||
@@ -147,13 +80,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
function getNextCronRelativeTime(expression: string, now: number) {
|
||||
const cron = cronParser.parseExpression(expression);
|
||||
return formatRelative(cron.next().toISOString(), now, {
|
||||
locale: dateFnsLocale
|
||||
});
|
||||
}
|
||||
|
||||
function askRebootConfirmation() {
|
||||
if (device.shutdown_confirm) {
|
||||
modalReboot.showModal();
|
||||
@@ -183,17 +109,14 @@
|
||||
{#if device.wake_cron_enabled}
|
||||
<div class="tooltip" data-tip={$LL.device.card_tooltip_wake_cron()}>
|
||||
<span class="badge badge-success gap-1 p-3"
|
||||
><Fa icon={faCircleArrowUp} />{getNextCronRelativeTime(device.wake_cron, now)}</span
|
||||
><Fa icon={faCircleArrowUp} />{parseCron(device.wake_cron, now)}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
{#if device.shutdown_cron_enabled}
|
||||
<div class="tooltip" data-tip={$LL.device.card_tooltip_shutdown_cron()}>
|
||||
<span class="badge badge-error gap-1 p-3"
|
||||
><Fa icon={faCircleArrowDown} />{getNextCronRelativeTime(
|
||||
device.shutdown_cron,
|
||||
now
|
||||
)}</span
|
||||
><Fa icon={faCircleArrowDown} />{parseCron(device.shutdown_cron, now)}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -209,13 +132,11 @@
|
||||
class="tooltip"
|
||||
data-tip="{$LL.device.card_tooltip_last_status_change()}: {device.updated}"
|
||||
>
|
||||
{#if dateFnsLocale !== undefined}
|
||||
{formatDistance(parseISO(device.updated), now, {
|
||||
includeSeconds: true,
|
||||
addSuffix: true,
|
||||
locale: dateFnsLocale
|
||||
})}
|
||||
{/if}
|
||||
{formatDistance(parseISO(device.updated), now, {
|
||||
includeSeconds: true,
|
||||
addSuffix: true,
|
||||
locale: $dateFnsLocale
|
||||
})}
|
||||
</span>
|
||||
{#if moreButtons.filter((btn) => btn.requires).length > 0}
|
||||
<div class="ms-auto flex flex-row flex-wrap gap-1">
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation';
|
||||
import DeviceFormPort from '$lib/components/DeviceFormPort.svelte';
|
||||
import { cronRegex, parseCron } from '$lib/helpers/cron';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { permission, pocketbase } from '$lib/stores/pocketbase';
|
||||
import type { Device, Group, Port } from '$lib/types/device';
|
||||
import { faSave, faTrash, faX } from '@fortawesome/free-solid-svg-icons';
|
||||
import cronParser from 'cron-parser';
|
||||
import { onMount } from 'svelte';
|
||||
import Fa from 'svelte-fa';
|
||||
import toast from 'svelte-french-toast';
|
||||
@@ -20,6 +22,24 @@
|
||||
});
|
||||
|
||||
async function save() {
|
||||
// validate crons
|
||||
if (
|
||||
device.wake_cron_enabled &&
|
||||
(!cronRegex.test(device.wake_cron) ||
|
||||
Object.keys(cronParser.parseString(device.wake_cron).errors).length > 0)
|
||||
) {
|
||||
toast.error($LL.settings.invalid_cron());
|
||||
throw new Error('wake_cron not valid');
|
||||
}
|
||||
if (
|
||||
device.shutdown_cron_enabled &&
|
||||
(!cronRegex.test(device.shutdown_cron) ||
|
||||
Object.keys(cronParser.parseString(device.shutdown_cron).errors).length > 0)
|
||||
) {
|
||||
toast.error($LL.settings.invalid_cron());
|
||||
throw new Error('shutdown_cron not valid');
|
||||
}
|
||||
|
||||
// create/update all ports
|
||||
let portIds: string[] = [];
|
||||
await Promise.all(
|
||||
@@ -183,7 +203,7 @@
|
||||
<h2 class="card-title">{$LL.device.general()}</h2>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.general_name()} <span class="text-error">*</span></span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -195,7 +215,7 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.general_ip()} <span class="text-error">*</span></span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -207,7 +227,7 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.general_mac()} <span class="text-error">*</span></span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -219,7 +239,7 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.general_netmask()} <span class="text-error">*</span></span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -231,7 +251,7 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.general_description()}</span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -248,7 +268,7 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.ports()}</h2>
|
||||
<p class="my-2">{$LL.device.ports_desc()}</p>
|
||||
<p>{$LL.device.ports_desc()}</p>
|
||||
<div class="w-full">
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
||||
@@ -267,12 +287,12 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.link()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
{$LL.device.link_desc()}
|
||||
</p>
|
||||
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.link()}</span>
|
||||
<input
|
||||
type="url"
|
||||
@@ -283,7 +303,7 @@
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.link_open()}</span>
|
||||
<select class="select" bind:value={device.link_open}>
|
||||
<option value="">{$LL.device.link_open_no()}</option>
|
||||
@@ -298,12 +318,12 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.ping()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.ping_desc()}
|
||||
</p>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.ping_cmd()}</span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -318,13 +338,13 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.wake()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
{$LL.device.wake_desc()}
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.settings.ping_interval_desc2()}
|
||||
</p>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.wake_cmd()}</span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -345,7 +365,7 @@
|
||||
/>
|
||||
{$LL.device.wake_cron_enable()}
|
||||
</label>
|
||||
<label class="floating-label mt-4">
|
||||
<label class="floating-label mt-2">
|
||||
<span
|
||||
>{$LL.device.wake_cron()}
|
||||
{#if device.wake_cron_enabled}
|
||||
@@ -360,6 +380,9 @@
|
||||
disabled={!device.wake_cron_enabled}
|
||||
required={device.wake_cron_enabled}
|
||||
/>
|
||||
{#if device.wake_cron_enabled}
|
||||
<p class="fieldset-label">{parseCron(device.wake_cron, Date.now())}</p>
|
||||
{/if}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset bg-base-100 border-base-300 rounded-box w-64 border p-4">
|
||||
@@ -380,7 +403,7 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">Sleep-On-LAN</h2>
|
||||
<p class="mt-2">
|
||||
<p>
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.sol_desc1()}
|
||||
</p>
|
||||
@@ -402,7 +425,7 @@
|
||||
/>
|
||||
{$LL.device.sol_enable()}
|
||||
</label>
|
||||
<label class="floating-label mt-4">
|
||||
<label class="floating-label mt-2">
|
||||
<span
|
||||
>{$LL.device.sol_port()}
|
||||
{#if device.sol_enabled}
|
||||
@@ -428,7 +451,7 @@
|
||||
<input type="checkbox" bind:checked={device.sol_auth} class="toggle toggle-success" />
|
||||
{$LL.device.sol_authorization()}
|
||||
</label>
|
||||
<label class="floating-label mt-4">
|
||||
<label class="floating-label mt-2">
|
||||
<span
|
||||
>{$LL.device.sol_user()}
|
||||
{#if device.sol_auth}
|
||||
@@ -444,7 +467,7 @@
|
||||
required={device.sol_auth}
|
||||
/>
|
||||
</label>
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span
|
||||
>{$LL.device.sol_password()}
|
||||
{#if device.sol_auth}
|
||||
@@ -469,12 +492,12 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.shutdown()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.shutdown_desc()}
|
||||
</p>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.shutdown_cmd()}</span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -484,7 +507,7 @@
|
||||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<p class="my-2 font-bold">{$LL.device.shutdown_examples()}</p>
|
||||
<p class="font-bold">{$LL.device.shutdown_examples()}</p>
|
||||
<div class="mockup-code max-w-fit min-w-0 text-sm">
|
||||
<pre data-prefix="#" class="italic"><code>{$LL.device.shutdown_examples_windows()}</code
|
||||
></pre>
|
||||
@@ -499,7 +522,7 @@
|
||||
>sshpass -p password ssh -o "StrictHostKeyChecking=no" user@192.168.1.13 "sudo poweroff"</code
|
||||
></pre>
|
||||
</div>
|
||||
<p class="mt-4">
|
||||
<p>
|
||||
{$LL.device.shutdown_cron_desc()}
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-4">
|
||||
@@ -513,7 +536,7 @@
|
||||
/>
|
||||
{$LL.device.shutdown_cron_enable()}
|
||||
</label>
|
||||
<label class="floating-label mt-4">
|
||||
<label class="floating-label mt-2">
|
||||
<span
|
||||
>{$LL.device.shutdown_cron()}
|
||||
{#if device.shutdown_cron_enabled}
|
||||
@@ -523,11 +546,14 @@
|
||||
<input
|
||||
type="text"
|
||||
placeholder={$LL.device.shutdown_cron()}
|
||||
class="input"
|
||||
class="input validator"
|
||||
bind:value={device.shutdown_cron}
|
||||
disabled={!device.shutdown_cron_enabled}
|
||||
required={device.shutdown_cron_enabled}
|
||||
/>
|
||||
{#if device.shutdown_cron_enabled}
|
||||
<p class="fieldset-label">{parseCron(device.shutdown_cron, Date.now())}</p>
|
||||
{/if}
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset class="fieldset bg-base-100 border-base-300 rounded-box w-64 border p-4">
|
||||
@@ -548,12 +574,12 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.password()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
<!-- eslint-disable svelte/no-at-html-tags -->
|
||||
{@html $LL.device.password_desc()}
|
||||
</p>
|
||||
<fieldset class="fieldset">
|
||||
<label class="floating-label">
|
||||
<label class="floating-label mt-2">
|
||||
<span>{$LL.device.password()}</span>
|
||||
<input
|
||||
type="text"
|
||||
@@ -569,7 +595,7 @@
|
||||
<div class="card bg-base-200 mt-6 w-full shadow-sm">
|
||||
<div class="card-body">
|
||||
<h2 class="card-title">{$LL.device.groups()}</h2>
|
||||
<p class="my-2">
|
||||
<p>
|
||||
{$LL.device.groups_desc()}
|
||||
</p>
|
||||
<div class="flex flex-row flex-wrap gap-2">
|
||||
|
||||
28
frontend/src/lib/helpers/cron.ts
Normal file
28
frontend/src/lib/helpers/cron.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { dateFnsLocale } from '$lib/stores/locale';
|
||||
import cronParser from 'cron-parser';
|
||||
import { formatDistanceStrict } from 'date-fns';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
export const cronRegex = new RegExp(/((((\d+,)+\d+|(\d+(\/|-)\d+)|\d+|\*) ?){6})/);
|
||||
|
||||
export function parseCron(expression: string, now: number) {
|
||||
try {
|
||||
if (
|
||||
expression === '' ||
|
||||
!cronRegex.test(expression) ||
|
||||
Object.keys(cronParser.parseString(expression).errors).length > 0
|
||||
) {
|
||||
throw new Error('cron-parser failed to parse string');
|
||||
}
|
||||
|
||||
const cron = cronParser.parseExpression(expression, {});
|
||||
|
||||
return formatDistanceStrict(cron.next().toISOString(), new Date(now), {
|
||||
locale: get(dateFnsLocale),
|
||||
addSuffix: true
|
||||
});
|
||||
} catch {
|
||||
return get(LL).settings.invalid_cron();
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ const de = {
|
||||
ping_interval_desc1:
|
||||
'Legt den Intervall fest, in dem Geräte gepingt werden. Leer lassen um den Standardwert von <span class="badge">@every 3s</span> zu verwenden.',
|
||||
ping_interval_desc2:
|
||||
'Lerne mehr über die korrekte Cron Syntax auf <a class="link" href="https://de.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oder sieh dir die <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">Paketdokumentation</a> an.',
|
||||
'Lerne mehr über die korrekte Cron Syntax auf <a class="link" href="https://de.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oder sieh dir die <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">Paketdokumentation</a> an.',
|
||||
lazy_ping_title: 'Lazy Ping',
|
||||
lazy_ping_desc:
|
||||
'Wenn Lazy Ping aktiviert ist, pingt UpSnap die Geräte nur an, wenn ein aktiver Benutzer die Website besucht. Wenn es ausgeschaltet ist, pingt UpSnap die Geräte immer an.',
|
||||
@@ -150,7 +150,8 @@ const de = {
|
||||
website_title_desc: 'Setzt den Seiten- und den Tabtitel.',
|
||||
icon_title: 'Icon',
|
||||
icon_desc: 'Eigenes Favicon nutzen. Unterstützte Dateitypen sind:',
|
||||
upsnap_version: 'UpSnap Version'
|
||||
upsnap_version: 'UpSnap Version',
|
||||
invalid_cron: 'Ungültige cron Syntax'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Benutzer',
|
||||
|
||||
@@ -138,7 +138,7 @@ const en = {
|
||||
ping_interval_desc1:
|
||||
'Sets the interval in which the devices are pinged. Leave blank to use default value of <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">package documentation</a>.',
|
||||
'Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">package documentation</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc:
|
||||
"When lazy ping is turned on, UpSnap will only ping devices if there is an active user visiting the website. If it's turned off, UpSnap will always ping devices.",
|
||||
@@ -147,7 +147,8 @@ const en = {
|
||||
website_title_desc: 'Sets the title of the website and in the browser tab.',
|
||||
icon_title: 'Icon',
|
||||
icon_desc: 'Set a custom favicon. Supported file types are:',
|
||||
upsnap_version: 'UpSnap version'
|
||||
upsnap_version: 'UpSnap version',
|
||||
invalid_cron: 'Invalid cron syntax'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Users',
|
||||
|
||||
@@ -140,7 +140,7 @@ const es = {
|
||||
ping_interval_desc1:
|
||||
'Establece el intervalo en el que se realiza el ping. Déjelo en blanco para usar el valor predeterminado de <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Puedes ver la sintaxis correcta para cron en <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> o en <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">documentación cron</a>.',
|
||||
'Puedes ver la sintaxis correcta para cron en <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> o en <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">documentación cron</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc:
|
||||
'Cuando el ping diferido está activado, UpSnap solo hará ping a los dispositivos si hay un usuario activo visitando el sitio web. Si está desactivado, UpSnap siempre hará ping a los dispositivos.',
|
||||
@@ -149,7 +149,8 @@ const es = {
|
||||
website_title_desc: 'Establece el título del sitio web y en la pestaña del navegador.',
|
||||
icon_title: 'Icon',
|
||||
icon_desc: 'Establece un favicon personalizado. Los tipos de archivos admitidos son:',
|
||||
upsnap_version: 'Version UpSnap'
|
||||
upsnap_version: 'Version UpSnap',
|
||||
invalid_cron: 'Sintaxis cron no válida'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Usuarios',
|
||||
|
||||
@@ -142,7 +142,7 @@ const fr = {
|
||||
ping_interval_desc1:
|
||||
'Définit l\'intervalle dans lequel les appareils reçoivent une requête ping. Laissez vide pour utiliser la valeur par défaut de <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Pour écrire une syntaxe correcte pour l\'utilitaire cron, consultez <a class="link" href="https://fr.wikipedia.org/wiki/Cron" target="_blank">Wikipédia</a> ou référez-vous à la <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">documentation du paquet</a>.',
|
||||
'Pour écrire une syntaxe correcte pour l\'utilitaire cron, consultez <a class="link" href="https://fr.wikipedia.org/wiki/Cron" target="_blank">Wikipédia</a> ou référez-vous à la <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">documentation du paquet</a>.',
|
||||
lazy_ping_title: 'Ping fainéant',
|
||||
lazy_ping_desc:
|
||||
"Lorsque le mode ping fainéant est activé, UpSnap ne va effectuer des requêtes de ping sur les appareils que lorsqu'un utilisateur actif visite le site. Si vous désactivez cette fonction, UpSnap exécutera toujours ces requêtes.",
|
||||
@@ -152,7 +152,8 @@ const fr = {
|
||||
"Définit le titre du site ainsi que le texte affiché dans l'onglet du navigateur.",
|
||||
icon_title: 'Icône',
|
||||
icon_desc: 'Définit un favicon personnalisé. Les types de fichier compatibles sont :',
|
||||
upsnap_version: 'Version de UpSnap '
|
||||
upsnap_version: 'Version de UpSnap ',
|
||||
invalid_cron: 'Syntaxe cron invalide'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Utilisateurs',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
|
||||
|
||||
import { initI18nSvelte } from 'typesafe-i18n/svelte';
|
||||
import type { Formatters, Locales, TranslationFunctions, Translations } from './i18n-types';
|
||||
|
||||
@@ -521,7 +521,7 @@ type RootTranslation = {
|
||||
*/
|
||||
ping_interval_desc1: string;
|
||||
/**
|
||||
* Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">package documentation</a>.
|
||||
* Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">package documentation</a>.
|
||||
*/
|
||||
ping_interval_desc2: string;
|
||||
/**
|
||||
@@ -556,6 +556,10 @@ type RootTranslation = {
|
||||
* UpSnap version
|
||||
*/
|
||||
upsnap_version: string;
|
||||
/**
|
||||
* Invalid cron syntax
|
||||
*/
|
||||
invalid_cron: string;
|
||||
};
|
||||
users: {
|
||||
/**
|
||||
@@ -1316,7 +1320,7 @@ export type TranslationFunctions = {
|
||||
*/
|
||||
ping_interval_desc1: () => LocalizedString;
|
||||
/**
|
||||
* Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">package documentation</a>.
|
||||
* Learn more about the correct syntax for cron on <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> or refer to the <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">package documentation</a>.
|
||||
*/
|
||||
ping_interval_desc2: () => LocalizedString;
|
||||
/**
|
||||
@@ -1351,6 +1355,10 @@ export type TranslationFunctions = {
|
||||
* UpSnap version
|
||||
*/
|
||||
upsnap_version: () => LocalizedString;
|
||||
/**
|
||||
* Invalid cron syntax
|
||||
*/
|
||||
invalid_cron: () => LocalizedString;
|
||||
};
|
||||
users: {
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
|
||||
|
||||
import { initFormatters } from './formatters';
|
||||
import type { Locales, Translations } from './i18n-types';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
|
||||
|
||||
import { initFormatters } from './formatters';
|
||||
import type { Locales, Translations } from './i18n-types';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// This file was auto-generated by 'typesafe-i18n'. Any manual changes will be overwritten.
|
||||
|
||||
|
||||
import {
|
||||
i18n as initI18n,
|
||||
|
||||
@@ -141,7 +141,7 @@ const it = {
|
||||
ping_interval_desc1:
|
||||
'Imposta l\'intervallo nel quale i dispositivi vengono testati con ping. Lasciare in bianco per lasciare il valore predefinito <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Per ulteriori dettagli circa la sintassi corretta di cron, fare riferimento a <a class="link" href="https://it.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oppure a <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">questa documentazione</a> (in inglese).',
|
||||
'Per ulteriori dettagli circa la sintassi corretta di cron, fare riferimento a <a class="link" href="https://it.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> oppure a <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">questa documentazione</a> (in inglese).',
|
||||
lazy_ping_title: 'Ping pigro',
|
||||
lazy_ping_desc:
|
||||
"Quando il ping pigro e' abilitato, UpSnap effettuera' i ping verso di dispositivi solamente quando un utente effettivamente visita il sito. Se e' spento, allora UpSnap continuera' con il ping sempre.",
|
||||
@@ -150,7 +150,8 @@ const it = {
|
||||
website_title_desc: 'Imposta il nome del sito.',
|
||||
icon_title: 'Icona',
|
||||
icon_desc: 'Imposta una icona personalizzata. I tipi di file supportati sono:',
|
||||
upsnap_version: 'Versione UpSnap'
|
||||
upsnap_version: 'Versione UpSnap',
|
||||
invalid_cron: 'Sintassi cron non valida'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Utenti',
|
||||
|
||||
@@ -139,7 +139,7 @@ const ja = {
|
||||
ping_interval_desc1:
|
||||
'デバイスが ping される間隔を設定します。空白の場合、デフォルト値 <span class="badge">@every 3s</span> が使用されます。',
|
||||
ping_interval_desc2:
|
||||
'cron の正しい構文については、<a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> または <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">パッケージドキュメント</a> を参照してください。',
|
||||
'cron の正しい構文については、<a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> または <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">パッケージドキュメント</a> を参照してください。',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc:
|
||||
'Lazy ping をオンにすると、UpSnap はアクティブなユーザーがウェブサイトを訪れている場合にのみデバイスを ping します。オフの場合、UpSnap は常にデバイスを ping します。',
|
||||
@@ -148,7 +148,8 @@ const ja = {
|
||||
website_title_desc: 'ウェブサイトおよびブラウザタブのタイトルを設定します。',
|
||||
icon_title: 'アイコン',
|
||||
icon_desc: 'カスタムファビコンを設定します。サポートされているファイルタイプ:',
|
||||
upsnap_version: 'UpSnap バージョン'
|
||||
upsnap_version: 'UpSnap バージョン',
|
||||
invalid_cron: '無効なcron構文'
|
||||
},
|
||||
users: {
|
||||
page_title: 'ユーザー',
|
||||
|
||||
@@ -141,7 +141,7 @@ const nl = {
|
||||
ping_interval_desc1:
|
||||
'Stelt de interval in waarin de apparaten worden gepingt. Laat leeg om de standaardwaarde van <span class="badge">@every 3s</span> te gebruiken.',
|
||||
ping_interval_desc2:
|
||||
'Leer meer over de juiste syntax voor cron op <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> of raadpleeg de <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">pakketdocumentatie</a>.',
|
||||
'Leer meer over de juiste syntax voor cron op <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> of raadpleeg de <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">pakketdocumentatie</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc:
|
||||
'Wanneer lazy ping is ingeschakeld, pingt UpSnap alleen apparaten als er een actieve gebruiker de website bezoekt. Als het is uitgeschakeld, pingt UpSnap altijd apparaten.',
|
||||
@@ -150,7 +150,8 @@ const nl = {
|
||||
website_title_desc: 'Stelt de titel van de website en in het browsertabblad in.',
|
||||
icon_title: 'Icoon',
|
||||
icon_desc: 'Stel een aangepast favicon in. Ondersteunde bestandstypen zijn:',
|
||||
upsnap_version: 'UpSnap versie'
|
||||
upsnap_version: 'UpSnap versie',
|
||||
invalid_cron: 'Ongeldige cron syntaxis'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Gebruikers',
|
||||
|
||||
@@ -139,7 +139,7 @@ const pl = {
|
||||
ping_interval_desc1:
|
||||
'Ustawia interwał, w którym urządzenia są pingowane. Pozostaw puste aby użyć domyślnej wartości <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Dowiedz się więcej na temat składni cron na <a class="link" href="https://pl.wikipedia.org/wiki/Cron" target="_blank">Wikipedii</a> lub w <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">dokumentacji pakietu cron</a>.',
|
||||
'Dowiedz się więcej na temat składni cron na <a class="link" href="https://pl.wikipedia.org/wiki/Cron" target="_blank">Wikipedii</a> lub w <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">dokumentacji pakietu cron</a>.',
|
||||
lazy_ping_title: 'Leniwe pingowanie',
|
||||
lazy_ping_desc:
|
||||
'Gdy leniwe pingowanie jest włączone, UpSnap będzie pingować urządzenia tylko wtedy, gdy użytkownicy odwiedzają stronę. Gdy ta opcja jest wyłączona, UpSnap będzie pingować urządzenia nieustannie.',
|
||||
@@ -148,7 +148,8 @@ const pl = {
|
||||
website_title_desc: 'Ustawia tytuł strony internetowej i na karcie przeglądarki.',
|
||||
icon_title: 'Ikona',
|
||||
icon_desc: 'Ustaw niestandardowy favicon. Obsługiwane są typy plików:',
|
||||
upsnap_version: 'Wersja UpSnap'
|
||||
upsnap_version: 'Wersja UpSnap',
|
||||
invalid_cron: 'Nieprawidłowa składnia cron'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Użytkownicy',
|
||||
|
||||
@@ -140,7 +140,7 @@ const pt = {
|
||||
ping_interval_desc1:
|
||||
'Define o intervalo em que o ping é executado. Deixe em branco para usar o valor por defeito de <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'Poderá ver a sintaxe correta para cron em <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> ou em <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">documentação cron</a>.',
|
||||
'Poderá ver a sintaxe correta para cron em <a class="link" href="https://en.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> ou em <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">documentação cron</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc:
|
||||
'Quando o lazy ping está activo, o UpSnap irá apenas executar o ping se existir um utilizador ativo no website. Se desligado, o UpSnap irá executar sempre o ping.',
|
||||
@@ -149,7 +149,8 @@ const pt = {
|
||||
website_title_desc: 'Define o título do website.',
|
||||
icon_title: 'Ícone',
|
||||
icon_desc: 'Define um favicon personalizado. Tipos de ficheiros suportados são:',
|
||||
upsnap_version: 'Versão UpSnap'
|
||||
upsnap_version: 'Versão UpSnap',
|
||||
invalid_cron: 'Sintaxe inválida do cron'
|
||||
},
|
||||
users: {
|
||||
page_title: 'Utilizadores',
|
||||
|
||||
@@ -135,7 +135,7 @@ const zh_TW = {
|
||||
ping_interval_title: 'Ping 間隔',
|
||||
ping_interval_desc1: '設定 ping 的間隔。留空使用預設值 <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'有關 cron 的更多資訊,請見<a class="link" href="https://zh.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> 或參閱 <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">cron 文檔</a>.',
|
||||
'有關 cron 的更多資訊,請見<a class="link" href="https://zh.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> 或參閱 <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">cron 文檔</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc: '當開關打開時, UpSnap 只會在有訪問者訪問網站時才對設備進行 ping 測試',
|
||||
lazy_ping_enable: '啟用',
|
||||
@@ -143,7 +143,8 @@ const zh_TW = {
|
||||
website_title_desc: '設置網站的標題,同時顯示在瀏覽器的標籤中。',
|
||||
icon_title: '圖標',
|
||||
icon_desc: '設定一個自訂義圖標。支援的檔案類型:',
|
||||
upsnap_version: 'UpSnap 版本'
|
||||
upsnap_version: 'UpSnap 版本',
|
||||
invalid_cron: '「無效的 Cron 語法」'
|
||||
},
|
||||
users: {
|
||||
page_title: '使用者',
|
||||
|
||||
@@ -133,7 +133,7 @@ const zh = {
|
||||
ping_interval_title: 'Ping 间隔',
|
||||
ping_interval_desc1: '设置 ping 的间隔。为空时使用默认值 <span class="badge">@every 3s</span>.',
|
||||
ping_interval_desc2:
|
||||
'有关 cron 的更多信息, 请访问<a class="link" href="https://zh.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> 或参阅 <a class="link" href="https://pkg.go.dev/github.com/robfig/cron/v3" target="_blank">cron 文档</a>.',
|
||||
'有关 cron 的更多信息, 请访问<a class="link" href="https://zh.wikipedia.org/wiki/Cron" target="_blank">Wikipedia</a> 或参阅 <a class="link" href="https://github.com/harrisiirak/cron-parser" target="_blank">cron 文档</a>.',
|
||||
lazy_ping_title: 'Lazy ping',
|
||||
lazy_ping_desc: '当开关打开时, UpSnap 只会在用户访问网站期间进行 ping 操作.',
|
||||
lazy_ping_enable: '启用',
|
||||
@@ -141,7 +141,8 @@ const zh = {
|
||||
website_title_desc: '设置在浏览器选项卡和网站中显示标题',
|
||||
icon_title: '图标',
|
||||
icon_desc: '设置一个自定义图标. 支持的文件类型:',
|
||||
upsnap_version: 'UpSnap 版本'
|
||||
upsnap_version: 'UpSnap 版本',
|
||||
invalid_cron: '无效的 cron 语法'
|
||||
},
|
||||
users: {
|
||||
page_title: '用户',
|
||||
|
||||
69
frontend/src/lib/stores/locale.ts
Normal file
69
frontend/src/lib/stores/locale.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { locale } from '$lib/i18n/i18n-svelte';
|
||||
import type { Locale } from 'date-fns';
|
||||
import { de } from 'date-fns/locale/de';
|
||||
import { enUS } from 'date-fns/locale/en-US';
|
||||
import { es } from 'date-fns/locale/es';
|
||||
import { fr } from 'date-fns/locale/fr';
|
||||
import { it } from 'date-fns/locale/it';
|
||||
import { ja } from 'date-fns/locale/ja';
|
||||
import { nl } from 'date-fns/locale/nl';
|
||||
import { pl } from 'date-fns/locale/pl';
|
||||
import { pt } from 'date-fns/locale/pt';
|
||||
import { zhCN } from 'date-fns/locale/zh-CN';
|
||||
import { zhTW } from 'date-fns/locale/zh-TW';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
|
||||
// dynamically import locales
|
||||
// it's ugly but the only working solution i found in ~ 3 hours
|
||||
export const dateFnsLocale: Writable<Locale> = writable(enUS);
|
||||
|
||||
locale.subscribe((l) => {
|
||||
switch (l) {
|
||||
case 'de':
|
||||
case 'de-DE':
|
||||
dateFnsLocale.set(de);
|
||||
break;
|
||||
case 'en':
|
||||
case 'en-US':
|
||||
dateFnsLocale.set(enUS);
|
||||
break;
|
||||
case 'fr':
|
||||
case 'fr-FR':
|
||||
dateFnsLocale.set(fr);
|
||||
break;
|
||||
case 'it':
|
||||
case 'it-IT':
|
||||
dateFnsLocale.set(it);
|
||||
break;
|
||||
case 'ja':
|
||||
case 'ja-JP':
|
||||
dateFnsLocale.set(ja);
|
||||
break;
|
||||
case 'es':
|
||||
case 'es-ES':
|
||||
dateFnsLocale.set(es);
|
||||
break;
|
||||
case 'nl':
|
||||
case 'nl-NL':
|
||||
dateFnsLocale.set(nl);
|
||||
break;
|
||||
case 'pl':
|
||||
case 'pl-PL':
|
||||
dateFnsLocale.set(pl);
|
||||
break;
|
||||
case 'pt':
|
||||
case 'pt-PT':
|
||||
dateFnsLocale.set(pt);
|
||||
break;
|
||||
case 'zh':
|
||||
case 'zh-CN':
|
||||
dateFnsLocale.set(zhCN);
|
||||
break;
|
||||
case 'zh-TW':
|
||||
dateFnsLocale.set(zhTW);
|
||||
break;
|
||||
default:
|
||||
dateFnsLocale.set(enUS);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -8,7 +8,7 @@ export type SettingsPublic = RecordModel & {
|
||||
};
|
||||
|
||||
export type SettingsPrivate = RecordModel & {
|
||||
interval: number;
|
||||
interval: string;
|
||||
lazy_ping: boolean;
|
||||
scan_range: string;
|
||||
};
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
import { page } from '$app/state';
|
||||
import { PUBLIC_VERSION } from '$env/static/public';
|
||||
import PageLoading from '$lib/components/PageLoading.svelte';
|
||||
import { cronRegex, parseCron } from '$lib/helpers/cron';
|
||||
import LL from '$lib/i18n/i18n-svelte';
|
||||
import { backendUrl, pocketbase } from '$lib/stores/pocketbase';
|
||||
import { settingsPriv, settingsPub } from '$lib/stores/settings';
|
||||
import type { SettingsPrivate, SettingsPublic } from '$lib/types/settings';
|
||||
import { faSave } from '@fortawesome/free-solid-svg-icons';
|
||||
import cronParser from 'cron-parser';
|
||||
import { onMount } from 'svelte';
|
||||
import Fa from 'svelte-fa';
|
||||
import toast from 'svelte-french-toast';
|
||||
@@ -17,6 +19,15 @@
|
||||
let faviconPreview: HTMLImageElement;
|
||||
let faviconInputElement: HTMLInputElement;
|
||||
|
||||
let now = Date.now();
|
||||
let interval: number;
|
||||
$: {
|
||||
clearInterval(interval);
|
||||
interval = setInterval(() => {
|
||||
now = Date.now();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
if (!$pocketbase.authStore.isSuperuser) {
|
||||
toast($LL.toasts.no_permission({ url: page.url.pathname }), {
|
||||
@@ -58,6 +69,15 @@
|
||||
)
|
||||
return;
|
||||
|
||||
if (
|
||||
settingsPrivClone.interval === '' ||
|
||||
!cronRegex.test(settingsPrivClone.interval) ||
|
||||
Object.keys(cronParser.parseString(settingsPrivClone.interval).errors).length > 0
|
||||
) {
|
||||
toast.error($LL.settings.invalid_cron());
|
||||
throw new Error('ping_interval not valid');
|
||||
}
|
||||
|
||||
if (faviconInputElement.files !== null && faviconInputElement.files?.length > 0) {
|
||||
let form = new FormData();
|
||||
form.append('favicon', faviconInputElement.files[0]);
|
||||
@@ -114,6 +134,8 @@
|
||||
bind:value={settingsPrivClone.interval}
|
||||
/>
|
||||
</div>
|
||||
<p class="fieldset-label">{parseCron(settingsPrivClone.interval, now)}</p>
|
||||
|
||||
<h2 class="card-title mt-2">{$LL.settings.lazy_ping_title()}</h2>
|
||||
<p class="mt-2">
|
||||
{$LL.settings.lazy_ping_desc()}
|
||||
|
||||
Reference in New Issue
Block a user