Skip to content

Commit 64bf8ae

Browse files
authored
Merge pull request #214 from Routstr/v0.2.0-dev
v0.2.0
2 parents af6ecbd + 3a69491 commit 64bf8ae

File tree

180 files changed

+43809
-2747
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

180 files changed

+43809
-2747
lines changed

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,7 @@ UPSTREAM_API_KEY=your-upstream-api-key
3737
# BASE_URL=https://openrouter.ai/api/v1
3838
# MODELS_PATH=models.json
3939
# SOURCE=
40+
41+
# UI Configuration (for Next.js frontend)
42+
# These variables are prefixed with NEXT_PUBLIC_ to be accessible in the browser
43+
# NEXT_PUBLIC_API_URL=http://127.0.0.1:8000

.github/workflows/test.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
branches: ["*"] # Run on PRs to all branches
88

99
jobs:
10-
test:
10+
backend-test:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
@@ -51,3 +51,29 @@ jobs:
5151
pytest.xml
5252
.coverage
5353
retention-days: 30
54+
55+
ui-build:
56+
runs-on: ubuntu-latest
57+
58+
steps:
59+
- name: Checkout code
60+
uses: actions/checkout@v4
61+
62+
- name: Setup Node.js
63+
uses: actions/setup-node@v4
64+
with:
65+
node-version: '18'
66+
cache: 'npm'
67+
cache-dependency-path: ui/package-lock.json
68+
69+
- name: Install UI dependencies
70+
working-directory: ./ui
71+
run: npm ci
72+
73+
- name: Run UI linting
74+
working-directory: ./ui
75+
run: npm run lint
76+
77+
- name: Run UI build
78+
working-directory: ./ui
79+
run: npm run build

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ wallet.sqlite3
88
build/
99
dist/
1010
*.egg
11+
.mypy_cache/**
1112

1213
# Development
1314
.notes
@@ -35,3 +36,5 @@ logs/*
3536
# deployment
3637
proof_backups
3738

39+
*.todo
40+
ui_out

Makefile

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ else
1616
ALEMBIC := alembic
1717
endif
1818

19-
.PHONY: help setup test test-unit test-integration test-integration-docker test-all test-fast test-performance clean docker-up docker-down lint format type-check dev-setup check-deps db-upgrade db-downgrade db-current db-history db-migrate db-revision db-heads db-clean
19+
.PHONY: help setup test test-unit test-integration test-integration-docker test-all test-fast test-performance clean docker-up docker-down lint format type-check dev-setup check-deps db-upgrade db-downgrade db-current db-history db-migrate db-revision db-heads db-clean ui-build ui-build-docker ui-dev
2020

2121
# Default target
2222
help:
@@ -38,6 +38,12 @@ help:
3838
@echo " make check-deps - Check system dependencies"
3939
@echo " make setup - First-time project setup"
4040
@echo ""
41+
@echo "UI targets:"
42+
@echo " make ui-build - Build UI for production (static export)"
43+
@echo " make ui-build-docker - Build UI using Docker (no Node.js needed)"
44+
@echo " make ui-dev - Start UI development server"
45+
@echo ""
46+
@echo "Docker UI build requires only Docker, no local Node.js installation needed."
4147
@echo "Database migration shortcuts:"
4248
@echo " make create-migration - Auto-generate new migration"
4349
@echo " make db-upgrade - Apply all pending migrations"
@@ -261,3 +267,19 @@ docs-deploy:
261267
docs-install:
262268
@echo "📚 Installing documentation dependencies..."
263269
pip install -r docs/requirements.txt
270+
271+
# UI build
272+
ui-build:
273+
@echo "🎨 Building UI for static deployment..."
274+
./scripts/build-ui.sh
275+
276+
ui-build-docker:
277+
@echo "🐳 Building UI using Docker (no Node.js installation required)..."
278+
@echo "Building UI with environment variables from .env..."
279+
docker build -f ui/Dockerfile.build -t routstr-ui-build --build-arg NEXT_PUBLIC_API_URL=$(NEXT_PUBLIC_API_URL) --build-arg NEXT_PUBLIC_ADMIN_API_KEY=$(NEXT_PUBLIC_ADMIN_API_KEY) .
280+
docker run --rm -v $(PWD)/ui_out:/output routstr-ui-build cp -r /ui_out /output/
281+
@echo "✅ UI build complete! Static files available in ui_out/"
282+
283+
ui-dev:
284+
@echo "🎨 Starting UI development server..."
285+
cd ui && (command -v pnpm >/dev/null 2>&1 && pnpm run dev || npm run dev)

README.md

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,41 @@ make db-migrate
143143
make db-upgrade
144144
```
145145

146+
## Admin UI
147+
148+
Routstr includes a modern Next.js admin dashboard that's served directly from the Python backend as static files - no separate Node.js server required.
149+
150+
### Building the UI
151+
152+
```bash
153+
make ui-build
154+
```
155+
156+
This compiles the Next.js application into static HTML, CSS, and JavaScript files in `ui/out/`.
157+
158+
### Accessing the Dashboard
159+
160+
Once built, the UI is automatically served by the FastAPI backend:
161+
162+
- **Dashboard**: `http://localhost:8000/`
163+
- **Login**: `http://localhost:8000/login`
164+
- **Models Management**: `http://localhost:8000/model
165+
- **Providers Management**: `http://localhost:8000/providers`
166+
- **Settings**: `http://localhost:8000/settings`
167+
168+
The dashboard provides:
169+
170+
- Real-time wallet balance monitoring
171+
- Model pricing configuration
172+
- Upstream provider management
173+
- Transaction history
174+
- System settings
175+
176+
**Authentication**: Use the `ADMIN_PASSWORD` environment variable to access the dashboard.
177+
146178
## Withdrawing Balance
147179

148-
Go to `https://<your.routstr.proxy>/admin/` (NOTE: be sure to add the '/' at the end), enter the `ADMIN_PASSWORD` you set above and withdraw your balance as a Cashu token.
180+
Go to the admin dashboard at `http://localhost:8000/` and login with your `ADMIN_PASSWORD` to withdraw your balance as a Cashu token.
149181

150182
## Example Client
151183

compose.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,27 @@
11
services:
2+
ui:
3+
env_file:
4+
- .env
5+
build:
6+
context: ./ui
7+
dockerfile: Dockerfile.build
8+
args:
9+
NEXT_PUBLIC_API_URL: ${NEXT_PUBLIC_API_URL:-http://127.0.0.1:8000}
10+
NEXT_PUBLIC_ADMIN_API_KEY: ${NEXT_PUBLIC_ADMIN_API_KEY:-}
11+
volumes:
12+
- ./ui_out:/output
13+
command:
14+
["sh", "-c", "mkdir -p /output && cp -r /app/built/. /output/ && echo 'UI build copied to mounted volume' && ls -la /output/ && echo 'UI built and ready' && tail -f /dev/null"]
15+
216
routstr:
317
build: .
18+
depends_on:
19+
- ui
420
volumes:
521
- .:/app
622
- ./logs:/app/logs
723
- tor-data:/var/lib/tor:ro
24+
- ./ui_out:/app/ui_out:ro
825
env_file:
926
- .env
1027
environment:

docs/api/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ GET /health
347347
Response:
348348
{
349349
"status": "healthy",
350-
"version": "0.1.4",
350+
"version": "0.2.0",
351351
"timestamp": "2024-01-01T00:00:00Z",
352352
"checks": {
353353
"database": "ok",

docs/contributing/code-structure.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ Project metadata and dependencies:
348348
```toml
349349
[project]
350350
name = "routstr"
351-
version = "0.1.4"
351+
version = "0.2.0"
352352
dependencies = [
353353
"fastapi[standard]>=0.115",
354354
"sqlmodel>=0.0.24",

docs/getting-started/quickstart.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ You should see:
6767
{
6868
"name": "ARoutstrNode",
6969
"description": "A Routstr Node",
70-
"version": "0.1.4",
70+
"version": "0.2.0",
7171
"npub": "",
7272
"mints": ["https://mint.minibits.cash/Bitcoin"],
7373
"models": {...}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""change models to composite primary key (id, upstream_provider_id)
2+
3+
Revision ID: a1a1a1a1a1a1
4+
Revises: f7a8b9c0d1e2
5+
Create Date: 2025-10-20 00:00:00.000000
6+
"""
7+
8+
from __future__ import annotations
9+
10+
import sqlalchemy as sa
11+
from alembic import op
12+
13+
revision = "a1a1a1a1a1a1"
14+
down_revision = "f7a8b9c0d1e2"
15+
branch_labels = None
16+
depends_on = None
17+
18+
19+
def upgrade() -> None:
20+
conn = op.get_bind()
21+
inspector = sa.inspect(conn)
22+
23+
if "models" in inspector.get_table_names():
24+
op.drop_table("models")
25+
26+
op.create_table(
27+
"models",
28+
sa.Column("id", sa.String(), nullable=False),
29+
sa.Column("upstream_provider_id", sa.Integer(), nullable=False),
30+
sa.Column("name", sa.String(), nullable=False),
31+
sa.Column("created", sa.Integer(), nullable=False),
32+
sa.Column("description", sa.Text(), nullable=False),
33+
sa.Column("context_length", sa.Integer(), nullable=False),
34+
sa.Column("architecture", sa.Text(), nullable=False),
35+
sa.Column("pricing", sa.Text(), nullable=False),
36+
sa.Column("sats_pricing", sa.Text(), nullable=True),
37+
sa.Column("per_request_limits", sa.Text(), nullable=True),
38+
sa.Column("top_provider", sa.Text(), nullable=True),
39+
sa.Column("enabled", sa.Boolean(), nullable=False, server_default="1"),
40+
sa.PrimaryKeyConstraint("id", "upstream_provider_id"),
41+
sa.ForeignKeyConstraint(
42+
["upstream_provider_id"], ["upstream_providers.id"], ondelete="CASCADE"
43+
),
44+
)
45+
46+
47+
def downgrade() -> None:
48+
op.drop_table("models")
49+
op.create_table(
50+
"models",
51+
sa.Column("id", sa.String(), primary_key=True, nullable=False),
52+
sa.Column("name", sa.String(), nullable=False),
53+
sa.Column("created", sa.Integer(), nullable=False),
54+
sa.Column("description", sa.Text(), nullable=False),
55+
sa.Column("context_length", sa.Integer(), nullable=False),
56+
sa.Column("architecture", sa.Text(), nullable=False),
57+
sa.Column("pricing", sa.Text(), nullable=False),
58+
sa.Column("sats_pricing", sa.Text(), nullable=True),
59+
sa.Column("per_request_limits", sa.Text(), nullable=True),
60+
sa.Column("top_provider", sa.Text(), nullable=True),
61+
sa.Column("enabled", sa.Boolean(), nullable=False, server_default="1"),
62+
sa.Column("upstream_provider_id", sa.Integer(), nullable=True),
63+
sa.ForeignKeyConstraint(["upstream_provider_id"], ["upstream_providers.id"]),
64+
)

0 commit comments

Comments
 (0)