diff --git a/config.go b/config.go index 46f83e6..537f85f 100644 --- a/config.go +++ b/config.go @@ -123,6 +123,7 @@ var defaultConfig = &Config{ InactivityLimitSeconds: 60, JitterPercentage: 25, ScheduleCronTab: "0 * * * * *", + Timezone: "UTC", }, TLSMode: "", UsbConfig: &usbgadget.Config{ diff --git a/jiggler.go b/jiggler.go index e5aa14e..a11ff5a 100644 --- a/jiggler.go +++ b/jiggler.go @@ -12,6 +12,7 @@ type JigglerConfig struct { InactivityLimitSeconds int `json:"inactivity_limit_seconds"` JitterPercentage int `json:"jitter_percentage"` ScheduleCronTab string `json:"schedule_cron_tab"` + Timezone string `json:"timezone,omitempty"` } var jigglerEnabled = false @@ -68,6 +69,12 @@ func initJiggler() { func runJigglerCronTab() error { cronTab := config.JigglerConfig.ScheduleCronTab + + // Apply timezone if specified + if config.JigglerConfig.Timezone != "" && config.JigglerConfig.Timezone != "UTC" { + cronTab = fmt.Sprintf("TZ=%s %s", config.JigglerConfig.Timezone, cronTab) + } + s, err := gocron.NewScheduler() if err != nil { return err diff --git a/ui/src/components/JigglerSetting.tsx b/ui/src/components/JigglerSetting.tsx index d881089..6fff919 100644 --- a/ui/src/components/JigglerSetting.tsx +++ b/ui/src/components/JigglerSetting.tsx @@ -1,44 +1,216 @@ import { useState } from "react"; +import { LuExternalLink } from "react-icons/lu"; -import { Button } from "@components/Button"; +import { Button, LinkButton } from "@components/Button"; import { InputFieldWithLabel } from "./InputField"; -import ExtLink from "./ExtLink"; +import { SelectMenuBasic } from "./SelectMenuBasic"; export interface JigglerConfig { inactivity_limit_seconds: number; jitter_percentage: number; schedule_cron_tab: string; + timezone?: string; } export function JigglerSetting({ onSave, + defaultJigglerState, }: { onSave: (jigglerConfig: JigglerConfig) => void; + defaultJigglerState?: JigglerConfig; }) { - const [jigglerConfigState, setJigglerConfigState] = useState({ - inactivity_limit_seconds: 20, - jitter_percentage: 0, - schedule_cron_tab: "*/20 * * * * *", - }); + const [jigglerConfigState, setJigglerConfigState] = useState( + defaultJigglerState || { + inactivity_limit_seconds: 20, + jitter_percentage: 0, + schedule_cron_tab: "*/20 * * * * *", + timezone: "UTC", + } + ); + + const timezoneOptions = [ + { value: "UTC", label: "UTC" }, + + // Americas - North America + { value: "America/New_York", label: "Eastern Time (US/Canada)" }, + { value: "America/Chicago", label: "Central Time (US/Canada)" }, + { value: "America/Denver", label: "Mountain Time (US/Canada)" }, + { value: "America/Los_Angeles", label: "Pacific Time (US/Canada)" }, + { value: "America/Anchorage", label: "Alaska Time" }, + { value: "Pacific/Honolulu", label: "Hawaii Time" }, + { value: "America/Phoenix", label: "Arizona (No DST)" }, + { value: "America/Toronto", label: "Toronto" }, + { value: "America/Vancouver", label: "Vancouver" }, + { value: "America/Montreal", label: "Montreal" }, + { value: "America/Halifax", label: "Halifax (Atlantic)" }, + { value: "America/St_Johns", label: "Newfoundland" }, + + // Americas - Central & South America + { value: "America/Mexico_City", label: "Mexico City" }, + { value: "America/Cancun", label: "Cancun" }, + { value: "America/Guatemala", label: "Guatemala" }, + { value: "America/Costa_Rica", label: "Costa Rica" }, + { value: "America/Panama", label: "Panama" }, + { value: "America/Bogota", label: "Bogotá" }, + { value: "America/Lima", label: "Lima" }, + { value: "America/Santiago", label: "Santiago" }, + { value: "America/Buenos_Aires", label: "Buenos Aires" }, + { value: "America/Montevideo", label: "Montevideo" }, + { value: "America/Sao_Paulo", label: "São Paulo" }, + { value: "America/Rio_Branco", label: "Rio de Janeiro" }, + { value: "America/Caracas", label: "Caracas" }, + { value: "America/La_Paz", label: "La Paz" }, + + // Europe - Western Europe + { value: "Europe/London", label: "London (GMT/BST)" }, + { value: "Europe/Dublin", label: "Dublin (GMT/IST)" }, + { value: "Europe/Lisbon", label: "Lisbon (WET/WEST)" }, + { value: "Atlantic/Reykjavik", label: "Reykjavik (GMT)" }, + + // Europe - Central Europe + { value: "Europe/Paris", label: "Paris (CET/CEST)" }, + { value: "Europe/Berlin", label: "Berlin (CET/CEST)" }, + { value: "Europe/Rome", label: "Rome (CET/CEST)" }, + { value: "Europe/Madrid", label: "Madrid (CET/CEST)" }, + { value: "Europe/Amsterdam", label: "Amsterdam (CET/CEST)" }, + { value: "Europe/Brussels", label: "Brussels (CET/CEST)" }, + { value: "Europe/Vienna", label: "Vienna (CET/CEST)" }, + { value: "Europe/Zurich", label: "Zurich (CET/CEST)" }, + { value: "Europe/Prague", label: "Prague (CET/CEST)" }, + { value: "Europe/Warsaw", label: "Warsaw (CET/CEST)" }, + { value: "Europe/Budapest", label: "Budapest (CET/CEST)" }, + { value: "Europe/Stockholm", label: "Stockholm (CET/CEST)" }, + { value: "Europe/Copenhagen", label: "Copenhagen (CET/CEST)" }, + { value: "Europe/Oslo", label: "Oslo (CET/CEST)" }, + { value: "Europe/Helsinki", label: "Helsinki (EET/EEST)" }, + + // Europe - Eastern Europe + { value: "Europe/Athens", label: "Athens (EET/EEST)" }, + { value: "Europe/Istanbul", label: "Istanbul (TRT)" }, + { value: "Europe/Bucharest", label: "Bucharest (EET/EEST)" }, + { value: "Europe/Sofia", label: "Sofia (EET/EEST)" }, + { value: "Europe/Kiev", label: "Kiev (EET/EEST)" }, + { value: "Europe/Moscow", label: "Moscow (MSK)" }, + { value: "Europe/Minsk", label: "Minsk (MSK)" }, + + // Asia - East Asia + { value: "Asia/Tokyo", label: "Tokyo (JST)" }, + { value: "Asia/Seoul", label: "Seoul (KST)" }, + { value: "Asia/Shanghai", label: "Shanghai (CST)" }, + { value: "Asia/Hong_Kong", label: "Hong Kong (HKT)" }, + { value: "Asia/Taipei", label: "Taipei (CST)" }, + { value: "Asia/Manila", label: "Manila (PST)" }, + + // Asia - Southeast Asia + { value: "Asia/Singapore", label: "Singapore (SGT)" }, + { value: "Asia/Bangkok", label: "Bangkok (ICT)" }, + { value: "Asia/Ho_Chi_Minh", label: "Ho Chi Minh City (ICT)" }, + { value: "Asia/Jakarta", label: "Jakarta (WIB)" }, + { value: "Asia/Kuala_Lumpur", label: "Kuala Lumpur (MYT)" }, + + // Asia - South Asia + { value: "Asia/Kolkata", label: "India (IST)" }, + { value: "Asia/Dhaka", label: "Dhaka (BST)" }, + { value: "Asia/Karachi", label: "Karachi (PKT)" }, + { value: "Asia/Colombo", label: "Colombo (IST)" }, + { value: "Asia/Kathmandu", label: "Kathmandu (NPT)" }, + + // Asia - Central Asia + { value: "Asia/Tashkent", label: "Tashkent (UZT)" }, + { value: "Asia/Almaty", label: "Almaty (ALMT)" }, + { value: "Asia/Yekaterinburg", label: "Yekaterinburg (YEKT)" }, + { value: "Asia/Novosibirsk", label: "Novosibirsk (NOVT)" }, + + // Asia - Middle East + { value: "Asia/Dubai", label: "Dubai (GST)" }, + { value: "Asia/Qatar", label: "Doha (AST)" }, + { value: "Asia/Kuwait", label: "Kuwait (AST)" }, + { value: "Asia/Riyadh", label: "Riyadh (AST)" }, + { value: "Asia/Baghdad", label: "Baghdad (AST)" }, + { value: "Asia/Tehran", label: "Tehran (IRST)" }, + { value: "Asia/Jerusalem", label: "Jerusalem (IST)" }, + { value: "Asia/Beirut", label: "Beirut (EET)" }, + + // Africa + { value: "Africa/Cairo", label: "Cairo (EET)" }, + { value: "Africa/Lagos", label: "Lagos (WAT)" }, + { value: "Africa/Nairobi", label: "Nairobi (EAT)" }, + { value: "Africa/Johannesburg", label: "Johannesburg (SAST)" }, + { value: "Africa/Cape_Town", label: "Cape Town (SAST)" }, + { value: "Africa/Casablanca", label: "Casablanca (WET)" }, + { value: "Africa/Tunis", label: "Tunis (CET)" }, + { value: "Africa/Algiers", label: "Algiers (CET)" }, + { value: "Africa/Addis_Ababa", label: "Addis Ababa (EAT)" }, + + // Australia/Oceania + { value: "Australia/Sydney", label: "Sydney (AEDT/AEST)" }, + { value: "Australia/Melbourne", label: "Melbourne (AEDT/AEST)" }, + { value: "Australia/Brisbane", label: "Brisbane (AEST)" }, + { value: "Australia/Perth", label: "Perth (AWST)" }, + { value: "Australia/Adelaide", label: "Adelaide (ACDT/ACST)" }, + { value: "Australia/Darwin", label: "Darwin (ACST)" }, + { value: "Pacific/Auckland", label: "Auckland (NZDT/NZST)" }, + { value: "Pacific/Fiji", label: "Fiji (FJT)" }, + { value: "Pacific/Guam", label: "Guam (ChST)" }, + { value: "Pacific/Tahiti", label: "Tahiti (TAHT)" }, + ]; + + const exampleConfigs = [ + { + name: "Business Hours 9-17", + config: { + inactivity_limit_seconds: 60, + jitter_percentage: 25, + schedule_cron_tab: "0 * 9-17 * * 1-5", + timezone: jigglerConfigState.timezone || "UTC", + }, + }, + { + name: "Business Hours 8-17", + config: { + inactivity_limit_seconds: 60, + jitter_percentage: 25, + schedule_cron_tab: "0 * 8-17 * * 1-5", + timezone: jigglerConfigState.timezone || "UTC", + }, + }, + ]; return ( -
-
+
+
+

+ Examples +

+
+ {exampleConfigs.map((example, index) => ( +
+
+ +
- Generate with{" "} - - crontab.guru - - - } + description="Cron expression for scheduling" placeholder="*/20 * * * * *" - defaultValue={jigglerConfigState.schedule_cron_tab} + value={jigglerConfigState.schedule_cron_tab} onChange={e => setJigglerConfigState({ ...jigglerConfigState, @@ -50,7 +222,7 @@ export function JigglerSetting({ %} - defaultValue={jigglerConfigState.jitter_percentage} + value={jigglerConfigState.jitter_percentage} type="number" min="0" max="100" @@ -81,9 +253,23 @@ export function JigglerSetting({ }) } /> + + + setJigglerConfigState({ + ...jigglerConfigState, + timezone: e.target.value, + }) + } + options={timezoneOptions} + />
-
+