trigger rebuild after activities upload
This commit is contained in:
+7
-1
@@ -20,9 +20,11 @@ console = Console()
|
||||
@click.option("--strava-client-secret", default=None, envvar="STRAVA_CLIENT_SECRET", help="Strava OAuth client secret")
|
||||
@click.option("--max-users", default=None, type=int, help="Override max users for this instance (0 = unlimited; updates the DB setting)")
|
||||
@click.option("--public-url", default=None, envvar="PUBLIC_URL", help="Public base URL (e.g. https://yourdomain.com). Required for Strava OAuth to work behind a reverse proxy.")
|
||||
@click.option("--webroot", default=None, type=click.Path(), help="Nginx webroot (e.g. /var/www/bincio). When set, uploads trigger a full Astro build + rsync so new activity pages are immediately accessible without a git push.")
|
||||
def serve(data_dir: str, site_dir: Optional[str], host: str, port: int,
|
||||
strava_client_id: Optional[str], strava_client_secret: Optional[str],
|
||||
max_users: Optional[int], public_url: Optional[str]) -> None:
|
||||
max_users: Optional[int], public_url: Optional[str],
|
||||
webroot: Optional[str]) -> None:
|
||||
"""Start the bincio multi-user application server.
|
||||
|
||||
Handles auth, user management, and write operations.
|
||||
@@ -54,6 +56,8 @@ def serve(data_dir: str, site_dir: Optional[str], host: str, port: int,
|
||||
srv.strava_client_secret = strava_client_secret
|
||||
if public_url:
|
||||
srv.public_url = public_url
|
||||
if webroot and site_dir:
|
||||
srv.webroot = Path(webroot).expanduser().resolve()
|
||||
|
||||
db = open_db(dd)
|
||||
current_limit = get_setting(db, "max_users")
|
||||
@@ -63,6 +67,8 @@ def serve(data_dir: str, site_dir: Optional[str], host: str, port: int,
|
||||
console.print(f" Data: [cyan]{dd}[/cyan]")
|
||||
if srv.site_dir:
|
||||
console.print(f" Site: [cyan]{srv.site_dir}[/cyan]")
|
||||
if srv.webroot:
|
||||
console.print(f" Web: [cyan]{srv.webroot}[/cyan] (auto-rebuild on upload)")
|
||||
console.print(f" URL: [cyan]http://{host}:{port}[/cyan]")
|
||||
if current_limit and int(current_limit) > 0:
|
||||
console.print(f" Users: [yellow]max {current_limit}[/yellow]")
|
||||
|
||||
+56
-13
@@ -82,6 +82,7 @@ def _job_finish(job_id: str) -> None:
|
||||
|
||||
data_dir: Path | None = None
|
||||
site_dir: Path | None = None # for post-write rebuild trigger
|
||||
webroot: Path | None = None # nginx webroot — when set, trigger full rebuild + rsync
|
||||
strava_client_id: str = ""
|
||||
strava_client_secret: str = ""
|
||||
public_url: str = "" # e.g. "https://yourdomain.com" — used for OAuth redirect URIs
|
||||
@@ -201,25 +202,67 @@ def _unique_image_name(directory: Path, filename: str) -> str:
|
||||
|
||||
# ── Post-write rebuild ────────────────────────────────────────────────────────
|
||||
|
||||
# Serialises concurrent rebuilds — only one full build runs at a time.
|
||||
# A second upload that arrives while a build is in progress will queue and
|
||||
# run after the first finishes, picking up all data written in between.
|
||||
_rebuild_lock = threading.Lock()
|
||||
|
||||
|
||||
def _trigger_rebuild(handle: str) -> None:
|
||||
"""Asynchronously re-merge one user's shard and rewrite the root manifest."""
|
||||
"""Asynchronously re-merge and optionally rebuild + rsync the site.
|
||||
|
||||
- Without --webroot: fast path — merges sidecars + rewrites root manifest
|
||||
(~1 s). New activity pages require the nginx try_files fallback to work.
|
||||
- With --webroot: full Astro build + rsync to the nginx webroot (~30–60 s,
|
||||
serialised). New activity pages are immediately accessible.
|
||||
"""
|
||||
if site_dir is None:
|
||||
return
|
||||
if not _VALID_HANDLE.match(handle):
|
||||
return # safety: never pass untrusted strings to subprocess
|
||||
|
||||
uv = shutil.which("uv") or str(Path.home() / ".local" / "bin" / "uv")
|
||||
try:
|
||||
subprocess.Popen(
|
||||
[uv, "run", "bincio", "render",
|
||||
"--data-dir", str(data_dir),
|
||||
"--site-dir", str(site_dir),
|
||||
"--handle", handle,
|
||||
"--no-build"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except Exception:
|
||||
pass # rebuild failure must never 500 the calling endpoint
|
||||
_data_dir = str(data_dir)
|
||||
_site_dir = str(site_dir)
|
||||
_webroot = str(webroot) if webroot else None
|
||||
_handle = handle
|
||||
|
||||
def _run() -> None:
|
||||
try:
|
||||
if _webroot is None:
|
||||
# Fast: only update data, skip Astro build
|
||||
subprocess.run(
|
||||
[uv, "run", "bincio", "render",
|
||||
"--data-dir", _data_dir,
|
||||
"--site-dir", _site_dir,
|
||||
"--handle", _handle,
|
||||
"--no-build"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
else:
|
||||
# Full build + rsync — serialised so concurrent uploads don't race
|
||||
with _rebuild_lock:
|
||||
result = subprocess.run(
|
||||
[uv, "run", "bincio", "render",
|
||||
"--data-dir", _data_dir,
|
||||
"--site-dir", _site_dir,
|
||||
"--handle", _handle],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
# Rsync built site to nginx webroot
|
||||
subprocess.run(
|
||||
["rsync", "-a", "--delete",
|
||||
f"{_site_dir}/dist/", _webroot + "/"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
except Exception:
|
||||
pass # rebuild failure must never affect the calling request
|
||||
|
||||
threading.Thread(target=_run, daemon=True).start()
|
||||
|
||||
|
||||
# ── Auth endpoints ────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -113,6 +113,7 @@ WorkingDirectory=/opt/bincio
|
||||
ExecStart=/root/.local/bin/uv run bincio serve \
|
||||
--data-dir /var/bincio/data \
|
||||
--site-dir /opt/bincio/site \
|
||||
--webroot /var/www/bincio \
|
||||
--host 127.0.0.1 \
|
||||
--port 4041 \
|
||||
--public-url https://yourdomain.com
|
||||
|
||||
Reference in New Issue
Block a user