Refactor database URL handling and ensure SQLite parent directory creation

- Introduced a helper function `_to_sync_database_url` to standardize the conversion of async database URLs to sync URLs in `env.py` and `migrations.py`.
- Added `_ensure_sqlite_parent_dir` function in `db_utils.py` to create the parent directory for SQLite databases if it doesn't exist, ensuring compatibility with Windows paths.
- Updated the `get_database_url_and_connect_args` function to call `_ensure_sqlite_parent_dir` before processing the database URL.
- Simplified the `sync_export_runtime.js` script by removing the build step and directly using committed runtime artifacts, ensuring a smoother export process.
This commit is contained in:
sudipnext 2026-03-16 07:47:43 +05:45
parent 90a54cb791
commit c8faab97d9
5 changed files with 86 additions and 77 deletions

File diff suppressed because one or more lines are too long

View file

@ -34,6 +34,17 @@ if alembic_config.config_file_name is not None:
target_metadata = SQLModel.metadata
def _to_sync_database_url(database_url: str) -> str:
# Preserve slash counts for sqlite URLs so Windows paths stay valid.
if database_url.startswith("sqlite+aiosqlite:///"):
return "sqlite:///" + database_url[len("sqlite+aiosqlite:///") :]
if database_url.startswith("postgresql+asyncpg://"):
return "postgresql://" + database_url[len("postgresql+asyncpg://") :]
if database_url.startswith("mysql+aiomysql://"):
return "mysql://" + database_url[len("mysql+aiomysql://") :]
return database_url
def _get_url() -> str:
"""
Prefer the URL injected by migrations.py via config.set_main_option,
@ -46,12 +57,7 @@ def _get_url() -> str:
from utils.db_utils import get_database_url_and_connect_args
url, _ = get_database_url_and_connect_args()
return (
url
.replace("sqlite+aiosqlite://", "sqlite:///")
.replace("postgresql+asyncpg://", "postgresql://")
.replace("mysql+aiomysql://", "mysql://")
)
return _to_sync_database_url(url)
def run_migrations_offline() -> None:

View file

@ -8,6 +8,17 @@ from utils.db_utils import get_database_url_and_connect_args
from utils.get_env import get_migrate_database_on_startup_env
def _to_sync_database_url(database_url: str) -> str:
# Preserve slash counts for sqlite URLs so Windows paths stay valid.
if database_url.startswith("sqlite+aiosqlite:///"):
return "sqlite:///" + database_url[len("sqlite+aiosqlite:///") :]
if database_url.startswith("postgresql+asyncpg://"):
return "postgresql://" + database_url[len("postgresql+asyncpg://") :]
if database_url.startswith("mysql+aiomysql://"):
return "mysql://" + database_url[len("mysql+aiomysql://") :]
return database_url
async def migrate_database_on_startup() -> None:
if get_migrate_database_on_startup_env() not in ["true", "True"]:
return
@ -29,12 +40,7 @@ def _run_migrations() -> None:
database_url, _ = get_database_url_and_connect_args()
# Alembic uses synchronous engines; strip async driver prefixes.
database_url = (
database_url
.replace("sqlite+aiosqlite://", "sqlite:///")
.replace("postgresql+asyncpg://", "postgresql://")
.replace("mysql+aiomysql://", "mysql://")
)
database_url = _to_sync_database_url(database_url)
config.set_main_option("sqlalchemy.url", database_url)
command.upgrade(config, "head")

View file

@ -4,11 +4,31 @@ from urllib.parse import urlsplit, urlunsplit, parse_qsl
import ssl
def _ensure_sqlite_parent_dir(database_url: str) -> None:
if not database_url.startswith("sqlite://"):
return
split_result = urlsplit(database_url)
db_path = split_result.path
if not db_path:
return
# sqlite URLs on Windows can start with /C:/..., normalize that for os.path.
if os.name == "nt" and len(db_path) >= 3 and db_path[0] == "/" and db_path[2] == ":":
db_path = db_path[1:]
parent = os.path.dirname(db_path)
if parent:
os.makedirs(parent, exist_ok=True)
def get_database_url_and_connect_args() -> tuple[str, dict]:
database_url = get_database_url_env() or "sqlite:///" + os.path.join(
get_app_data_directory_env() or "/tmp/presenton", "fastapi.db"
)
_ensure_sqlite_parent_dir(database_url)
if database_url.startswith("sqlite://"):
database_url = database_url.replace("sqlite://", "sqlite+aiosqlite://", 1)
elif database_url.startswith("postgresql://"):

View file

@ -1,39 +1,16 @@
const fs = require("fs");
const path = require("path");
const { spawnSync } = require("child_process");
const repoRoot = path.resolve(__dirname, "..");
const exportProjectDir = path.join(repoRoot, "presenton-export-opensource");
const sourceIndex = path.join(exportProjectDir, "dist", "index.js");
const sourceConvert = path.join(exportProjectDir, "dist", "py", "convert");
const targetRoot = path.join(__dirname, "resources", "export");
const targetPyDir = path.join(targetRoot, "py");
const targetIndex = path.join(targetRoot, "index.js");
const targetConvert = path.join(targetPyDir, "convert");
function run(command, args, cwd) {
const result = spawnSync(command, args, {
cwd,
stdio: "inherit",
shell: process.platform === "win32",
});
if (result.status !== 0) {
throw new Error(`Command failed: ${command} ${args.join(" ")}`);
}
}
function ensureExists(filePath, label) {
if (!fs.existsSync(filePath)) {
throw new Error(`${label} not found at: ${filePath}`);
}
}
function copyFile(source, target) {
fs.mkdirSync(path.dirname(target), { recursive: true });
fs.copyFileSync(source, target);
}
function chmodIfPossible(filePath) {
if (process.platform !== "win32") {
fs.chmodSync(filePath, 0o755);
@ -41,17 +18,17 @@ function chmodIfPossible(filePath) {
}
function main() {
console.log("[export-runtime] Building export runtime artifacts...");
run("bun", ["run", "build:all"], exportProjectDir);
ensureExists(sourceIndex, "Export runtime JS bundle");
ensureExists(sourceConvert, "Export runtime converter binary");
copyFile(sourceIndex, targetIndex);
copyFile(sourceConvert, targetConvert);
ensureExists(
targetIndex,
"Committed runtime JS bundle (electron/resources/export/index.js)"
);
ensureExists(
targetConvert,
"Committed runtime converter binary (electron/resources/export/py/convert)"
);
chmodIfPossible(targetConvert);
console.log("[export-runtime] Synced files:");
console.log("[export-runtime] Using committed runtime artifacts:");
console.log(` - ${targetIndex}`);
console.log(` - ${targetConvert}`);
}