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("--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("--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("--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,
|
def serve(data_dir: str, site_dir: Optional[str], host: str, port: int,
|
||||||
strava_client_id: Optional[str], strava_client_secret: Optional[str],
|
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.
|
"""Start the bincio multi-user application server.
|
||||||
|
|
||||||
Handles auth, user management, and write operations.
|
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
|
srv.strava_client_secret = strava_client_secret
|
||||||
if public_url:
|
if public_url:
|
||||||
srv.public_url = public_url
|
srv.public_url = public_url
|
||||||
|
if webroot and site_dir:
|
||||||
|
srv.webroot = Path(webroot).expanduser().resolve()
|
||||||
|
|
||||||
db = open_db(dd)
|
db = open_db(dd)
|
||||||
current_limit = get_setting(db, "max_users")
|
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]")
|
console.print(f" Data: [cyan]{dd}[/cyan]")
|
||||||
if srv.site_dir:
|
if srv.site_dir:
|
||||||
console.print(f" Site: [cyan]{srv.site_dir}[/cyan]")
|
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]")
|
console.print(f" URL: [cyan]http://{host}:{port}[/cyan]")
|
||||||
if current_limit and int(current_limit) > 0:
|
if current_limit and int(current_limit) > 0:
|
||||||
console.print(f" Users: [yellow]max {current_limit}[/yellow]")
|
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
|
data_dir: Path | None = None
|
||||||
site_dir: Path | None = None # for post-write rebuild trigger
|
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_id: str = ""
|
||||||
strava_client_secret: str = ""
|
strava_client_secret: str = ""
|
||||||
public_url: str = "" # e.g. "https://yourdomain.com" — used for OAuth redirect URIs
|
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 ────────────────────────────────────────────────────────
|
# ── 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:
|
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:
|
if site_dir is None:
|
||||||
return
|
return
|
||||||
if not _VALID_HANDLE.match(handle):
|
if not _VALID_HANDLE.match(handle):
|
||||||
return # safety: never pass untrusted strings to subprocess
|
return # safety: never pass untrusted strings to subprocess
|
||||||
|
|
||||||
uv = shutil.which("uv") or str(Path.home() / ".local" / "bin" / "uv")
|
uv = shutil.which("uv") or str(Path.home() / ".local" / "bin" / "uv")
|
||||||
try:
|
_data_dir = str(data_dir)
|
||||||
subprocess.Popen(
|
_site_dir = str(site_dir)
|
||||||
[uv, "run", "bincio", "render",
|
_webroot = str(webroot) if webroot else None
|
||||||
"--data-dir", str(data_dir),
|
_handle = handle
|
||||||
"--site-dir", str(site_dir),
|
|
||||||
"--handle", handle,
|
def _run() -> None:
|
||||||
"--no-build"],
|
try:
|
||||||
stdout=subprocess.DEVNULL,
|
if _webroot is None:
|
||||||
stderr=subprocess.DEVNULL,
|
# Fast: only update data, skip Astro build
|
||||||
)
|
subprocess.run(
|
||||||
except Exception:
|
[uv, "run", "bincio", "render",
|
||||||
pass # rebuild failure must never 500 the calling endpoint
|
"--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 ────────────────────────────────────────────────────────────
|
# ── Auth endpoints ────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -113,6 +113,7 @@ WorkingDirectory=/opt/bincio
|
|||||||
ExecStart=/root/.local/bin/uv run bincio serve \
|
ExecStart=/root/.local/bin/uv run bincio serve \
|
||||||
--data-dir /var/bincio/data \
|
--data-dir /var/bincio/data \
|
||||||
--site-dir /opt/bincio/site \
|
--site-dir /opt/bincio/site \
|
||||||
|
--webroot /var/www/bincio \
|
||||||
--host 127.0.0.1 \
|
--host 127.0.0.1 \
|
||||||
--port 4041 \
|
--port 4041 \
|
||||||
--public-url https://yourdomain.com
|
--public-url https://yourdomain.com
|
||||||
|
|||||||
Reference in New Issue
Block a user