From 1734b3f3713d87c0aab9585d4525ed21b0f2f56a Mon Sep 17 00:00:00 2001
From: DeepZone
Date: Fri, 22 May 2026 14:01:08 +0100
Subject: [PATCH] v1.0.1: UI polish and release version updates
---
README.md | 4 ++--
RELEASE_NOTES.md | 8 ++++++++
ROADMAP.md | 2 +-
backend/app/api/routes_system.py | 2 +-
backend/app/core/system_status.py | 2 +-
backend/app/main.py | 2 +-
backend/pyproject.toml | 2 +-
backend/tests/test_api_smoke.py | 2 +-
frontend/package.json | 2 +-
frontend/src/App.tsx | 8 ++++----
frontend/src/components/AuditLogView.tsx | 10 ++++++----
frontend/src/components/Layout.tsx | 15 ++++++---------
frontend/src/components/UsersView.tsx | 5 +++--
13 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/README.md b/README.md
index e8540fc..d67bda0 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+
@@ -61,7 +61,7 @@ Using read-only checks before execution helps teams validate assumptions without
## Current Release
-RouteForge is a **stable** self-hosted release for production-grade read-only validation workflows. Current release: **v1.0.0**.
+RouteForge is a **stable** self-hosted release for production-grade read-only validation workflows. Current release: **v1.0.1**.
## Quickstart with Docker Compose
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 16bdc0a..0df4769 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,3 +1,11 @@
+## v1.0.1 (2026-05-22)
+
+- Polished the global layout and removed redundant runtime badges from the live header.
+- Improved sidebar branding with a cleaner logo presentation.
+- Improved User Management readability with formatted role labels and status badges.
+- Improved Audit Log readability with cleaner filters, compact details and a more structured table.
+- Kept the release read-only and without database migrations.
+
## v1.0.0 (2026-05-22)
### Summary
diff --git a/ROADMAP.md b/ROADMAP.md
index c3fc156..e6bf9f3 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -1,7 +1,7 @@
# RouteForge Roadmap
## Current Status
-RouteForge is now **v1.0.0 stable**: a selfhosted, read-only routing preflight release for production-oriented validation workflows.
+RouteForge is now **v1.0.1 stable**: a selfhosted, read-only routing preflight release for production-oriented validation workflows.
## v1.0.0 Completed Scope
The stable release includes:
diff --git a/backend/app/api/routes_system.py b/backend/app/api/routes_system.py
index 4bb2e66..6b2928c 100644
--- a/backend/app/api/routes_system.py
+++ b/backend/app/api/routes_system.py
@@ -12,7 +12,7 @@
def system_info():
return {
'name': 'RouteForge',
- 'version': 'v1.0.0',
+ 'version': 'v1.0.1',
'demo_mode': settings.demo_mode,
'read_only': True,
'data_sources': ['RIPEstat', 'RIPEstat Whois/Registry'],
diff --git a/backend/app/core/system_status.py b/backend/app/core/system_status.py
index 0e02aa4..cf1f5f8 100644
--- a/backend/app/core/system_status.py
+++ b/backend/app/core/system_status.py
@@ -153,7 +153,7 @@ def build_system_status(engine: Engine | None) -> dict:
return {
"status": "ok",
"name": settings.app_name,
- "version": "v1.0.0",
+ "version": "v1.0.1",
"read_only": True,
"mode": "demo" if settings.demo_mode else "live",
"demo_mode": settings.demo_mode,
diff --git a/backend/app/main.py b/backend/app/main.py
index 1578091..8cf567a 100644
--- a/backend/app/main.py
+++ b/backend/app/main.py
@@ -20,7 +20,7 @@
logging.basicConfig(level=getattr(logging, settings.log_level.upper(), logging.INFO))
logger = logging.getLogger("routeforge")
-app = FastAPI(title="RouteForge", version="1.0.0")
+app = FastAPI(title="RouteForge", version="1.0.1")
app.add_middleware(
CORSMiddleware,
diff --git a/backend/pyproject.toml b/backend/pyproject.toml
index 2e72624..4a1053b 100644
--- a/backend/pyproject.toml
+++ b/backend/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "routeforge-backend"
-version = "1.0.0"
+version = "1.0.1"
description = "RouteForge backend"
license = "AGPL-3.0-or-later"
requires-python = ">=3.12"
diff --git a/backend/tests/test_api_smoke.py b/backend/tests/test_api_smoke.py
index f5bb036..ed99135 100644
--- a/backend/tests/test_api_smoke.py
+++ b/backend/tests/test_api_smoke.py
@@ -177,7 +177,7 @@ def test_system_status_endpoint() -> None:
response = client.get('/api/system/status')
assert response.status_code == 200
payload = response.json()
- assert payload.get('version') == 'v1.0.0'
+ assert payload.get('version') == 'v1.0.1'
assert payload.get('read_only') is True
assert payload.get('database', {}).get('status')
assert payload.get('ripestat', {}).get('cache_ttl_seconds') is not None
diff --git a/frontend/package.json b/frontend/package.json
index 1c74cb2..c142456 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "routeforge-frontend",
- "version": "1.0.0",
+ "version": "1.0.1",
"private": true,
"license": "AGPL-3.0-or-later",
"type": "module",
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index e49900a..9ced8d1 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -74,7 +74,7 @@ export default function App() {
if (authMode === 'login') return
if (authMode === 'error') return
{authError}
- const systemLine = system ? `${system.name} · ${system.version}` : 'RouteForge · v1.0.0'
+ const systemLine = system ? `${system.name} · ${system.version}` : 'RouteForge · v1.0.1'
const title = { dashboard: 'Dashboard', asn: 'ASN Check', prefix: 'Prefix Check', preflight: 'Preflight', 'roa-planner': 'ROA Planner', 'bgp-visibility': 'BGP Visibility', reports: 'Reports', 'watch-mode': 'Watch Mode', 'change-cases': 'Change Cases', system: 'System', users: 'Users', audit: 'Audit Log', about: 'About' }[active]
const proxyStatus = systemStatusError ? 'ERROR' : 'OK'
const migrationStatus = systemStatus?.database?.migration_status || 'unknown'
@@ -88,8 +88,8 @@ export default function App() {
}
const readOnlyLabel = system?.read_only ? 'Enabled' : 'Disabled'
- return
- {active === 'dashboard' && {system?.name || 'RouteForge'} {system?.version || 'v1.0.0'}
Current user: {currentUser?.username || '-'}
Role: {currentUser?.role || '-'}
Read-only: {readOnlyLabel}
Mode: {system?.demo_mode ? 'DEMO' : 'LIVE'}
{system?.demo_mode && Demo mode is active.
}Quick Actions
{role === 'admin' && }
{migrationsBlocked && Database migrations are required before using RouteForge. Run: alembic current, alembic heads, alembic upgrade head.}}
+ return
+ {active === 'dashboard' && {system?.name || 'RouteForge'} {system?.version || 'v1.0.1'}
Current user: {currentUser?.username || '-'}
Role: {currentUser?.role || '-'}
Read-only: {readOnlyLabel}
Mode: {system?.demo_mode ? 'DEMO' : 'LIVE'}
{system?.demo_mode && Demo mode is active.
}Quick Actions
{role === 'admin' && }
{migrationsBlocked && Database migrations are required before using RouteForge. Run: alembic current, alembic heads, alembic upgrade head.}}
{!canAccess(active) && You do not have permission to access this section.}
{active === 'asn' && canAccess('asn') && }
{active === 'prefix' && canAccess('prefix') && }
@@ -102,6 +102,6 @@ export default function App() {
{active === 'system' && canAccess('system') && {systemStatusError && {systemStatusError}}{migrationsBlocked && Database migrations are required before using RouteForge. Run: alembic current, alembic heads, alembic upgrade head.}{systemStatus && Version: {systemStatus.version}
Mode: {systemStatus.mode}
API Proxy: {proxyStatus}
Migration Status: {migrationStatus}
DB Current Revision: {systemStatus.database?.schema_version || 'unknown'}
DB Head Revision: {systemStatus.database?.migration_head || 'unknown'}
}}
{active === 'users' && canAccess('users') && }
{active === 'audit' && canAccess('audit') && }
- {active === 'about' && RouteForge is a read-only routing operations console for validation workflows.
Read-only: All checks are non-destructive.
Version: {system?.version || 'v1.0.0'}
Build and maintained by Norman Sens.
}
+ {active === 'about' && RouteForge is a read-only routing operations console for validation workflows.
Read-only: All checks are non-destructive.
Version: {system?.version || 'v1.0.1'}
Build and maintained by Norman Sens.
}
}
diff --git a/frontend/src/components/AuditLogView.tsx b/frontend/src/components/AuditLogView.tsx
index f74d79d..32f56c2 100644
--- a/frontend/src/components/AuditLogView.tsx
+++ b/frontend/src/components/AuditLogView.tsx
@@ -8,6 +8,7 @@ export function AuditLogView() {
const [error, setError] = useState('')
const [action, setAction] = useState('')
const [targetType, setTargetType] = useState('')
+ const hasFilters = action.trim().length > 0 || targetType.trim().length > 0
useEffect(() => {
const run = async () => {
@@ -24,13 +25,14 @@ export function AuditLogView() {
return
Audit Log
-
-
setAction(e.target.value)} />
-
setTargetType(e.target.value)} />
+
+ setAction(e.target.value)} />
+ setTargetType(e.target.value)} />
+
{loading &&
Loading audit log…
}
{error &&
{error}
}
{!loading && !error && items.length === 0 &&
No audit entries found.
}
- {!loading && !error && items.length > 0 &&
| Time | User | Action | Target | IP | User Agent | Details |
{items.map((a)=>| {a.created_at} | {a.username || a.user_id || '-'} | {a.action} | {a.target_type || '-'}:{a.target_id || '-'} | {a.ip_address || '-'} | {a.user_agent || '-'} | {JSON.stringify(a.details_json || {}, null, 2)} |
)}
}
+ {!loading && !error && items.length > 0 &&
| Time | User | Action | Target | IP | Details |
{items.map((a) => { const target = a.target_type && a.target_id ? `${a.target_type} · ${a.target_id}` : a.target_type || a.target_id || '-'; const detailsPayload = { ...(a.details_json || {}), ...(a.user_agent ? { user_agent: a.user_agent } : {}) }; return | {a.created_at} | {a.username || a.user_id || '-'} | {a.action} | {target} | {a.ip_address || '-'} | View{JSON.stringify(detailsPayload, null, 2)} |
})}
}
}
diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx
index fb83c0b..1aedbcf 100644
--- a/frontend/src/components/Layout.tsx
+++ b/frontend/src/components/Layout.tsx
@@ -19,7 +19,7 @@ const nav: { key: NavKey; label: string; desc: string }[] = [
{ key: 'about', label: 'About', desc: 'Data sources and limits' },
]
-export function Layout({ children, active, onNav, systemLine, title, demoMode, currentUser, onLogout, appVersion }: { children: ReactNode; active: NavKey; onNav: (key: NavKey) => void; systemLine: string; title: string; demoMode: boolean; currentUser?: { username: string; role: UserRole } | null; onLogout: () => void; appVersion?: string }) {
+export function Layout({ children, active, onNav, systemLine, title, demoMode, currentUser, onLogout }: { children: ReactNode; active: NavKey; onNav: (key: NavKey) => void; systemLine: string; title: string; demoMode: boolean; currentUser?: { username: string; role: UserRole } | null; onLogout: () => void }) {
const visibleNav = nav.filter((item) => {
if (!currentUser) return ['dashboard', 'about'].includes(item.key)
if (currentUser.role === 'admin') return true
@@ -29,12 +29,11 @@ export function Layout({ children, active, onNav, systemLine, title, demoMode, c
return