Made support for custom timezone simpler

This commit is contained in:
Théophile Diot 2024-08-20 11:39:44 +01:00
parent b2976de125
commit 904166be7c
No known key found for this signature in database
GPG key ID: FA995104A0BA376A
25 changed files with 90 additions and 152 deletions

View file

@ -6,7 +6,6 @@ from os import getenv
from time import sleep
from typing import Any, Dict, List, Optional
from common_utils import get_timezone # type: ignore
from Database import Database # type: ignore
from logger import setup_logger # type: ignore
@ -83,9 +82,9 @@ class Config:
)
def wait_applying(self, startup: bool = False):
current_time = datetime.now(get_timezone())
current_time = datetime.now()
ready = False
while not ready and (datetime.now(get_timezone()) - current_time).seconds < 240:
while not ready and (datetime.now() - current_time).seconds < 240:
db_metadata = self._db.get_metadata()
if isinstance(db_metadata, str):
if not startup:

View file

@ -37,7 +37,7 @@ FROM python:3.12.5-alpine@sha256:c2f41e6a5a67bc39b95be3988dd19fbd05d1b82375c46d9
RUN umask 027
# Install bash and create autoconf user
RUN apk add --no-cache bash && \
RUN apk add --no-cache bash tzdata && \
addgroup -g 101 autoconf && \
adduser -h /var/cache/autoconf -g autoconf -s /bin/sh -G autoconf -D -H -u 101 autoconf

View file

@ -48,7 +48,7 @@ FROM nginx:1.26.2-alpine-slim@sha256:28967af9fa8d5e1c58a45feeb35e2f326bb6d99b120
RUN umask 027
# Install runtime dependencies
RUN apk add --no-cache openssl pcre bash python3 yajl geoip libxml2 libgd curl
RUN apk add --no-cache openssl pcre bash python3 yajl geoip libxml2 libgd curl tzdata
# Fix CVEs
RUN apk add --no-cache "busybox>=1.36.1-r17" "busybox-binsh>=1.36.1-r17" "ssl_client>=1.36.1-r17" # CVE-2023-42363 CVE-2023-42366

View file

@ -13,8 +13,6 @@ if deps_path not in sys_path:
from utils import acquire_db_lock, backup_database, BACKUP_DIR, DB_LOCK_FILE, LOGGER, restore_database
from common_utils import get_timezone # type: ignore
status = 0
try:
@ -51,7 +49,7 @@ try:
sys_exit(1)
LOGGER.info("Backing up the current database before restoring the backup ...")
current_time = datetime.now(get_timezone())
current_time = datetime.now()
tmp_backup_dir = Path(sep, "tmp", "bunkerweb", "backups")
tmp_backup_dir.mkdir(parents=True, exist_ok=True)
db = backup_database(current_time, backup_dir=tmp_backup_dir)

View file

@ -13,8 +13,6 @@ if deps_path not in sys_path:
from utils import acquire_db_lock, backup_database, BACKUP_DIR, DB_LOCK_FILE, LOGGER
from common_utils import get_timezone # type: ignore
status = 0
try:
@ -41,7 +39,7 @@ try:
LOGGER.info(f"Creating directory {directory} as it does not exist")
directory.mkdir(parents=True, exist_ok=True)
backup_database(datetime.now(get_timezone()), backup_dir=directory)
backup_database(datetime.now(), backup_dir=directory)
except SystemExit as se:
status = se.code
except:

View file

