fix(infra): bind-mount postgres to ./data/postgres, gitignore data/

- Switched from Docker named volume to bind mount ./data/postgres
- DB data persists on disk across rebuilds (no more re-inputting keys)
- Added data/ to .gitignore
- Updated docker-test.sh docs to reflect bind mount
- Included uncommitted ai-providers page changes
This commit is contained in:
null 2026-05-20 22:57:31 -05:00
parent 328d71b0f8
commit 07b47ace8f
4 changed files with 48 additions and 7 deletions

3
.gitignore vendored
View File

@ -22,6 +22,9 @@ node_modules/
issue-drafts/ issue-drafts/
*.issue-draft.md *.issue-draft.md
# Data directory (postgres, redis persistence)
data/
# Accidental literal "~" directories (e.g. when a configured path contains "~" but isn't expanded) # Accidental literal "~" directories (e.g. when a configured path contains "~" but isn't expanded)
backend/~/ backend/~/
backend/coverage.* backend/coverage.*

View File

@ -8,7 +8,7 @@ services:
POSTGRES_USER: ${POSTGRES_USER:-postgres} POSTGRES_USER: ${POSTGRES_USER:-postgres}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres}
volumes: volumes:
- postgres_data:/var/lib/postgresql/data - ./data/postgres:/var/lib/postgresql/data
ports: ports:
- "127.0.0.1:${POSTGRES_PORT:-5432}:5432" - "127.0.0.1:${POSTGRES_PORT:-5432}:5432"
healthcheck: healthcheck:
@ -99,5 +99,4 @@ services:
FORGEJO_SYNC_STARTUP_DELAY_SECONDS: ${FORGEJO_SYNC_STARTUP_DELAY_SECONDS:-60} FORGEJO_SYNC_STARTUP_DELAY_SECONDS: ${FORGEJO_SYNC_STARTUP_DELAY_SECONDS:-60}
restart: unless-stopped restart: unless-stopped
volumes:
postgres_data:

View File

@ -253,8 +253,10 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
} }
const tok = usage.tokens; const tok = usage.tokens;
const inputTok = usage.input_tokens;
const req = usage.requests; const req = usage.requests;
const isOllama = provider === "ollama"; const isOllama = provider === "ollama";
const modelCount = usage.models?.length ?? 0;
return ( return (
<div className="mt-2 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] p-2.5"> <div className="mt-2 rounded-lg border border-[color:var(--border)] bg-[color:var(--surface)] p-2.5">
@ -273,6 +275,15 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div> </div>
) : ( ) : (
<div className="space-y-1.5"> <div className="space-y-1.5">
{modelCount > 0 && (
<div className="flex items-center justify-between text-[11px] text-muted">
<span>Models</span>
<span className="tabular-nums text-strong">
{modelCount} available
</span>
</div>
)}
{/* Tokens */} {/* Tokens */}
{tok.limit != null ? ( {tok.limit != null ? (
<div> <div>
@ -300,6 +311,33 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div> </div>
) : null} ) : null}
{/* Anthropic input tokens */}
{inputTok.limit != null ? (
<div>
<div className="mb-1 flex items-center justify-between text-[11px]">
<span className="font-medium text-muted">Input tokens</span>
<span className="tabular-nums text-strong">
{fmtTokens(inputTok.remaining)} / {fmtTokens(inputTok.limit)} remaining
{inputTok.reset_in_ms != null && (
<span className="ml-2 text-muted">· resets in {fmtResetMs(inputTok.reset_in_ms)}</span>
)}
</span>
</div>
{inputTok.pct_used != null && (
<div className="h-1.5 overflow-hidden rounded-full bg-[color:var(--surface-strong)]">
<div
className={`h-full rounded-full transition-all ${
inputTok.pct_used > 90 ? "bg-[color:var(--danger)]" :
inputTok.pct_used > 75 ? "bg-[color:var(--warning)]" :
"bg-[color:var(--success)]"
}`}
style={{ width: `${Math.min(100, inputTok.pct_used)}%` }}
/>
</div>
)}
</div>
) : null}
{/* Requests */} {/* Requests */}
{req.limit != null ? ( {req.limit != null ? (
<div className="flex items-center justify-between text-[11px] text-muted"> <div className="flex items-center justify-between text-[11px] text-muted">
@ -313,9 +351,9 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div> </div>
) : null} ) : null}
{tok.limit == null && req.limit == null && ( {tok.limit == null && inputTok.limit == null && req.limit == null && (
<p className="text-[11px] text-muted"> <p className="text-[11px] text-muted">
Connected no rate limit headers returned by this key tier. Connected provider did not return token/request limit headers for this key tier.
</p> </p>
)} )}

View File

@ -10,8 +10,9 @@
# `docker image prune`, `docker volume prune`). Those commands operate on # `docker image prune`, `docker volume prune`). Those commands operate on
# ALL Docker resources system-wide and will destroy other running services. # ALL Docker resources system-wide and will destroy other running services.
# #
# DATA PERSISTENCE: The postgres database volume is preserved across rebuilds. # DATA PERSISTENCE: The postgres database lives in ./data/postgres (bind mount).
# Only app containers/images are cycled. DB data (gateways, keys, etc.) survives. # It is NOT a Docker volume — it survives rebuilds automatically.
# DB data (gateways, keys, etc.) persists across docker-test.sh runs.
set -euo pipefail set -euo pipefail
cd "$(git rev-parse --show-toplevel)" cd "$(git rev-parse --show-toplevel)"