feat(docs): add screenshots + beginner install guide, MIBP Edition credits
This commit is contained in:
@@ -2,22 +2,159 @@
|
|||||||
|
|
||||||
9Router is a local AI routing gateway with provider fallback and token-saving features.
|
9Router is a local AI routing gateway with provider fallback and token-saving features.
|
||||||
|
|
||||||
## Quick Start
|

|
||||||
|
|
||||||
### Install
|
## New Features
|
||||||
|
|
||||||
#### Docker
|
### Proxy Fitness
|
||||||
|
|
||||||
|
Monitor proxy health and egress location to keep routed requests reliable.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Smart Rotation
|
||||||
|
|
||||||
|
Automatically rotate proxy pools and provider connections for better availability.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation for Beginners
|
||||||
|
|
||||||
|
The easiest installation method is Docker. Docker runs 9Router in a container, so you do not need to install Node.js, npm, or the application dependencies manually.
|
||||||
|
|
||||||
|
### 1. Install Docker
|
||||||
|
|
||||||
|
Install Docker Desktop on Windows or macOS, or install Docker Engine and Docker Compose on Linux:
|
||||||
|
|
||||||
|
- [Docker Desktop](https://www.docker.com/products/docker-desktop/)
|
||||||
|
- [Docker Engine for Linux](https://docs.docker.com/engine/install/)
|
||||||
|
|
||||||
|
After installation, open a terminal and check that Docker is available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker --version
|
||||||
|
docker compose version
|
||||||
|
```
|
||||||
|
|
||||||
|
Both commands should print a version number.
|
||||||
|
|
||||||
|
### 2. Download the project
|
||||||
|
|
||||||
|
Clone this repository, then enter its folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/mhiqrambg/9router-mibp-version.git
|
||||||
|
cd 9router-mibp-version
|
||||||
|
```
|
||||||
|
|
||||||
|
If Git is not installed, download the repository as a ZIP file from GitHub and open the extracted folder in a terminal.
|
||||||
|
|
||||||
|
### 3. Create the environment file
|
||||||
|
|
||||||
|
Create `.env` from the example file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
Open `.env` in a text editor and change at least these values:
|
||||||
|
|
||||||
|
```env
|
||||||
|
JWT_SECRET=replace-with-a-long-random-secret
|
||||||
|
INITIAL_PASSWORD=replace-with-your-dashboard-password
|
||||||
|
API_KEY_SECRET=replace-with-another-long-random-secret
|
||||||
|
MACHINE_ID_SALT=replace-with-another-random-secret
|
||||||
|
REQUIRE_API_KEY=true
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not share `.env` or commit it to Git. It contains the dashboard password and security secrets. On Linux or macOS, you can generate random values with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openssl rand -hex 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Use a different generated value for each secret.
|
||||||
|
|
||||||
|
### 4. Start 9Router
|
||||||
|
|
||||||
|
Run this command in the project folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
Dashboard: `http://localhost:20128/dashboard`
|
The first run downloads the 9Router and Headroom images. The `-d` option keeps them running in the background.
|
||||||
|
|
||||||
API endpoint: `http://localhost:20128/v1`
|
Check the containers:
|
||||||
|
|
||||||
#### From Source
|
```bash
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `9router` and `headroom` should have a running status.
|
||||||
|
|
||||||
|
### 5. Open the dashboard
|
||||||
|
|
||||||
|
Open this address in your browser:
|
||||||
|
|
||||||
|
```text
|
||||||
|
http://localhost:20128/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Log in with the `INITIAL_PASSWORD` value from `.env`.
|
||||||
|
|
||||||
|
### 6. Create an API key
|
||||||
|
|
||||||
|
In the dashboard, open **Endpoint & Key** and create a key. Keep the complete key that starts with `sk-`.
|
||||||
|
|
||||||
|
Use it when calling the API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:20128/v1/models \
|
||||||
|
-H "Authorization: Bearer YOUR_API_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
For a server exposed through a domain, replace the URL with your domain:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl https://your-domain.example/v1/models \
|
||||||
|
-H "Authorization: Bearer YOUR_API_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
Remote requests without a valid API key are rejected. The API key is stored in the application database, not in `.env`.
|
||||||
|
|
||||||
|
### Useful Docker commands
|
||||||
|
|
||||||
|
View live logs:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose logs -f 9router
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose restart 9router
|
||||||
|
```
|
||||||
|
|
||||||
|
Stop the services without deleting data:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
Update to the latest image:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
The Docker volume named `9router-data` keeps your settings, providers, and API keys when the container is recreated.
|
||||||
|
|
||||||
|
### Install from source
|
||||||
|
|
||||||
|
This option is intended for development. Install Node.js 22 or newer, then run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
@@ -25,7 +162,7 @@ npm install
|
|||||||
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
|
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Before starting, set at least `JWT_SECRET` and `INITIAL_PASSWORD` in `.env`.
|
Open `http://localhost:20128/dashboard` after the development server starts.
|
||||||
|
|
||||||
### Update
|
### Update
|
||||||
|
|
||||||
|
|||||||
-1315
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 740 KiB After Width: | Height: | Size: 604 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 306 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 158 KiB |
@@ -1167,6 +1167,14 @@ export default function ProfilePage() {
|
|||||||
<div className="text-center text-xs sm:text-sm text-text-muted py-4">
|
<div className="text-center text-xs sm:text-sm text-text-muted py-4">
|
||||||
<p>{APP_CONFIG.name} v{APP_CONFIG.version}</p>
|
<p>{APP_CONFIG.name} v{APP_CONFIG.version}</p>
|
||||||
<p className="mt-1">Local Mode - All data stored on your machine</p>
|
<p className="mt-1">Local Mode - All data stored on your machine</p>
|
||||||
|
<a
|
||||||
|
href="https://github.com/mhiqrambg/9router-mibp-version"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="inline-block mt-1 text-[11px] hover:text-primary transition-colors"
|
||||||
|
>
|
||||||
|
MIBP Edition
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import ProviderIcon from "@/shared/components/ProviderIcon";
|
|||||||
import HeaderMenu from "@/shared/components/HeaderMenu";
|
import HeaderMenu from "@/shared/components/HeaderMenu";
|
||||||
import HeaderLanguage from "@/shared/components/HeaderLanguage";
|
import HeaderLanguage from "@/shared/components/HeaderLanguage";
|
||||||
import ThemeToggle from "@/shared/components/ThemeToggle";
|
import ThemeToggle from "@/shared/components/ThemeToggle";
|
||||||
import DonateModal from "@/shared/components/DonateModal";
|
|
||||||
import { useHeaderSearchStore } from "@/store/headerSearchStore";
|
import { useHeaderSearchStore } from "@/store/headerSearchStore";
|
||||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config";
|
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS } from "@/shared/constants/config";
|
||||||
import { MEDIA_PROVIDER_KINDS, AI_PROVIDERS } from "@/shared/constants/providers";
|
import { MEDIA_PROVIDER_KINDS, AI_PROVIDERS } from "@/shared/constants/providers";
|
||||||
@@ -183,7 +182,6 @@ export default function Header({ onMenuClick, showMenuButton = true }) {
|
|||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const [displayName, setDisplayName] = useState("");
|
const [displayName, setDisplayName] = useState("");
|
||||||
const [loginMethod, setLoginMethod] = useState("");
|
const [loginMethod, setLoginMethod] = useState("");
|
||||||
const [donateOpen, setDonateOpen] = useState(false);
|
|
||||||
|
|
||||||
// Memoize page info to prevent unnecessary recalculations
|
// Memoize page info to prevent unnecessary recalculations
|
||||||
const pageInfo = useMemo(() => getPageInfo(pathname), [pathname]);
|
const pageInfo = useMemo(() => getPageInfo(pathname), [pathname]);
|
||||||
@@ -313,19 +311,20 @@ export default function Header({ onMenuClick, showMenuButton = true }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<HeaderSearch />
|
<HeaderSearch />
|
||||||
<button
|
<a
|
||||||
onClick={() => setDonateOpen(true)}
|
href="https://github.com/mhiqrambg/9router-mibp-version"
|
||||||
className="flex items-center gap-1.5 px-3 h-8 rounded-lg border border-pink-500/30 bg-pink-500/10 text-pink-600 dark:text-pink-400 hover:bg-pink-500/20 transition-colors text-sm font-medium"
|
target="_blank"
|
||||||
aria-label="Donate"
|
rel="noreferrer"
|
||||||
|
className="flex items-center gap-1.5 px-3 h-8 rounded-lg border border-primary/30 bg-primary/10 text-primary hover:bg-primary/20 transition-colors text-sm font-medium"
|
||||||
|
aria-label="MIBP Edition"
|
||||||
>
|
>
|
||||||
<span className="material-symbols-outlined text-[18px]">volunteer_activism</span>
|
<span className="material-symbols-outlined text-[18px]">code</span>
|
||||||
<span className="hidden sm:inline">Donate</span>
|
<span className="hidden sm:inline">MIBP Edition</span>
|
||||||
</button>
|
</a>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
<HeaderLanguage />
|
<HeaderLanguage />
|
||||||
<HeaderMenu onLogout={handleLogout} />
|
<HeaderMenu onLogout={handleLogout} />
|
||||||
</div>
|
</div>
|
||||||
<DonateModal isOpen={donateOpen} onClose={() => setDonateOpen(false)} />
|
|
||||||
</header>
|
</header>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -347,6 +347,17 @@ export default function Sidebar({ onClose }) {
|
|||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<div className="border-t border-border-subtle px-6 py-3 text-center">
|
||||||
|
<a
|
||||||
|
href="https://github.com/mhiqrambg/9router-mibp-version"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-[10px] text-text-muted/60 hover:text-text-muted transition-colors"
|
||||||
|
>
|
||||||
|
MIBP Edition · GitHub
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
{/* Remote Promo Modal */}
|
{/* Remote Promo Modal */}
|
||||||
|
|||||||
Reference in New Issue
Block a user