@ -16,8 +16,6 @@ from logger import setup_logger # type: ignore
from jobs import Job # type: ignore
from utils import backup_database
from common_utils import get_timezone # type: ignore
LOGGER = setup_logger("BACKUP", getenv("LOG_LEVEL", "INFO"))
status = 0
@ -37,7 +35,7 @@ try:
if last_backup_date:
last_backup_date = datetime.fromisoformat(last_backup_date)
current_time = datetime.now(get_timezone())
current_time = datetime.now()
backup_period = getenv("BACKUP_SCHEDULE", "daily")
PERIOD_STAMPS = {
"daily": timedelta(days=1).total_seconds(),

View file

@ -16,7 +16,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
if deps_path not in sys_path:
sys_path.append(deps_path)
from common_utils import bytes_hash, get_timezone # type: ignore
from common_utils import bytes_hash # type: ignore
from Database import Database # type: ignore
from logger import setup_logger # type: ignore
from model import Base # type: ignore
@ -30,7 +30,7 @@ DB_LOCK_FILE = Path(sep, "var", "lib", "bunkerweb", "db.lock")
def acquire_db_lock():
"""Acquire the database lock to prevent concurrent access to the database."""
current_time = datetime.now(get_timezone())
current_time = datetime.now()
while DB_LOCK_FILE.is_file() and DB_LOCK_FILE.stat().st_ctime + 30 > current_time.timestamp():
LOGGER.warning("Database is locked, waiting for it to be unlocked (timeout: 30s) ...")
sleep(1)
@ -46,9 +46,9 @@ def backup_database(current_time: datetime, db: Database = None, backup_dir: Pat
backup_file = backup_dir.joinpath(f"backup-{database}-{current_time.strftime('%Y-%m-%d_%H-%M-%S')}.zip")
LOGGER.debug(f"Backup file path: {backup_file}")
stderr = "Table 'db.test_"
current_time = datetime.now(get_timezone())
current_time = datetime.now()
while "Table 'db.test_" in stderr and (datetime.now(get_timezone()) - current_time).total_seconds() < 10:
while "Table 'db.test_" in stderr and (datetime.now() - current_time).total_seconds() < 10:
if database == "sqlite":
match = DB_STRING_RX.search(db.database_uri)
if not match:
@ -94,7 +94,7 @@ def backup_database(current_time: datetime, db: Database = None, backup_dir: Pat
LOGGER.error(f"Failed to dump the database: {stderr}")
sys_exit(1)
if (datetime.now(get_timezone()) - current_time).total_seconds() >= 10:
if (datetime.now() - current_time).total_seconds() >= 10:
LOGGER.error("Failed to dump the database: Timeout reached")
sys_exit(1)

View file

@ -18,7 +18,7 @@ from maxminddb import open_database
from requests import RequestException, Response, get
from logger import setup_logger # type: ignore
from common_utils import bytes_hash, get_timezone, file_hash # type: ignore
from common_utils import bytes_hash, file_hash # type: ignore
from jobs import Job # type: ignore
LOGGER = setup_logger("JOBS.mmdb-asn", getenv("LOG_LEVEL", "INFO"))
@ -62,7 +62,7 @@ try:
if response and response.status_code == 200:
skip_dl = response.content.find(bytes_hash(job_cache["data"], algorithm="sha1").encode()) != -1
elif job_cache["last_update"] < (datetime.now(get_timezone()) - timedelta(weeks=1)).timestamp():
elif job_cache["last_update"] < (datetime.now() - timedelta(weeks=1)).timestamp():
LOGGER.warning("Unable to check if the cache file is the latest version from db-ip.com and file is older than 1 week, checking anyway...")
skip_dl = False

View file

@ -18,7 +18,7 @@ from maxminddb import open_database
from requests import RequestException, Response, get
from logger import setup_logger # type: ignore
from common_utils import bytes_hash, get_timezone, file_hash # type: ignore
from common_utils import bytes_hash, file_hash # type: ignore
from jobs import Job # type: ignore
LOGGER = setup_logger("JOBS.mmdb-country", getenv("LOG_LEVEL", "INFO"))
@ -62,7 +62,7 @@ try:
if response and response.status_code == 200:
skip_dl = response.content.find(bytes_hash(job_cache["data"], algorithm="sha1").encode()) != -1
elif job_cache["last_update"] < (datetime.now(get_timezone()) - timedelta(weeks=1)).timestamp():
elif job_cache["last_update"] < (datetime.now() - timedelta(weeks=1)).timestamp():
LOGGER.warning("Unable to check if the cache file is the latest version from db-ip.com and file is older than 1 week, checking anyway...")
skip_dl = False

View file

@ -23,7 +23,7 @@ from requests import get
from Database import Database # type: ignore
from logger import setup_logger # type: ignore
from common_utils import bytes_hash, get_os_info, get_integration, get_timezone, get_version # type: ignore
from common_utils import bytes_hash, get_os_info, get_integration, get_version # type: ignore
API_ENDPOINT = "https://api.bunkerweb.io"
PREVIEW_ENDPOINT = "https://assets.bunkerity.com/bw-pro/preview"
@ -95,7 +95,7 @@ def install_plugin(plugin_path: Path, db, preview: bool = True) -> bool:
try:
db = Database(LOGGER, sqlalchemy_string=getenv("DATABASE_URI"))
db_metadata = db.get_metadata()
current_date = datetime.now(get_timezone())
current_date = datetime.now()
pro_license_key = getenv("PRO_LICENSE_KEY", "").strip()
LOGGER.info("Checking BunkerWeb Pro status...")

View file

@ -43,7 +43,7 @@ for deps_path in [os_join(sep, "usr", "share", "bunkerweb", *paths) for paths in
if deps_path not in sys_path:
sys_path.append(deps_path)
from common_utils import bytes_hash, get_timezone # type: ignore
from common_utils import bytes_hash # type: ignore
from pymysql import install_as_MySQLdb
from sqlalchemy import create_engine, event, MetaData as sql_metadata, func, join, select as db_select, text, inspect
@ -168,7 +168,7 @@ class Database:
DATABASE_RETRY_TIMEOUT = int(DATABASE_RETRY_TIMEOUT)
current_time = datetime.now(get_timezone())
current_time = datetime.now()
not_connected = True
fallback = False
@ -185,7 +185,7 @@ class Database:
not_connected = False
except (OperationalError, DatabaseError) as e:
if (datetime.now(get_timezone()) - current_time).total_seconds() > DATABASE_RETRY_TIMEOUT:
if (datetime.now() - current_time).total_seconds() > DATABASE_RETRY_TIMEOUT:
if not fallback and self.database_uri_readonly:
self.logger.error(f"Can't connect to database after {DATABASE_RETRY_TIMEOUT} seconds. Falling back to read-only database connection")
self.sql_engine.dispose(close=True)
@ -241,7 +241,7 @@ class Database:
def retry_connection(self, *, readonly: bool = False, fallback: bool = False, log: bool = True, **kwargs) -> None:
"""Retry the connection to the database"""
self.last_connection_retry = datetime.now(get_timezone())
self.last_connection_retry = datetime.now()
if log:
self.logger.debug(f"Retrying the connection to the database{' in read-only mode' if readonly else ''}{' with fallback' if fallback else ''} ...")
@ -476,7 +476,7 @@ class Database:
if not metadata:
return "The metadata are not set yet, try again"
current_time = datetime.now(get_timezone())
current_time = datetime.now()
if "config" in changes:
if not metadata.first_config_saved:
@ -536,7 +536,7 @@ class Database:
db_ui_version = db_version
self.logger.warning(f"Database version ({db_version}) is different from Bunkerweb version ({bunkerweb_version}), migrating ...")
current_time = datetime.now(get_timezone())
current_time = datetime.now()
error = True
# ? Wait for the metadata to be available
while error:
@ -545,7 +545,7 @@ class Database:
metadata.reflect(self.sql_engine)
error = False
except BaseException as e:
if (datetime.now(get_timezone()) - current_time).total_seconds() > 10:
if (datetime.now() - current_time).total_seconds() > 10:
raise e
sleep(1)
@ -1328,7 +1328,7 @@ class Database:
session.query(Custom_configs).filter(Custom_configs.service_id.in_(missing_ids)).delete()
session.query(Jobs_cache).filter(Jobs_cache.service_id.in_(missing_ids)).delete()
session.query(Metadata).filter_by(id=1).update(
{Metadata.custom_configs_changed: True, Metadata.last_custom_configs_change: datetime.now(get_timezone())}
{Metadata.custom_configs_changed: True, Metadata.last_custom_configs_change: datetime.now()}
)
changed_services = True
@ -1672,7 +1672,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.custom_configs_changed = True
metadata.last_custom_configs_change = datetime.now(get_timezone())
metadata.last_custom_configs_change = datetime.now()
try:
session.add_all(to_put)
@ -1994,7 +1994,7 @@ class Database:
if self.readonly:
return "The database is read-only, the changes will not be saved"
session.add(Jobs_runs(job_name=job_name, success=success, start_date=start_date, end_date=end_date or datetime.now(get_timezone())))
session.add(Jobs_runs(job_name=job_name, success=success, start_date=start_date, end_date=end_date or datetime.now()))
try:
session.commit()
@ -2062,13 +2062,13 @@ class Database:
service_id=service_id,
file_name=file_name,
data=data,
last_update=datetime.now(get_timezone()),
last_update=datetime.now(),
checksum=checksum,
)
)
else:
cache.data = data
cache.last_update = datetime.now(get_timezone())
cache.last_update = datetime.now()
cache.checksum = checksum
try:
@ -2858,10 +2858,10 @@ class Database:
if metadata is not None:
if _type in ("external", "ui"):
metadata.external_plugins_changed = True
metadata.last_external_plugins_change = datetime.now(get_timezone())
metadata.last_external_plugins_change = datetime.now()
elif _type == "pro":
metadata.pro_plugins_changed = True
metadata.last_pro_plugins_change = datetime.now(get_timezone())
metadata.last_pro_plugins_change = datetime.now()
try:
session.add_all(to_put)
@ -2902,10 +2902,10 @@ class Database:
if metadata is not None:
if method in ("external", "ui"):
metadata.external_plugins_changed = True
metadata.last_external_plugins_change = datetime.now(get_timezone())
metadata.last_external_plugins_change = datetime.now()
elif method == "pro":
metadata.pro_plugins_changed = True
metadata.last_pro_plugins_change = datetime.now(get_timezone())
metadata.last_pro_plugins_change = datetime.now()
try:
session.commit()
@ -3127,7 +3127,7 @@ class Database:
if db_instance is not None:
return f"Instance {hostname} already exists, will not be added."
current_time = datetime.now(get_timezone())
current_time = datetime.now()
session.add(
Instances(
hostname=hostname,
@ -3145,7 +3145,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now(get_timezone())
metadata.last_instances_change = datetime.now()
try:
session.commit()
@ -3172,7 +3172,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now(get_timezone())
metadata.last_instances_change = datetime.now()
try:
session.commit()
@ -3194,7 +3194,7 @@ class Database:
if instance.get("hostname") is None:
continue
current_time = datetime.now(get_timezone())
current_time = datetime.now()
to_put.append(
Instances(
hostname=instance["hostname"],
@ -3214,7 +3214,7 @@ class Database:
metadata = session.query(Metadata).get(1)
if metadata is not None:
metadata.instances_changed = True
metadata.last_instances_change = datetime.now(get_timezone())
metadata.last_instances_change = datetime.now()
try:
session.add_all(to_put)
@ -3236,7 +3236,7 @@ class Database:
return f"Instance {hostname} does not exist, will not be updated."
db_instance.status = status
db_instance.last_seen = datetime.now(get_timezone())
db_instance.last_seen = datetime.now()
try:
session.commit()

View file

@ -209,6 +209,7 @@ class Configurator:
"SHLVL",
"SERVER_SOFTWARE",
"NAMESPACE",
"TZ",
)
):
self.__logger.warning(f"Ignoring variable {variable} : {err} - {value = !r}")

View file

@ -1,13 +1,10 @@
from hashlib import new as new_hash
from io import BytesIO
from logging import error
from os import environ, getenv, sep
from os import getenv, sep
from pathlib import Path
from platform import machine
from typing import Dict, Union
from pytz import UnknownTimeZoneError, timezone
def dict_to_frozenset(d):
if isinstance(d, list):
@ -91,12 +88,3 @@ def bytes_hash(bio: Union[str, bytes, BytesIO], *, algorithm: str = "sha512") ->
_hash.update(data)
bio.seek(0, 0)
return _hash.hexdigest()
def get_timezone():
try:
return timezone(getenv("TZ", "UTC"))
except UnknownTimeZoneError as e:
environ["TZ"] = "UTC"
error(f"Invalid timezone: {e}, using UTC instead")
return timezone("UTC")

View file

@ -14,7 +14,7 @@ from threading import Lock
from traceback import format_exc
from typing import Any, Dict, Literal, Optional, Tuple, Union
from common_utils import bytes_hash, file_hash, get_timezone
from common_utils import bytes_hash, file_hash
LOCK = Lock()
EXPIRE_TIME = {
@ -144,7 +144,7 @@ class Job:
try:
cache_info = self.get_cache(name, job_name=job_name, service_id=service_id, plugin_id=plugin_id, with_info=True, with_data=False)
if isinstance(cache_info, dict):
current_time = datetime.now(get_timezone()).timestamp()
current_time = datetime.now().timestamp()
if current_time < cache_info["last_update"]:
return False
is_cached = current_time - cache_info["last_update"] < EXPIRE_TIME[expire]

View file

@ -1,41 +1,7 @@
from datetime import datetime, timezone
from logging import (
CRITICAL,
DEBUG,
ERROR,
INFO,
WARNING,
Formatter,
Logger,
_nameToLevel,
addLevelName,
getLogger,
setLoggerClass,
StreamHandler,
)
from logging import CRITICAL, DEBUG, ERROR, INFO, WARNING, Logger, _nameToLevel, addLevelName, basicConfig, getLogger, setLoggerClass
from os import getenv
from typing import Optional, Union
from common_utils import get_timezone
class BwFormatter(Formatter):
"""Overrides logging.Formatter to use an aware datetime object."""
def converter(self, timestamp):
"""Convert timestamp to an aware datetime object in the desired timezone."""
return datetime.fromtimestamp(timestamp, tz=timezone.utc).astimezone(get_timezone())
def formatTime(self, record, datefmt=None):
"""Format the datetime according to the specified format or ISO 8601."""
dt = self.converter(record.created)
if datefmt:
return dt.strftime(datefmt)
try:
return dt.isoformat(timespec="milliseconds")
except TypeError:
return dt.isoformat()
class BWLogger(Logger):
"""Custom logger class inheriting from the standard Logger."""
@ -50,17 +16,11 @@ setLoggerClass(BWLogger)
# Set the default logging level based on environment variables
default_level = _nameToLevel.get(getenv("CUSTOM_LOG_LEVEL", getenv("LOG_LEVEL", "INFO")).upper(), INFO)
# Create a custom formatter instance
formatter = BwFormatter(fmt="%(asctime)s [%(name)s] [%(process)d] [%(levelname)s] - %(message)s", datefmt="[%Y-%m-%d %H:%M:%S %z]")
# Create a console handler and set the custom formatter
handler = StreamHandler()
handler.setFormatter(formatter)
# Get the root logger and add the handler to it
root_logger = getLogger()
root_logger.setLevel(default_level)
root_logger.addHandler(handler)
basicConfig(
format="%(asctime)s [%(name)s] [%(process)d] [%(levelname)s] - %(message)s",
datefmt="[%Y-%m-%d %H:%M:%S %z]",
level=default_level,
)
# Set the default logging level for specific SQLAlchemy components
database_default_level = _nameToLevel.get(getenv("DATABASE_LOG_LEVEL", "WARNING").upper(), WARNING)

View file

@ -41,7 +41,7 @@ FROM python:3.12.5-alpine@sha256:c2f41e6a5a67bc39b95be3988dd19fbd05d1b82375c46d9
RUN umask 027
# Install runtime dependencies and add scheduler user
RUN apk add --no-cache bash unzip libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client postgresql-client sqlite && \
RUN apk add --no-cache bash unzip libgcc libstdc++ libpq openssl libmagic mariadb-connector-c mariadb-client postgresql-client sqlite tzdata && \
addgroup -g 101 scheduler && \
adduser -h /var/cache/nginx -g scheduler -s /bin/sh -G scheduler -D -H -u 101 scheduler

View file

@ -27,7 +27,6 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
if deps_path not in sys_path:
sys_path.append(deps_path)
from common_utils import get_timezone # type: ignore
from Database import Database # type: ignore
from logger import setup_logger # type: ignore
from ApiCaller import ApiCaller # type: ignore
@ -163,7 +162,7 @@ class JobScheduler(ApiCaller):
self.__logger.info(f"Executing job {name} from plugin {plugin} ...")
success = True
ret = -1
start_date = datetime.now(get_timezone())
start_date = datetime.now()
try:
proc = run(join(path, "jobs", file), stdin=DEVNULL, stderr=STDOUT, env=self.__env, check=False)
ret = proc.returncode
@ -172,7 +171,7 @@ class JobScheduler(ApiCaller):
self.__logger.error(f"Exception while executing job {name} from plugin {plugin} :\n{format_exc()}")
with self.__thread_lock:
self.__job_success = False
end_date = datetime.now(get_timezone())
end_date = datetime.now()
if ret == 1:
with self.__thread_lock:
@ -352,7 +351,7 @@ class JobScheduler(ApiCaller):
except BaseException:
self.db.readonly = True
return True
elif not force and self.db.last_connection_retry and (datetime.now(get_timezone()) - self.db.last_connection_retry).total_seconds() > 30:
elif not force and self.db.last_connection_retry and (datetime.now() - self.db.last_connection_retry).total_seconds() > 30:
return True
if self.db.database_uri and self.db.readonly:

View file

@ -27,7 +27,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
from dotenv import dotenv_values
from schedule import every as schedule_every, run_pending
from common_utils import bytes_hash, dict_to_frozenset, get_integration, get_timezone # type: ignore
from common_utils import bytes_hash, dict_to_frozenset, get_integration # type: ignore
from logger import setup_logger # type: ignore
from Database import Database # type: ignore
from JobScheduler import JobScheduler
@ -97,8 +97,8 @@ MASTER_MODE = getenv("MASTER_MODE", "no") == "yes"
def handle_stop(signum, frame):
current_time = datetime.now(get_timezone())
while APPLYING_CHANGES.is_set() and (datetime.now(get_timezone()) - current_time).seconds < 30:
current_time = datetime.now()
while APPLYING_CHANGES.is_set() and (datetime.now() - current_time).seconds < 30:
LOGGER.warning("Waiting for the changes to be applied before stopping ...")
sleep(1)
@ -373,8 +373,12 @@ def run_in_slave_mode(): # TODO: Refactor this feature
continue
sleep(5)
tz = getenv("TZ")
if tz:
env["TZ"] = tz
# Instantiate scheduler environment
SCHEDULER.env = env | {"TZ": getenv("TZ", "UTC"), "LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", env.get("LOG_LEVEL", "notice"))}
SCHEDULER.env = env | {"LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", env.get("LOG_LEVEL", "notice"))}
threads = [
Thread(target=generate_custom_configs),
@ -561,9 +565,12 @@ if __name__ == "__main__":
env = SCHEDULER.db.get_config()
env["DATABASE_URI"] = SCHEDULER.db.database_uri
tz = getenv("TZ")
if tz:
env["TZ"] = tz
# Instantiate scheduler environment
SCHEDULER.env = env | {"TZ": getenv("TZ", "UTC"), "LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", env.get("LOG_LEVEL", "notice"))}
SCHEDULER.env = env | {"LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", env.get("LOG_LEVEL", "notice"))}
threads = []
@ -685,8 +692,6 @@ if __name__ == "__main__":
LOGGER.info("Running plugins download jobs ...")
# Update the environment variables of the scheduler
SCHEDULER.env = env | {"TZ": getenv("TZ", "UTC"), "LOG_LEVEL": getenv("CUSTOM_LOG_LEVEL", env.get("LOG_LEVEL", "notice"))}
if not SCHEDULER.run_single("download-plugins"):
LOGGER.warning("download-plugins job failed at first start, plugins settings set by the user may not be up to date ...")
if not SCHEDULER.run_single("download-pro-plugins"):
@ -729,6 +734,9 @@ if __name__ == "__main__":
SCHEDULER.update_jobs()
env = SCHEDULER.db.get_config()
env["DATABASE_URI"] = SCHEDULER.db.database_uri
tz = getenv("TZ")
if tz:
env["TZ"] = tz
LOGGER.info("Executing scheduler ...")
@ -900,7 +908,7 @@ if __name__ == "__main__":
scheduler_first_start = False
if not HEALTHY_PATH.is_file():
HEALTHY_PATH.write_text(datetime.now(get_timezone()).isoformat(), encoding="utf-8")
HEALTHY_PATH.write_text(datetime.now().isoformat(), encoding="utf-8")
APPLYING_CHANGES.clear()
schedule_every(HEALTHCHECK_INTERVAL).seconds.do(healthcheck_job)
@ -913,7 +921,7 @@ if __name__ == "__main__":
sleep(3 if SCHEDULER.db.readonly else 1)
run_pending()
SCHEDULER.run_pending()
current_time = datetime.now(get_timezone())
current_time = datetime.now()
while DB_LOCK_FILE.is_file() and DB_LOCK_FILE.stat().st_ctime + 30 > current_time.timestamp():
LOGGER.debug("Database is locked, waiting for it to be unlocked (timeout: 30s) ...")
@ -1043,6 +1051,9 @@ if __name__ == "__main__":
CHANGES.append("config")
env = SCHEDULER.db.get_config()
env["DATABASE_URI"] = SCHEDULER.db.database_uri
tz = getenv("TZ")
if tz:
env["TZ"] = tz
except:
LOGGER.error(f"Exception while executing scheduler : {format_exc()}")

View file

@ -56,7 +56,7 @@ FROM python:3.12.5-alpine@sha256:c2f41e6a5a67bc39b95be3988dd19fbd05d1b82375c46d9
RUN umask 027
# Install runtime dependencies and add ui user
RUN apk add --no-cache bash unzip libmagic mariadb-connector-c mariadb-client postgresql-client sqlite && \
RUN apk add --no-cache bash unzip libmagic mariadb-connector-c mariadb-client postgresql-client sqlite tzdata && \
addgroup -g 101 ui && \
adduser -h /var/cache/nginx -g ui -s /bin/bash -G ui -D -H -u 101 ui

View file

@ -18,8 +18,6 @@ from json import dumps
from signal import SIGINT, signal, SIGTERM
from time import time
from common_utils import get_timezone # type: ignore
from src.reverse_proxied import ReverseProxied
from pages.bans import bans
@ -245,10 +243,7 @@ def before_request():
if (
DB.database_uri
and DB.readonly
and (
datetime.now(get_timezone()) - datetime.fromisoformat(DATA.get("LAST_DATABASE_RETRY", "1970-01-01T00:00:00")).replace(tzinfo=get_timezone())
> timedelta(minutes=1)
)
and (datetime.now() - datetime.fromisoformat(DATA.get("LAST_DATABASE_RETRY", "1970-01-01T00:00:00")).astimezone() > timedelta(minutes=1))
):
try:
DB.retry_connection(pool_timeout=1)
@ -265,19 +260,19 @@ def before_request():
DB.retry_connection(fallback=True, pool_timeout=1)
DB.retry_connection(fallback=True, log=False)
DATA["READONLY_MODE"] = True
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now(get_timezone()).isoformat()
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat()
elif not DATA.get("READONLY_MODE", False) and request.method == "POST" and not ("/totp" in request.path or "/login" in request.path):
try:
DB.test_write()
DATA["READONLY_MODE"] = False
except BaseException:
DATA["READONLY_MODE"] = True
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now(get_timezone()).isoformat()
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat()
else:
try:
DB.test_read()
except BaseException:
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now(get_timezone()).isoformat()
DATA["LAST_DATABASE_RETRY"] = DB.last_connection_retry.isoformat() if DB.last_connection_retry else datetime.now().isoformat()
DB.readonly = DATA.get("READONLY_MODE", False)

View file

@ -6,8 +6,6 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
if deps_path not in sys_path:
sys_path.append(deps_path)
from common_utils import get_timezone # type: ignore
from bcrypt import checkpw
from flask_login import AnonymousUserMixin, UserMixin
from sqlalchemy.orm import declarative_base, relationship
@ -29,8 +27,8 @@ class AnonymousUser(AnonymousUserMixin):
last_login_ip = None
login_count = 0
totp_secret = None
creation_date = datetime.now(get_timezone())
update_date = datetime.now(get_timezone())
creation_date = datetime.now()
update_date = datetime.now()
list_roles = []
list_permissions = []
list_recovery_codes = []

View file

@ -8,8 +8,6 @@ from flask import Blueprint, flash, redirect, render_template, request, url_for
from flask_login import login_required
from redis import Redis, Sentinel
from common_utils import get_timezone # type: ignore
from builder.bans import bans_builder # type: ignore
from dependencies import BW_CONFIG, BW_INSTANCES_UTILS, DB
@ -170,7 +168,7 @@ def bans_page():
ban_end = float(ban["ban_end"])
except ValueError:
continue
ban_end = (datetime.fromtimestamp(ban_end) - datetime.now(get_timezone())).total_seconds()
ban_end = (datetime.fromtimestamp(ban_end) - datetime.now()).total_seconds()
if redis_client:
ok = redis_client.set(f"bans_ip_{ban['ip']}", dumps({"reason": reason, "date": time()}))

View file

@ -3,8 +3,6 @@ from datetime import datetime
from flask import Blueprint, flash, redirect, render_template, request, session, url_for
from flask_login import current_user, login_user
from common_utils import get_timezone # type: ignore
from dependencies import DB
from utils import LOGGER
@ -29,7 +27,7 @@ def login_page():
session["user_agent"] = request.headers.get("User-Agent")
session["totp_validated"] = False
ui_user.last_login_at = datetime.now(get_timezone())
ui_user.last_login_at = datetime.now()
ui_user.last_login_ip = request.remote_addr
ui_user.login_count += 1

View file

@ -10,8 +10,6 @@ from flask import Response, flash, redirect, request, url_for
from qrcode.main import QRCode
from regex import compile as re_compile
from common_utils import get_timezone # type: ignore
from src.instance import Instance
from dependencies import BW_CONFIG, DATA, DB
@ -25,9 +23,9 @@ PLUGIN_ID_RX = re_compile(r"^[\w_-]{1,64}$")
def wait_applying():
current_time = datetime.now(get_timezone())
current_time = datetime.now()
ready = False
while not ready and (datetime.now(get_timezone()) - current_time).seconds < 120:
while not ready and (datetime.now() - current_time).seconds < 120:
db_metadata = DB.get_metadata()
if isinstance(db_metadata, str):
LOGGER.error(f"An error occurred when checking for changes in the database : {db_metadata}")

View file

@ -16,7 +16,6 @@ from sqlalchemy import MetaData, inspect, text
from sqlalchemy.orm import joinedload
from sqlalchemy.exc import IntegrityError
from common_utils import get_timezone # type: ignore
from Database import Database # type: ignore
from model import Metadata # type: ignore
@ -48,7 +47,7 @@ class UIDatabase(Database):
if db_version != bunkerweb_version:
self.logger.warning(f"UI tables version ({db_version}) is different from BunkerWeb version ({bunkerweb_version}), migrating them ...")
current_time = datetime.now(get_timezone())
current_time = datetime.now()
error = True
while error:
try:
@ -56,7 +55,7 @@ class UIDatabase(Database):
metadata.reflect(self.sql_engine)
error = False
except BaseException as e:
if (datetime.now(get_timezone()) - current_time).total_seconds() > 10:
if (datetime.now() - current_time).total_seconds() > 10:
raise e
sleep(1)
@ -205,7 +204,7 @@ class UIDatabase(Database):
return f"Role {role} doesn't exist"
session.add(RolesUsers(user_name=username, role_name=role))
current_time = datetime.now(get_timezone())
current_time = datetime.now()
session.add(
Users(
username=username,
@ -255,7 +254,7 @@ class UIDatabase(Database):
user.password = password.decode("utf-8")
user.totp_secret = totp_secret
user.method = method
user.update_date = datetime.now(get_timezone())
user.update_date = datetime.now()
try:
session.commit()
@ -318,7 +317,7 @@ class UIDatabase(Database):
if session.query(Roles).with_entities(Roles.name).filter_by(name=name).first():
return f"Role {name} already exists"
session.add(Roles(name=name, description=description, update_datetime=datetime.now(get_timezone())))
session.add(Roles(name=name, description=description, update_datetime=datetime.now()))
for permission in permissions:
if not session.query(Permissions).with_entities(Permissions.name).filter_by(name=permission).first():