__package__ = "archivebox.config" import os import sys from datetime import datetime, timezone from rich.console import Console import django import django.db from archivebox.misc import logging from . import CONSTANTS from .common import SHELL_CONFIG if not SHELL_CONFIG.USE_COLOR: os.environ["NO_COLOR"] = "1" if not SHELL_CONFIG.SHOW_PROGRESS: os.environ["TERM"] = "dumb" # recreate rich console obj based on new config values STDOUT = CONSOLE = Console() STDERR = Console(stderr=True) logging.CONSOLE = CONSOLE def setup_django_minimal(): # sys.path.append(str(CONSTANTS.PACKAGE_DIR)) # os.environ.setdefault('ARCHIVEBOX_DATA_DIR', str(CONSTANTS.DATA_DIR)) # os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings') # django.setup() raise Exception("dont use this anymore") DJANGO_SET_UP = False def setup_django(check_db=False, in_memory_db=False) -> None: from rich.panel import Panel global DJANGO_SET_UP if DJANGO_SET_UP: # raise Exception('django is already set up!') # TODO: figure out why CLI entrypoints with init_pending are running this twice sometimes return from archivebox.config.permissions import IS_ROOT, ARCHIVEBOX_USER, ARCHIVEBOX_GROUP, SudoPermission # if running as root, chown the data dir to the archivebox user to make sure it's accessible to the archivebox user if IS_ROOT and ARCHIVEBOX_USER != 0: with SudoPermission(uid=0): # running as root is a special case where it's ok to be a bit slower # make sure data dir is always owned by the correct user os.system(f'chown {ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP} "{CONSTANTS.DATA_DIR}" 2>/dev/null') os.system(f'chown {ARCHIVEBOX_USER}:{ARCHIVEBOX_GROUP} "{CONSTANTS.DATA_DIR}"/* 2>/dev/null') # Suppress the "database access during app initialization" warning # This warning can be triggered during django.setup() but is safe to ignore # since we're doing intentional setup operations import warnings warnings.filterwarnings( "ignore", message=".*Accessing the database during app initialization.*", category=RuntimeWarning, ) try: from django.core.management import call_command if in_memory_db: raise Exception("dont use this anymore") # some commands dont store a long-lived sqlite3 db file on disk. # in those cases we create a temporary in-memory db and run the migrations # immediately to get a usable in-memory-database at startup os.environ.setdefault("ARCHIVEBOX_DATABASE_NAME", ":memory:") django.setup() call_command("migrate", interactive=False, verbosity=0) else: # Otherwise use default sqlite3 file-based database and initialize django # without running migrations automatically (user runs them manually by calling init) try: django.setup() except Exception as e: is_using_meta_cmd = any(ignored_subcommand in sys.argv for ignored_subcommand in ("help", "version", "--help", "--version")) if not is_using_meta_cmd: # show error message to user only if they're not running a meta command / just trying to get help STDERR.print() STDERR.print( Panel( f"\n[red]{e.__class__.__name__}[/red]: [yellow]{e}[/yellow]\nPlease check your config and [blue]DATA_DIR[/blue] permissions.\n", title="\n\n[red][X] Error while trying to load database![/red]", subtitle="[grey53]NO WRITES CAN BE PERFORMED[/grey53]", expand=False, style="bold red", ), ) STDERR.print() import traceback traceback.print_exc() return from django.conf import settings from archivebox.core.settings_logging import ERROR_LOG as DEFAULT_ERROR_LOG # log startup message to the error log error_log = getattr(settings, "ERROR_LOG", DEFAULT_ERROR_LOG) with open(error_log, "a", encoding="utf-8") as f: command = " ".join(sys.argv) ts = datetime.now(timezone.utc).strftime("%Y-%m-%d__%H:%M:%S") f.write(f"\n> {command}; TS={ts} VERSION={CONSTANTS.VERSION} IN_DOCKER={SHELL_CONFIG.IN_DOCKER} IS_TTY={SHELL_CONFIG.IS_TTY}\n") if check_db: # make sure the data dir is owned by a non-root user if CONSTANTS.DATA_DIR.stat().st_uid == 0: STDERR.print("[red][X] Error: ArchiveBox DATA_DIR cannot be owned by root![/red]") STDERR.print(f" {CONSTANTS.DATA_DIR}") STDERR.print() STDERR.print("[violet]Hint:[/violet] Are you running archivebox in the right folder? (and as a non-root user?)") STDERR.print(" cd path/to/your/archive/data") STDERR.print(" archivebox [command]") STDERR.print() raise SystemExit(9) # Create cache table in DB if needed try: from django.core.cache import cache cache.get("test", None) except django.db.utils.OperationalError: call_command("createcachetable", verbosity=0) # if archivebox gets imported multiple times, we have to close # the sqlite3 whenever we init from scratch to avoid multiple threads # sharing the same connection by accident from django.db import connections for conn in connections.all(): conn.close_if_unusable_or_obsolete() sql_index_path = CONSTANTS.DATABASE_FILE assert os.access(sql_index_path, os.F_OK), ( f"No database file {sql_index_path} found in: {CONSTANTS.DATA_DIR} (Are you in an ArchiveBox collection directory?)" ) # https://docs.pydantic.dev/logfire/integrations/django/ Logfire Debugging # if settings.DEBUG_LOGFIRE: # from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor # SQLite3Instrumentor().instrument() # import logfire # logfire.configure() # logfire.instrument_django(is_sql_commentor_enabled=True) # logfire.info(f'Started ArchiveBox v{CONSTANTS.VERSION}', argv=sys.argv) except KeyboardInterrupt: raise SystemExit(2) DJANGO_SET_UP = True