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-draft.md
# Data directory (postgres, redis persistence)
data/
# Accidental literal "~" directories (e.g. when a configured path contains "~" but isn't expanded)
backend/~/
backend/coverage.*

View File

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

View File

@ -253,8 +253,10 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
}
const tok = usage.tokens;
const inputTok = usage.input_tokens;
const req = usage.requests;
const isOllama = provider === "ollama";
const modelCount = usage.models?.length ?? 0;
return (
<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 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 */}
{tok.limit != null ? (
<div>
@ -300,6 +311,33 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div>
) : 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 */}
{req.limit != null ? (
<div className="flex items-center justify-between text-[11px] text-muted">
@ -313,9 +351,9 @@ function UsageStrip({ credentialId, provider }: { credentialId: string; provider
</div>
) : null}
{tok.limit == null && req.limit == null && (
{tok.limit == null && inputTok.limit == null && req.limit == null && (
<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>
)}

View File

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