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:
parent
90a54cb791
commit
c8faab97d9
5 changed files with 86 additions and 77 deletions
File diff suppressed because one or more lines are too long
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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://"):
|
||||
|
|
|
|||
|
|
@ -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}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue