mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-07-09 03:04:54 -04:00
fix: Convert Daily Schedule Time to UTC (#3914)
This commit is contained in:
parent
e33b62be2a
commit
5b1e827d45
@ -4,20 +4,20 @@
|
|||||||
|
|
||||||
### General
|
### General
|
||||||
|
|
||||||
| Variables | Default | Description |
|
| Variables | Default | Description |
|
||||||
| ----------------------------- | :-------------------: | ----------------------------------------------------------------------------------- |
|
| ----------------------------- | :-------------------: | --------------------------------------------------------------------------------------------------------- |
|
||||||
| PUID | 911 | UserID permissions between host OS and container |
|
| PUID | 911 | UserID permissions between host OS and container |
|
||||||
| PGID | 911 | GroupID permissions between host OS and container |
|
| PGID | 911 | GroupID permissions between host OS and container |
|
||||||
| DEFAULT_GROUP | Home | The default group for users |
|
| DEFAULT_GROUP | Home | The default group for users |
|
||||||
| BASE_URL | http://localhost:8080 | Used for Notifications |
|
| BASE_URL | http://localhost:8080 | Used for Notifications |
|
||||||
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
|
| TOKEN_TIME | 48 | The time in hours that a login/auth token is valid |
|
||||||
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
| API_PORT | 9000 | The port exposed by backend API. **Do not change this if you're running in Docker** |
|
||||||
| API_DOCS | True | Turns on/off access to the API documentation locally. |
|
| API_DOCS | True | Turns on/off access to the API documentation locally |
|
||||||
| TZ | UTC | Must be set to get correct date/time on the server |
|
| TZ | UTC | Must be set to get correct date/time on the server |
|
||||||
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
|
| ALLOW_SIGNUP<super>\*</super> | false | Allow user sign-up without token |
|
||||||
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
|
| LOG_CONFIG_OVERRIDE | | Override the config for logging with a custom path |
|
||||||
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug, trace) |
|
| LOG_LEVEL | info | Logging level (e.g. critical, error, warning, info, debug, trace) |
|
||||||
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run the daily tasks. |
|
| DAILY_SCHEDULE_TIME | 23:45 | The time of day to run daily server tasks, in HH:MM format. Use the server's local time, *not* UTC |
|
||||||
|
|
||||||
<super>\*</super> Starting in v1.4.0 this was changed to default to `false` as part of a security review of the application.
|
<super>\*</super> Starting in v1.4.0 this was changed to default to `false` as part of a security review of the application.
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
import logging
|
||||||
import secrets
|
import secrets
|
||||||
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import NamedTuple
|
||||||
|
|
||||||
|
from dateutil.tz import tzlocal
|
||||||
from pydantic import field_validator
|
from pydantic import field_validator
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
@ -9,6 +13,11 @@ from mealie.core.settings.themes import Theme
|
|||||||
from .db_providers import AbstractDBProvider, db_provider_factory
|
from .db_providers import AbstractDBProvider, db_provider_factory
|
||||||
|
|
||||||
|
|
||||||
|
class ScheduleTime(NamedTuple):
|
||||||
|
hour: int
|
||||||
|
minute: int
|
||||||
|
|
||||||
|
|
||||||
def determine_secrets(data_dir: Path, production: bool) -> str:
|
def determine_secrets(data_dir: Path, production: bool) -> str:
|
||||||
if not production:
|
if not production:
|
||||||
return "shh-secret-test-key"
|
return "shh-secret-test-key"
|
||||||
@ -58,6 +67,44 @@ class AppSettings(BaseSettings):
|
|||||||
ALLOW_SIGNUP: bool = False
|
ALLOW_SIGNUP: bool = False
|
||||||
|
|
||||||
DAILY_SCHEDULE_TIME: str = "23:45"
|
DAILY_SCHEDULE_TIME: str = "23:45"
|
||||||
|
"""Local server time, in HH:MM format. See `DAILY_SCHEDULE_TIME_UTC` for the parsed UTC equivalent"""
|
||||||
|
|
||||||
|
_logger: logging.Logger | None = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def logger(self) -> logging.Logger:
|
||||||
|
if self._logger is None:
|
||||||
|
# lazy load the logger, since get_logger depends on the settings being loaded
|
||||||
|
from mealie.core.root_logger import get_logger
|
||||||
|
|
||||||
|
self._logger = get_logger()
|
||||||
|
|
||||||
|
return self._logger
|
||||||
|
|
||||||
|
@property
|
||||||
|
def DAILY_SCHEDULE_TIME_UTC(self) -> ScheduleTime:
|
||||||
|
"""The DAILY_SCHEDULE_TIME in UTC, parsed into hours and minutes"""
|
||||||
|
|
||||||
|
# parse DAILY_SCHEDULE_TIME into hours and minutes
|
||||||
|
try:
|
||||||
|
hour_str, minute_str = self.DAILY_SCHEDULE_TIME.split(":")
|
||||||
|
local_hour = int(hour_str)
|
||||||
|
local_minute = int(minute_str)
|
||||||
|
except ValueError:
|
||||||
|
local_hour = 23
|
||||||
|
local_minute = 45
|
||||||
|
self.logger.exception(
|
||||||
|
f"Unable to parse {self.DAILY_SCHEDULE_TIME=} as HH:MM; defaulting to {local_hour}:{local_minute}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# DAILY_SCHEDULE_TIME is in local time, so we convert it to UTC
|
||||||
|
local_tz = tzlocal()
|
||||||
|
now = datetime.now(local_tz)
|
||||||
|
local_time = now.replace(hour=local_hour, minute=local_minute)
|
||||||
|
utc_time = local_time.astimezone(timezone.utc)
|
||||||
|
|
||||||
|
self.logger.debug(f"Local time: {local_hour}:{local_minute} | UTC time: {utc_time.hour}:{utc_time.minute}")
|
||||||
|
return ScheduleTime(utc_time.hour, utc_time.minute)
|
||||||
|
|
||||||
# ===============================================
|
# ===============================================
|
||||||
# Security Configuration
|
# Security Configuration
|
||||||
|
@ -29,20 +29,12 @@ class SchedulerService:
|
|||||||
|
|
||||||
async def schedule_daily():
|
async def schedule_daily():
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
daily_schedule_time = get_app_settings().DAILY_SCHEDULE_TIME
|
daily_schedule_time = get_app_settings().DAILY_SCHEDULE_TIME_UTC
|
||||||
logger.debug(
|
logger.debug(f"Current time is {now} and DAILY_SCHEDULE_TIME (in UTC) is {daily_schedule_time}")
|
||||||
"Current time is %s and DAILY_SCHEDULE_TIME is %s",
|
|
||||||
str(now),
|
|
||||||
daily_schedule_time,
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
hour_target, minute_target = _parse_daily_schedule_time(daily_schedule_time)
|
|
||||||
except Exception:
|
|
||||||
logger.exception(f"Unable to parse {daily_schedule_time=}")
|
|
||||||
hour_target = 23
|
|
||||||
minute_target = 45
|
|
||||||
|
|
||||||
next_schedule = now.replace(hour=hour_target, minute=minute_target, second=0, microsecond=0)
|
next_schedule = now.replace(
|
||||||
|
hour=daily_schedule_time.hour, minute=daily_schedule_time.minute, second=0, microsecond=0
|
||||||
|
)
|
||||||
delta = next_schedule - now
|
delta = next_schedule - now
|
||||||
if delta < timedelta(0):
|
if delta < timedelta(0):
|
||||||
next_schedule = next_schedule + timedelta(days=1)
|
next_schedule = next_schedule + timedelta(days=1)
|
||||||
@ -61,12 +53,6 @@ async def schedule_daily():
|
|||||||
await run_daily()
|
await run_daily()
|
||||||
|
|
||||||
|
|
||||||
def _parse_daily_schedule_time(time):
|
|
||||||
hour_target = int(time.split(":")[0])
|
|
||||||
minute_target = int(time.split(":")[1])
|
|
||||||
return hour_target, minute_target
|
|
||||||
|
|
||||||
|
|
||||||
def _scheduled_task_wrapper(callable):
|
def _scheduled_task_wrapper(callable):
|
||||||
try:
|
try:
|
||||||
callable()
|
callable()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user