diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index a553458ce4..b2783585de 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -9,6 +9,7 @@ ### Enhancements - **Sanitize post title and author**: The title and author of a post (RSS item or Atom entry) are now sanitized to prevent unexpected formatting issues. In particular, unexpected whitespaces and linebreaks are removed, and any HTML elements are stripped. This helps display them correctly in Telegram messages as well as Telegraph posts. +- **Improve robustness on Railway.app**: Some Railway-specific environment variables are recognized to improve robustness on Railway.app. In particular, `DATABASE_PRIVATE_URL` and `DATABASE_PUBLIC_URL` will be used when `DATABASE_URL` is unavailable or invalid. This should solve most database connection issues on Railway.app. - **Minor refactor**: Some internal functions have been refactored to improve readability and maintainability. ### Bug fixes diff --git a/docs/CHANGELOG.zh.md b/docs/CHANGELOG.zh.md index 2667c5f67f..fc3c50d0d7 100644 --- a/docs/CHANGELOG.zh.md +++ b/docs/CHANGELOG.zh.md @@ -9,6 +9,7 @@ ### 增强 - **净化文章标题和作者**: 文章 (RSS item 或 Atom entry) 的标题和作者现在被净化以防止意外的格式问题。特别是,预期外的空格和换行符被移除,任何 HTML 元素都被剥离。这有助于在 Telegram 消息以及 Telegraph 文章中正确显示它们。 +- **改善在 Railway.app 上的稳健性**: 一些特定于 Railway 的环境变量被识别以提高在 Railway.app 上的稳健性。特别是,当 `DATABASE_URL` 不可用或无效时,将使用 `DATABASE_PRIVATE_URL` 和 `DATABASE_PUBLIC_URL`。这应该解决在 Railway.app 上的大多数数据库连接问题。 - **次要的重构**: 重构了一些内部函数以提高可读性和可维护性。 ### Bug 修复 diff --git a/requirements.txt b/requirements.txt index ecf23d4726..315f363092 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,6 +28,7 @@ python-socks[asyncio]==2.5.0 dnspython[idna]==2.6.1 # utils +yarl==1.9.4 colorlog==6.8.2 APScheduler==3.10.4 python-dotenv==1.0.1 diff --git a/src/env.py b/src/env.py index cce2b9bfd3..cfb5c84942 100644 --- a/src/env.py +++ b/src/env.py @@ -19,6 +19,7 @@ from typing_extensions import Final import asyncio +import yarl import os import sys import colorlog @@ -316,11 +317,41 @@ def __get_version(): ) del _images_weserv_nl + # ----- db config ----- -_database_url = os.environ.get('DATABASE_URL') or f'sqlite://{config_folder_path}/db.sqlite3' -DATABASE_URL: Final = (_database_url.replace('postgresql', 'postgres', 1) if _database_url.startswith('postgresql') - else _database_url) -del _database_url +def __get_database_url() -> str: + # Railway.app provides DATABASE_PRIVATE_URL, DATABASE_URL and/or DATABASE_PUBLIC_URL. + # DATABASE_PRIVATE_URL is for private networking, while DATABASE_PUBLIC_URL is for public networking. + # If private networking is disabled, the former still exists but is invalid (i.e., missing host). + # If public networking is disabled, both host and port are missing in the latter, resulting in an invalid URL too. + # A legacy Postgres addon may provide only DATABASE_URL (for PUBLIC networking) and DATABASE_PRIVATE_URL, + # while a modern one may provide only DATABASE_URL (for PRIVATE networking) and DATABASE_PUBLIC_URL. + # Thus, we need to select a valid one from them. + urls: tuple[str, ...] = tuple(filter(None, map(os.environ.get, ( + 'DATABASE_URL', # Generic, Railway.app compatible + 'DATABASE_PRIVATE_URL', # Railway.app specific + 'DATABASE_PUBLIC_URL', # Railway.app specific + )))) + if not urls: + return f'sqlite://{config_folder_path}/db.sqlite3' + err: Optional[BaseException] = None + for url in urls: + try: + y_url = yarl.URL(url) + except ValueError as _err: + err = _err + else: + return str( + # Tortoise-ORM does not recognize 'postgresql' scheme + y_url.with_scheme('postgres') + if y_url.scheme == 'postgresql' + else y_url + ) + else: + logger.critical('INVALID DATABASE URL!', exc_info=err) + + +DATABASE_URL: Final = __get_database_url() # ----- misc config ----- TABLE_TO_IMAGE: Final = __bool_parser(os.environ.get('TABLE_TO_IMAGE'))