Record / Convert tabs — now gated behind PUBLIC_MOBILE_APP=true. Hidden by default in VPS/dev mode; only show when explicitly opted into the mobile app build.

Edit/Upload UI — split into two concepts:
  - PUBLIC_EDIT_URL — the server base URL (empty = use proxy at /api/)
  - PUBLIC_EDIT_ENABLED=true — whether to show the edit/upload buttons at all

  bincio dev now sets PUBLIC_EDIT_ENABLED=true when instance.db exists (multi-user mode), so the upload button, edit button, and edit drawer all appear. The fetch calls already produce  correct relative URLs (${''}/api/upload = /api/upload) which the Vite proxy forwards to bincio serve.
This commit is contained in:
Davide Scaini
2026-04-09 13:16:00 +02:00
parent 2501c9e0f6
commit fb202b4edf
4 changed files with 22 additions and 10 deletions
+4 -2
View File
@@ -148,8 +148,10 @@ def dev(
env = { env = {
**os.environ, **os.environ,
"BINCIO_DATA_DIR": str(data), "BINCIO_DATA_DIR": str(data),
"PUBLIC_EDIT_URL": "", # empty = proxy /api/* to bincio serve "PUBLIC_EDIT_URL": "", # empty = proxy /api/* to bincio serve
"VITE_API_PORT": str(api_port), # picked up by astro.config.mjs if needed "PUBLIC_EDIT_ENABLED": "true" if has_auth else "", # show edit/upload UI in VPS mode
"PUBLIC_MOBILE_APP": "", # Record/Convert tabs off by default
"VITE_API_PORT": str(api_port), # picked up by astro.config.mjs
} }
# Start astro dev in foreground (Ctrl+C stops everything) # Start astro dev in foreground (Ctrl+C stops everything)
+4 -3
View File
@@ -13,7 +13,8 @@
export let base: string = '/'; export let base: string = '/';
export let athlete: AthleteZones | null = null; export let athlete: AthleteZones | null = null;
const editUrl = import.meta.env.PUBLIC_EDIT_URL; const editUrl = import.meta.env.PUBLIC_EDIT_URL ?? '';
const editEnabled = editUrl !== '' || import.meta.env.PUBLIC_EDIT_ENABLED === 'true';
let detail: ActivityDetail | null = null; let detail: ActivityDetail | null = null;
let error = ''; let error = '';
@@ -89,7 +90,7 @@
<svelte:window on:keydown={onKeydown} /> <svelte:window on:keydown={onKeydown} />
{#if editOpen && editUrl} {#if editOpen && editEnabled}
<EditDrawer activityId={activity.id} {editUrl} on:saved={onSaved} on:close={() => editOpen = false} /> <EditDrawer activityId={activity.id} {editUrl} on:saved={onSaved} on:close={() => editOpen = false} />
{/if} {/if}
@@ -176,7 +177,7 @@
</div> </div>
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<h1 class="text-2xl font-bold text-white">{displayTitle}</h1> <h1 class="text-2xl font-bold text-white">{displayTitle}</h1>
{#if editUrl} {#if editEnabled}
<button <button
class="text-xs px-2 py-0.5 rounded border border-zinc-700 text-zinc-400 hover:border-zinc-500 hover:text-white transition-colors shrink-0" class="text-xs px-2 py-0.5 rounded border border-zinc-700 text-zinc-400 hover:border-zinc-500 hover:text-white transition-colors shrink-0"
on:click={() => editOpen = true} on:click={() => editOpen = true}
+1
View File
@@ -36,6 +36,7 @@
let uploading = false; let uploading = false;
let fileInput: HTMLInputElement; let fileInput: HTMLInputElement;
// editUrl is empty in multi-user VPS mode — the Vite proxy forwards /api/* to bincio serve.
const api = `${editUrl}/api/activity/${activityId}`; const api = `${editUrl}/api/activity/${activityId}`;
async function load() { async function load() {
+13 -5
View File
@@ -10,6 +10,10 @@ interface Props {
} }
const { title = 'BincioActivity', description = 'Your personal activity stats', public: isPublicPage = false } = Astro.props; const { title = 'BincioActivity', description = 'Your personal activity stats', public: isPublicPage = false } = Astro.props;
const editUrl = import.meta.env.PUBLIC_EDIT_URL ?? ''; const editUrl = import.meta.env.PUBLIC_EDIT_URL ?? '';
// Edit UI is enabled when PUBLIC_EDIT_URL is set (single-user bincio-edit mode)
// OR when PUBLIC_EDIT_ENABLED=true (multi-user VPS mode — API proxied at /api/).
const editEnabled = editUrl !== '' || import.meta.env.PUBLIC_EDIT_ENABLED === 'true';
const mobileApp = import.meta.env.PUBLIC_MOBILE_APP === 'true';
const baseUrl = import.meta.env.BASE_URL ?? '/'; const baseUrl = import.meta.env.BASE_URL ?? '/';
// Read root index.json at build time to detect instance configuration. // Read root index.json at build time to detect instance configuration.
@@ -163,8 +167,12 @@ try {
<!-- Per-user nav links — updated by user-widget script in multi-user mode --> <!-- Per-user nav links — updated by user-widget script in multi-user mode -->
<a id="nav-stats" href={singleHandle ? `${baseUrl}u/${singleHandle}/stats/` : `${baseUrl}stats/`} data-user-path="stats/" class="text-sm text-zinc-400 hover:text-white transition-colors">Stats</a> <a id="nav-stats" href={singleHandle ? `${baseUrl}u/${singleHandle}/stats/` : `${baseUrl}stats/`} data-user-path="stats/" class="text-sm text-zinc-400 hover:text-white transition-colors">Stats</a>
<a id="nav-athlete" href={singleHandle ? `${baseUrl}u/${singleHandle}/athlete/` : `${baseUrl}athlete/`} data-user-path="athlete/" class="text-sm text-zinc-400 hover:text-white transition-colors">Athlete</a> <a id="nav-athlete" href={singleHandle ? `${baseUrl}u/${singleHandle}/athlete/` : `${baseUrl}athlete/`} data-user-path="athlete/" class="text-sm text-zinc-400 hover:text-white transition-colors">Athlete</a>
<a id="nav-record" href={singleHandle ? `${baseUrl}u/${singleHandle}/record/` : `${baseUrl}record/`} data-user-path="record/" class="text-sm text-zinc-400 hover:text-white transition-colors">Record</a> {mobileApp && (
<a href={`${baseUrl}convert/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Convert</a> <a id="nav-record" href={singleHandle ? `${baseUrl}u/${singleHandle}/record/` : `${baseUrl}record/`} data-user-path="record/" class="text-sm text-zinc-400 hover:text-white transition-colors">Record</a>
)}
{mobileApp && (
<a href={`${baseUrl}convert/`} class="text-sm text-zinc-400 hover:text-white transition-colors">Convert</a>
)}
<div class="ml-auto flex items-center gap-1"> <div class="ml-auto flex items-center gap-1">
<!-- Logout button — hidden until logged in --> <!-- Logout button — hidden until logged in -->
@@ -174,7 +182,7 @@ try {
class="text-xs text-zinc-500 hover:text-white transition-colors px-2 h-8" class="text-xs text-zinc-500 hover:text-white transition-colors px-2 h-8"
aria-label="Log out" aria-label="Log out"
>Log out</button> >Log out</button>
{editUrl && ( {editEnabled && (
<button <button
id="upload-btn" id="upload-btn"
class="text-zinc-400 hover:text-white transition-colors w-8 h-8 flex items-center justify-center rounded-md hover:bg-zinc-800 text-base" class="text-zinc-400 hover:text-white transition-colors w-8 h-8 flex items-center justify-center rounded-md hover:bg-zinc-800 text-base"
@@ -191,7 +199,7 @@ try {
</div> </div>
</nav> </nav>
{editUrl && ( {editEnabled && (
<!-- Add activity modal --> <!-- Add activity modal -->
<div <div
id="upload-modal" id="upload-modal"
@@ -360,7 +368,7 @@ try {
</script> </script>
)} )}
{editUrl && ( {editEnabled && (
<script define:vars={{ editUrl, baseUrl }}> <script define:vars={{ editUrl, baseUrl }}>
const modal = document.getElementById('upload-modal'); const modal = document.getElementById('upload-modal');
const openBtn = document.getElementById('upload-btn'); const openBtn = document.getElementById('upload-btn');