mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-28 10:52:10 +03:00
Compare commits
86 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c74ed29739 | ||
|
|
6c8501f122 | ||
|
|
941e945f74 | ||
|
|
f2844d59e4 | ||
|
|
047ff187f6 | ||
|
|
1136c40811 | ||
|
|
5a78dc864f | ||
|
|
15c98c3048 | ||
|
|
0a5b005ce5 | ||
|
|
4d64e64127 | ||
|
|
5470c70cd0 | ||
|
|
47959ee395 | ||
|
|
7c34c178cd | ||
|
|
ac7cb41483 | ||
|
|
0ab388b88e | ||
|
|
54448902f1 | ||
|
|
12107a02fd | ||
|
|
eace06efdc | ||
|
|
ee0afa1eec | ||
|
|
83cdd0dafe | ||
|
|
5be025f1d1 | ||
|
|
c651842ea1 | ||
|
|
423abe6788 | ||
|
|
4003c38fd1 | ||
|
|
3e0c322fd4 | ||
|
|
7fcdd4abdd | ||
|
|
3f3280b2d4 | ||
|
|
aae2399631 | ||
|
|
03bd2b6803 | ||
|
|
48754fd999 | ||
|
|
c496ebdef9 | ||
|
|
c009c40606 | ||
|
|
b29456c8e5 | ||
|
|
38266bf2ff | ||
|
|
c2e51f8948 | ||
|
|
c54a57838e | ||
|
|
64f040bddd | ||
|
|
1a099ea2f2 | ||
|
|
dfbb9d5fff | ||
|
|
a7fe369ea0 | ||
|
|
b62e6c5a69 | ||
|
|
92e29a6ad7 | ||
|
|
eeb9c69aa3 | ||
|
|
b7662ed5a1 | ||
|
|
9d6296f610 | ||
|
|
fd2a1320e0 | ||
|
|
8a8a6a4a82 | ||
|
|
8cdc14eec1 | ||
|
|
a1200b2fb5 | ||
|
|
c88c29eddc | ||
|
|
2845c4de98 | ||
|
|
bfa9cd15b7 | ||
|
|
659e2b414d | ||
|
|
7bcb58e3db | ||
|
|
2d7d7776a6 | ||
|
|
c5f429521c | ||
|
|
426d8636bc | ||
|
|
a265c7096e | ||
|
|
1c9953b1ba | ||
|
|
601cc21a44 | ||
|
|
102c42dfe4 | ||
|
|
4953727aa7 | ||
|
|
e6af874b47 | ||
|
|
801b4eef4c | ||
|
|
fe5c20a04e | ||
|
|
246fd05fae | ||
|
|
a09b298127 | ||
|
|
f89f40778f | ||
|
|
3d0c8d8d45 | ||
|
|
0e5e8bf14e | ||
|
|
ce34d329d3 | ||
|
|
eaf4a5805c | ||
|
|
8420e565d4 | ||
|
|
00df10c29a | ||
|
|
1b68deb0f6 | ||
|
|
d1497c9ac8 | ||
|
|
03d4cbf6d5 | ||
|
|
718be831af | ||
|
|
9d5ec523be | ||
|
|
81c43b45fb | ||
|
|
146a491769 | ||
|
|
4c53388579 | ||
|
|
3403ddcc6e | ||
|
|
41d91d628a | ||
|
|
605c3f9be1 | ||
|
|
2f0894c220 |
@@ -17,6 +17,11 @@ Deploy OmniRoute to the production VPS using `npm pack + scp` + PM2.
|
||||
> The Next.js standalone build is at `app/server.js` inside that directory.
|
||||
> The npm registry rejects packages > 100MB, so deployment uses **npm pack + scp**.
|
||||
|
||||
> [!CAUTION]
|
||||
> **NEVER** use `pm2 restart omniroute` after `npm install -g`. This drops env vars.
|
||||
> Always use `pm2 delete omniroute && pm2 start <ecosystem.config.cjs> --update-env`.
|
||||
> After `npm install -g`, always rebuild better-sqlite3: `cd .../app && npm rebuild better-sqlite3`
|
||||
|
||||
## Steps
|
||||
|
||||
### 1. Build + pack locally
|
||||
@@ -38,11 +43,11 @@ scp omniroute-*.tgz root@69.164.221.35:/tmp/ && scp omniroute-*.tgz root@192.168
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && pm2 restart omniroute && pm2 save && echo '✅ Akamai done'"
|
||||
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Akamai done'"
|
||||
```
|
||||
|
||||
```bash
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && pm2 restart omniroute && pm2 save && echo '✅ Local done'"
|
||||
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 && pm2 delete omniroute 2>/dev/null; pm2 start /root/.omniroute/ecosystem.config.cjs --update-env && pm2 save && echo '✅ Local done'"
|
||||
```
|
||||
|
||||
### 3. Verify the deployment
|
||||
@@ -51,7 +56,11 @@ ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && p
|
||||
ssh root@69.164.221.35 "pm2 list && cat \$(npm root -g)/omniroute/app/package.json | grep version | head -1 && curl -s -o /dev/null -w 'HTTP %{http_code}' http://localhost:20128/"
|
||||
```
|
||||
|
||||
Expected: PM2 shows `online`, version matches, HTTP returns `307`.
|
||||
```bash
|
||||
ssh root@192.168.0.15 "pm2 list && cat \$(npm root -g)/omniroute/app/package.json | grep version | head -1 && curl -s -X POST http://localhost:20128/api/auth/login -H 'Content-Type: application/json' -d '{\"password\":\"123456\"}'"
|
||||
```
|
||||
|
||||
Expected: PM2 shows `online`, version matches, login returns `{"success":true}`.
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -59,26 +68,35 @@ Expected: PM2 shows `online`, version matches, HTTP returns `307`.
|
||||
2. `npm pack --ignore-scripts` packages without re-running the build
|
||||
3. `scp` transfers the .tgz to each VPS (~286MB)
|
||||
4. `npm install -g /tmp/omniroute-*.tgz --ignore-scripts` installs pre-built package
|
||||
5. PM2 runs `app/server.js` from `/usr/lib/node_modules/omniroute`
|
||||
5. `npm rebuild better-sqlite3` recompiles native bindings for the VPS Node.js version
|
||||
6. `pm2 delete` + `pm2 start ecosystem.config.cjs --update-env` restarts with env vars
|
||||
7. `pm2 save` persists the process list for reboot survival
|
||||
|
||||
## Ecosystem Config
|
||||
|
||||
Both VPSs have `ecosystem.config.cjs` at `/root/.omniroute/ecosystem.config.cjs`.
|
||||
This file defines env vars (PORT, DATA_DIR, INITIAL_PASSWORD, OAuth secrets, etc.)
|
||||
that `pm2 restart` does NOT inject — only `pm2 start --update-env` does.
|
||||
|
||||
## PM2 Setup (one-time — if reconfiguring from scratch)
|
||||
|
||||
```bash
|
||||
ssh root@<VPS> "
|
||||
pm2 delete omniroute ;
|
||||
cp /opt/omniroute-app/.env /usr/lib/node_modules/omniroute/.env &&
|
||||
PORT=20128 pm2 start /usr/lib/node_modules/omniroute/app/server.js --name omniroute --cwd /usr/lib/node_modules/omniroute/app &&
|
||||
pm2 delete omniroute 2>/dev/null;
|
||||
cd /usr/lib/node_modules/omniroute/app && npm rebuild better-sqlite3 &&
|
||||
pm2 start /root/.omniroute/ecosystem.config.cjs --update-env &&
|
||||
pm2 save && pm2 startup
|
||||
"
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Copy `.env` from the old installation first. For Akamai it was at `/opt/omniroute-app/.env`,
|
||||
> for the local VPS it was at `/root/omniroute-fresh/.env`.
|
||||
> Ensure `/root/.omniroute/ecosystem.config.cjs` exists with all required env vars.
|
||||
> For fresh installs, copy from the existing VPS or create from the template in `.env`.
|
||||
|
||||
## Notes
|
||||
|
||||
- `.env` should be placed at `/usr/lib/node_modules/omniroute/app/.env`
|
||||
- Env vars are in `/root/.omniroute/ecosystem.config.cjs` (NOT `.env` in app dir)
|
||||
- PM2 is configured with `pm2 startup` to auto-restart on reboot
|
||||
- Nginx proxies `omniroute.online` → `localhost:20128`
|
||||
- The VPS has only 1GB RAM — builds happen locally, never on the VPS
|
||||
- After `npm install -g`, `better-sqlite3` MUST be rebuilt in the `app/` subdir
|
||||
|
||||
@@ -56,13 +56,13 @@ README.pt-BR.md README.pt.md README.es.md README.fr.md README.it.md
|
||||
README.de.md README.nl.md README.sv.md README.no.md README.da.md README.fi.md
|
||||
README.ru.md README.uk-UA.md README.bg.md README.sk.md README.pl.md README.ro.md README.hu.md
|
||||
README.ar.md README.he.md README.th.md README.in.md README.id.md README.ms.md README.vi.md
|
||||
README.ja.md README.ko.md README.zh-CN.md README.phi.md
|
||||
README.ja.md README.ko.md README.zh-CN.md README.phi.md README.cs.md
|
||||
```
|
||||
|
||||
**docs/i18n/ directories (29 languages):**
|
||||
|
||||
```
|
||||
docs/i18n/{ar,bg,da,de,es,fi,fr,he,hu,id,in,it,ja,ko,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv,th,uk-UA,vi,zh-CN}/
|
||||
docs/i18n/{ar,bg,cs,da,de,es,fi,fr,he,hu,id,in,it,ja,ko,ms,nl,no,phi,pl,pt,pt-BR,ro,ru,sk,sv,th,uk-UA,vi,zh-CN}/
|
||||
Each contains: API_REFERENCE.md, ARCHITECTURE.md, CODEBASE_DOCUMENTATION.md, FEATURES.md, TROUBLESHOOTING.md, USER_GUIDE.md
|
||||
```
|
||||
|
||||
|
||||
10
.github/workflows/docker-publish.yml
vendored
10
.github/workflows/docker-publish.yml
vendored
@@ -21,18 +21,18 @@ jobs:
|
||||
IMAGE_NAME: diegosouzapw/omniroute
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'workflow_dispatch' && format('refs/tags/v{0}', inputs.version) || '' }}
|
||||
|
||||
- name: Set up QEMU (for multi-arch builds)
|
||||
uses: docker/setup-qemu-action@v3
|
||||
uses: docker/setup-qemu-action@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Publishing Docker image: $IMAGE_NAME:$VERSION"
|
||||
|
||||
- name: Build and push multi-arch image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: .
|
||||
target: runner-base
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
docker buildx imagetools inspect "${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Update Docker Hub description
|
||||
uses: peter-evans/dockerhub-description@v4
|
||||
uses: peter-evans/dockerhub-description@v5
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -55,6 +55,8 @@ logs/*
|
||||
# analysis directories (generated, not tracked)
|
||||
.analysis/
|
||||
antigravity-manager-analysis/
|
||||
.sisyphus/
|
||||
.plans/
|
||||
|
||||
# docs (allow specific tracked files)
|
||||
docs/*
|
||||
|
||||
314
CHANGELOG.md
314
CHANGELOG.md
@@ -4,6 +4,320 @@
|
||||
|
||||
---
|
||||
|
||||
## [2.9.0] — 2026-03-20
|
||||
|
||||
> Sprint: Cross-platform machineId fix, per-API-key rate limits, streaming context cache, Alibaba DashScope, search analytics, ZWS v5, and 8 issues closed.
|
||||
|
||||
### ✨ New Features
|
||||
|
||||
- **feat(search)**: Search Analytics tab in `/dashboard/analytics` — provider breakdown, cache hit rate, cost tracking. New API: `GET /api/v1/search/analytics` (#feat/search-provider-routing)
|
||||
- **feat(provider)**: Alibaba Cloud DashScope added with custom endpoint path validation — configurable `chatPath` and `modelsPath` per node (#feat/custom-endpoint-paths)
|
||||
- **feat(api)**: Per-API-key request-count limits — `max_requests_per_day` and `max_requests_per_minute` columns with in-memory sliding-window enforcement returning HTTP 429 (#452)
|
||||
- **feat(dev)**: ZWS v5 — HMR leak fix (485 DB connections → 1), memory 2.4GB → 195MB, `globalThis` singletons, Edge Runtime warning fix (@zhangqiang8vip)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(#506)**: Cross-platform `machineId` — `getMachineIdRaw()` rewritten with try/catch waterfall (Windows REG.exe → macOS ioreg → Linux file read → hostname → `os.hostname()`). Eliminates `process.platform` branching that Next.js bundler dead-code-eliminated, fixing `'head' is not recognized` on Windows. Also fixes #466.
|
||||
- **fix(#493)**: Custom provider model naming — removed incorrect prefix stripping in `DefaultExecutor.transformRequest()` that mangled org-scoped model IDs like `zai-org/GLM-5-FP8`.
|
||||
- **fix(#490)**: Streaming + context cache protection — `TransformStream` intercepts SSE to inject `<omniModel>` tag before `[DONE]` marker, enabling context cache protection for streaming responses.
|
||||
- **fix(#458)**: Combo schema validation — `system_message`, `tool_filter_regex`, `context_cache_protection` fields now pass Zod validation on save.
|
||||
- **fix(#487)**: KIRO MITM card cleanup — removed ZWS_README, generified `AntigravityToolCard` to use dynamic tool metadata.
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- Added Anthropic-format tools filter unit tests (PR #397) — 8 regression tests for `tool.name` without `.function` wrapper
|
||||
- Test suite: **821 tests, 0 failures** (up from 813)
|
||||
|
||||
### 📋 Issues Closed (8)
|
||||
|
||||
- **#506** — Windows machineId `head` not recognized (fixed)
|
||||
- **#493** — Custom provider model naming (fixed)
|
||||
- **#490** — Streaming context cache (fixed)
|
||||
- **#452** — Per-API-key request limits (implemented)
|
||||
- **#466** — Windows login failure (same root cause as #506)
|
||||
- **#504** — MITM inactive (expected behavior)
|
||||
- **#462** — Gemini CLI PSA (resolved)
|
||||
- **#434** — Electron app crash (duplicate of #402)
|
||||
|
||||
## [2.8.9] — 2026-03-20
|
||||
|
||||
> Sprint: Merge community PRs, fix KIRO MITM card, dependency updates.
|
||||
|
||||
### Merged PRs
|
||||
|
||||
- **PR #498** (@Sajid11194): Fix Windows machine ID crash (`undefined\REG.exe`). Replaces `node-machine-id` with native OS registry queries. **Closes #486.**
|
||||
- **PR #497** (@zhangqiang8vip): Fix dev-mode HMR resource leaks — 485 leaked DB connections → 1, memory 2.4GB → 195MB. `globalThis` singletons, Edge Runtime warning fix, Windows test stability. (+1168/-338 across 22 files)
|
||||
- **PRs #499-503** (Dependabot): GitHub Actions updates — `docker/build-push-action@7`, `actions/checkout@6`, `peter-evans/dockerhub-description@5`, `docker/setup-qemu-action@4`, `docker/login-action@4`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **#505** — KIRO MITM card now displays tool-specific instructions (`api.anthropic.com`) instead of Antigravity-specific text.
|
||||
- **#504** — Responded with UX clarification (MITM "Inactive" is expected behavior when proxy is not running).
|
||||
|
||||
---
|
||||
|
||||
## [2.8.8] — 2026-03-20
|
||||
|
||||
> Sprint: Fix OAuth batch test crash, add "Test All" button to individual provider pages.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **OAuth batch test crash** (ERR_CONNECTION_REFUSED): Replaced sequential for-loop with 5-connection concurrency limit + 30s per-connection timeout via `Promise.race()` + `Promise.allSettled()`. Prevents server crash when testing large OAuth provider groups (~30+ connections).
|
||||
|
||||
### Features
|
||||
|
||||
- **"Test All" button on provider pages**: Individual provider pages (e.g., `/providers/codex`) now show a "Test All" button in the Connections header when there are 2+ connections. Uses `POST /api/providers/test-batch` with `{mode: "provider", providerId}`. Results displayed in a modal with pass/fail summary and per-connection diagnosis.
|
||||
|
||||
---
|
||||
|
||||
## [2.8.7] — 2026-03-20
|
||||
|
||||
> Sprint: Merge PR #495 (Bottleneck 429 drop), fix #496 (custom embedding providers), triage features.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Bottleneck 429 infinite wait** (PR #495 by @xandr0s): On 429, `limiter.stop({ dropWaitingJobs: true })` immediately fails all queued requests so upstream callers can trigger fallback. Limiter is deleted from Map so next request creates a fresh instance.
|
||||
- **Custom embedding models unresolvable** (#496): `POST /v1/embeddings` now resolves custom embedding models from ALL provider_nodes (not just localhost). Enables models like `google/gemini-embedding-001` added via dashboard.
|
||||
|
||||
### Issues Responded
|
||||
|
||||
- **#452** — Per-API-key request-count limits (acknowledged, on roadmap)
|
||||
- **#464** — Auto-issue API keys with provider/account limits (needs more detail)
|
||||
- **#488** — Auto-update model lists (acknowledged, on roadmap)
|
||||
- **#496** — Custom embedding provider resolution (fixed)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.6] — 2026-03-20
|
||||
|
||||
> Sprint: Merge PR #494 (MiniMax role fix), fix KIRO MITM dashboard, triage 8 issues.
|
||||
|
||||
### Features
|
||||
|
||||
- **MiniMax developer→system role fix** (PR #494 by @zhangqiang8vip): Per-model `preserveDeveloperRole` toggle. Adds "Compatibility" UI in providers page. Fixes 422 "role param error" for MiniMax and similar gateways.
|
||||
- **roleNormalizer**: `normalizeDeveloperRole()` now accepts `preserveDeveloperRole` parameter with tri-state behavior (undefined=keep, true=keep, false=convert).
|
||||
- **DB**: New `getModelPreserveOpenAIDeveloperRole()` and `mergeModelCompatOverride()` in `models.ts`.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **KIRO MITM dashboard** (#481/#487): `CLIToolsPageClient` now routes any `configType: "mitm"` tool to `AntigravityToolCard` (MITM Start/Stop controls). Previously only Antigravity was hardcoded.
|
||||
- **AntigravityToolCard generic**: Uses `tool.image`, `tool.description`, `tool.id` instead of hardcoded Antigravity values. Guards against missing `defaultModels`.
|
||||
|
||||
### Cleanup
|
||||
|
||||
- Removed `ZWS_README_V2.md` (development-only docs from PR #494).
|
||||
|
||||
### Issues Triaged (8)
|
||||
|
||||
- **#487** — Closed (KIRO MITM fixed in this release)
|
||||
- **#486** — needs-info (Windows REG.exe PATH issue)
|
||||
- **#489** — needs-info (Antigravity projectId missing, OAuth reconnect needed)
|
||||
- **#492** — needs-info (missing app/server.js on mise-managed Node)
|
||||
- **#490** — Acknowledged (streaming + context cache blocking, fix planned)
|
||||
- **#491** — Acknowledged (Codex auth state inconsistency)
|
||||
- **#493** — Acknowledged (Modal provider model name prefix, workaround provided)
|
||||
- **#488** — Feature request backlog (auto-update model lists)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.5] — 2026-03-19
|
||||
|
||||
> Sprint: Fix zombie SSE streams, context cache first-turn, KIRO MITM, and triage 5 external issues.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Zombie SSE Streams** (#473): Reduce `STREAM_IDLE_TIMEOUT_MS` from 300s → 120s for faster combo fallback when providers hang mid-stream. Configurable via env var.
|
||||
- **Context Cache Tag** (#474): Fix `injectModelTag()` to handle first-turn requests (no assistant messages) — context cache protection now works from the very first response.
|
||||
- **KIRO MITM** (#481): Change KIRO `configType` from `guide` → `mitm` so the dashboard renders MITM Start/Stop controls.
|
||||
- **E2E Test** (CI): Fix `providers-bailian-coding-plan.spec.ts` — dismiss pre-existing modal overlay before clicking Add API Key button.
|
||||
|
||||
### Closed Issues
|
||||
|
||||
- #473 — Zombie SSE streams bypass combo fallback
|
||||
- #474 — Context cache `<omniModel>` tag missing on first turn
|
||||
- #481 — MITM for KIRO not activatable from dashboard
|
||||
- #468 — Gemini CLI remote server (superseded by #462 deprecation)
|
||||
- #438 — Claude unable to write files (external CLI issue)
|
||||
- #439 — AppImage doesn't work (documented libfuse2 workaround)
|
||||
- #402 — ARM64 DMG "damaged" (documented xattr -cr workaround)
|
||||
- #460 — CLI not runnable on Windows (documented PATH fix)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.4] — 2026-03-19
|
||||
|
||||
> Sprint: Gemini CLI deprecation, VM guide i18n fix, dependabot security fix, provider schema expansion.
|
||||
|
||||
### Features
|
||||
|
||||
- **Gemini CLI Deprecation** (#462): Mark `gemini-cli` provider as deprecated with warning — Google restricts third-party OAuth usage from March 2026
|
||||
- **Provider Schema** (#462): Expand Zod validation with `deprecated`, `deprecationReason`, `hasFree`, `freeNote`, `authHint`, `apiHint` optional fields
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **VM Guide i18n** (#471): Add `VM_DEPLOYMENT_GUIDE.md` to i18n translation pipeline, regenerate all 30 locale translations from English source (were stuck in Portuguese)
|
||||
|
||||
### Security
|
||||
|
||||
- **deps**: Bump `flatted` 3.3.3 → 3.4.2 — fixes CWE-1321 prototype pollution (#484, @dependabot)
|
||||
|
||||
### Closed Issues
|
||||
|
||||
- #472 — Model Aliases regression (fixed in v2.8.2)
|
||||
- #471 — VM guide translations broken
|
||||
- #483 — Trailing `data: null` after `[DONE]` (fixed in v2.8.3)
|
||||
|
||||
### Merged PRs
|
||||
|
||||
- #484 — deps: bump flatted from 3.3.3 to 3.4.2 (@dependabot)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.3] — 2026-03-19
|
||||
|
||||
> Sprint: Czech i18n, SSE protocol fix, VM guide translation.
|
||||
|
||||
### Features
|
||||
|
||||
- **Czech Language** (#482): Full Czech (cs) i18n — 22 docs, 2606 UI strings, language switcher updates (@zen0bit)
|
||||
- **VM Deployment Guide**: Translated from Portuguese to English as the source document (@zen0bit)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **SSE Protocol** (#483): Stop sending trailing `data: null` after `[DONE]` signal — fixes `AI_TypeValidationError` in strict AI SDK clients (Zod-based validators)
|
||||
|
||||
### Merged PRs
|
||||
|
||||
- #482 — Add Czech language + Fix VM_DEPLOYMENT_GUIDE.md English source (@zen0bit)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.2] — 2026-03-19
|
||||
|
||||
> Sprint: 2 merged PRs, model aliases routing fix, log export, and issue triage.
|
||||
|
||||
### Features
|
||||
|
||||
- **Log Export**: New Export button on `/dashboard/logs` with time range dropdown (1h, 6h, 12h, 24h). Downloads JSON of request/proxy/call logs via `/api/logs/export` API (#user-request)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **Model Aliases Routing** (#472): Settings → Model Aliases now correctly affect provider routing, not just format detection. Previously `resolveModelAlias()` output was only used for `getModelTargetFormat()` but the original model ID was sent to the provider
|
||||
- **Stream Flush Usage** (#480): Usage data from the last SSE event in the buffer is now correctly extracted during stream flush (merged from @prakersh)
|
||||
|
||||
### Merged PRs
|
||||
|
||||
- #480 — Extract usage from remaining buffer in flush handler (@prakersh)
|
||||
- #479 — Add missing Codex 5.3/5.4 and Anthropic model ID pricing entries (@prakersh)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.1] — 2026-03-19
|
||||
|
||||
> Sprint: Five community PRs — streaming call log fixes, Kiro compatibility, cache token analytics, Chinese translation, and configurable tool call IDs.
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **feat(logs)**: Call log response content now correctly accumulated from raw provider chunks (OpenAI/Claude/Gemini) before translation, fixing empty response payloads in streaming mode (#470, @zhangqiang8vip)
|
||||
- **feat(providers)**: Per-model configurable 9-char tool call ID normalization (Mistral-style) — only models with the option enabled get truncated IDs (#470)
|
||||
- **feat(api)**: Key PATCH API expanded to support `allowedConnections`, `name`, `autoResolve`, `isActive`, and `accessSchedule` fields (#470)
|
||||
- **feat(dashboard)**: Response-first layout in request log detail UI (#470)
|
||||
- **feat(i18n)**: Improved Chinese (zh-CN) translation — complete retranslation (#475, @only4copilot)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(kiro)**: Strip injected `model` field from request body — Kiro API rejects unknown top-level fields (#478, @prakersh)
|
||||
- **fix(usage)**: Include cache read + cache creation tokens in usage history input totals for accurate analytics (#477, @prakersh)
|
||||
- **fix(callLogs)**: Support Claude format usage fields (`input_tokens`/`output_tokens`) alongside OpenAI format, include all cache token variants (#476, @prakersh)
|
||||
|
||||
---
|
||||
|
||||
## [2.8.0] — 2026-03-19
|
||||
|
||||
> Sprint: Bailian Coding Plan provider with editable base URLs, plus community contributions for Alibaba Cloud and Kimi Coding.
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **feat(providers)**: Added Bailian Coding Plan (`bailian-coding-plan`) — Alibaba Model Studio with Anthropic-compatible API. Static catalog of 8 models including Qwen3.5 Plus, Qwen3 Coder, MiniMax M2.5, GLM 5, and Kimi K2.5. Includes custom auth validation (400=valid, 401/403=invalid) (#467, @Mind-Dragon)
|
||||
- **feat(admin)**: Editable default URL in Provider Admin create/edit flows — users can configure custom base URLs per connection. Persisted in `providerSpecificData.baseUrl` with Zod schema validation rejecting non-http(s) schemes (#467)
|
||||
|
||||
### 🧪 Tests
|
||||
|
||||
- Added 30+ unit tests and 2 e2e scenarios for Bailian Coding Plan provider covering auth validation, schema hardening, route-level behavior, and cross-layer integration
|
||||
|
||||
---
|
||||
|
||||
## [2.7.10] — 2026-03-19
|
||||
|
||||
> Sprint: Two new community-contributed providers (Alibaba Cloud Coding, Kimi Coding API-key) and Docker pino fix.
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **feat(providers)**: Added Alibaba Cloud Coding Plan support with two OpenAI-compatible endpoints — `alicode` (China) and `alicode-intl` (International), each with 8 models (#465, @dtk1985)
|
||||
- **feat(providers)**: Added dedicated `kimi-coding-apikey` provider path — API-key-based Kimi Coding access is no longer forced through OAuth-only `kimi-coding` route. Includes registry, constants, models API, config, and validation test (#463, @Mind-Dragon)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(docker)**: Added missing `split2` dependency to Docker image — `pino-abstract-transport` requires it at runtime but it was not being copied into the standalone container, causing `Cannot find module 'split2'` crashes (#459)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.9] — 2026-03-18
|
||||
|
||||
> Sprint: Codex responses subpath passthrough natively supported, Windows MITM crash fixed, and Combos agent schemas adjusted.
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **feat(codex)**: Native responses subpath passthrough for Codex — natively routes `POST /v1/responses/compact` to Codex upstream, maintaining Claude Code compatibility without stripping the `/compact` suffix (#457)
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(combos)**: Zod schemas (`updateComboSchema` and `createComboSchema`) now include `system_message`, `tool_filter_regex`, and `context_cache_protection`. Fixes bug where agent-specific settings created via the dashboard were silently discarded by the backend validation layer (#458)
|
||||
- **fix(mitm)**: Kiro MITM profile crash on Windows fixed — `node-machine-id` failed due to missing `REG.exe` env, and the fallback threw a fatal `crypto is not defined` error. Fallback now safely and correctly imports crypto (#456)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.8] — 2026-03-18
|
||||
|
||||
> Sprint: Budget save bug + combo agent features UI + omniModel tag security fix.
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(budget)**: "Save Limits" no longer returns 422 — `warningThreshold` is now correctly sent as fraction (0–1) instead of percentage (0–100) (#451)
|
||||
- **fix(combos)**: `<omniModel>` internal cache tag is now stripped before forwarding requests to providers, preventing cache session breaks (#454)
|
||||
|
||||
### ✨ Features
|
||||
|
||||
- **feat(combos)**: Agent Features section added to combo create/edit modal — expose `system_message` override, `tool_filter_regex`, and `context_cache_protection` directly from the dashboard (#454)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.7] — 2026-03-18
|
||||
|
||||
> Sprint: Docker pino crash, Codex CLI responses worker fix, package-lock sync.
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(docker)**: `pino-abstract-transport` and `pino-pretty` now explicitly copied in Docker runner stage — Next.js standalone trace misses these peer deps, causing `Cannot find module pino-abstract-transport` crash on startup (#449)
|
||||
- **fix(responses)**: Remove `initTranslators()` from `/v1/responses` route — was crashing Next.js worker with `the worker has exited` uncaughtException on Codex CLI requests (#450)
|
||||
|
||||
### 🔧 Maintenance
|
||||
|
||||
- **chore(deps)**: `package-lock.json` now committed on every version bump to ensure Docker `npm ci` uses exact dependency versions
|
||||
|
||||
---
|
||||
|
||||
## [2.7.5] — 2026-03-18
|
||||
|
||||
> Sprint: UX improvements and Windows CLI healthcheck fix.
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- **fix(ux)**: Show default password hint on login page — new users now see `"Default password: 123456"` below the password input (#437)
|
||||
- **fix(cli)**: Claude CLI and other npm-installed tools now correctly detected as runnable on Windows — spawn uses `shell:true` to resolve `.cmd` wrappers via PATHEXT (#447)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.4] — 2026-03-18
|
||||
|
||||
> Sprint: Search Tools dashboard, i18n fixes, Copilot limits, Serper validation fix.
|
||||
|
||||
@@ -32,6 +32,11 @@ COPY --from=builder /app/.next/static ./.next/static
|
||||
COPY --from=builder /app/.next/standalone ./
|
||||
# Explicitly copy @swc/helpers — not always traced by standalone output but needed at runtime
|
||||
COPY --from=builder /app/node_modules/@swc/helpers ./node_modules/@swc/helpers
|
||||
# Explicitly copy pino transport dependencies — pino spawns a worker that requires
|
||||
# pino-abstract-transport at runtime; Next.js standalone trace does not capture it (#449)
|
||||
COPY --from=builder /app/node_modules/pino-abstract-transport ./node_modules/pino-abstract-transport
|
||||
COPY --from=builder /app/node_modules/pino-pretty ./node_modules/pino-pretty
|
||||
COPY --from=builder /app/node_modules/split2 ./node_modules/split2
|
||||
COPY --from=builder /app/scripts/run-standalone.mjs ./run-standalone.mjs
|
||||
COPY --from=builder /app/scripts/runtime-env.mjs ./runtime-env.mjs
|
||||
COPY --from=builder /app/scripts/bootstrap-env.mjs ./bootstrap-env.mjs
|
||||
|
||||
@@ -20,7 +20,7 @@ _Your universal API proxy — one endpoint, 44+ providers, zero downtime. Now wi
|
||||
|
||||
</div>
|
||||
|
||||
🌐 **Available in:** 🇺🇸 [English](README.md) | 🇧🇷 [Português (Brasil)](docs/i18n/pt-BR/README.md) | 🇪🇸 [Español](docs/i18n/es/README.md) | 🇫🇷 [Français](docs/i18n/fr/README.md) | 🇮🇹 [Italiano](docs/i18n/it/README.md) | 🇷🇺 [Русский](docs/i18n/ru/README.md) | 🇨🇳 [中文 (简体)](docs/i18n/zh-CN/README.md) | 🇩🇪 [Deutsch](docs/i18n/de/README.md) | 🇮🇳 [हिन्दी](docs/i18n/in/README.md) | 🇹🇭 [ไทย](docs/i18n/th/README.md) | 🇺🇦 [Українська](docs/i18n/uk-UA/README.md) | 🇸🇦 [العربية](docs/i18n/ar/README.md) | 🇯🇵 [日本語](docs/i18n/ja/README.md) | 🇻🇳 [Tiếng Việt](docs/i18n/vi/README.md) | 🇧🇬 [Български](docs/i18n/bg/README.md) | 🇩🇰 [Dansk](docs/i18n/da/README.md) | 🇫🇮 [Suomi](docs/i18n/fi/README.md) | 🇮🇱 [עברית](docs/i18n/he/README.md) | 🇭🇺 [Magyar](docs/i18n/hu/README.md) | 🇮🇩 [Bahasa Indonesia](docs/i18n/id/README.md) | 🇰🇷 [한국어](docs/i18n/ko/README.md) | 🇲🇾 [Bahasa Melayu](docs/i18n/ms/README.md) | 🇳🇱 [Nederlands](docs/i18n/nl/README.md) | 🇳🇴 [Norsk](docs/i18n/no/README.md) | 🇵🇹 [Português (Portugal)](docs/i18n/pt/README.md) | 🇷🇴 [Română](docs/i18n/ro/README.md) | 🇵🇱 [Polski](docs/i18n/pl/README.md) | 🇸🇰 [Slovenčina](docs/i18n/sk/README.md) | 🇸🇪 [Svenska](docs/i18n/sv/README.md) | 🇵🇭 [Filipino](docs/i18n/phi/README.md)
|
||||
🌐 **Available in:** 🇺🇸 [English](README.md) | 🇧🇷 [Português (Brasil)](docs/i18n/pt-BR/README.md) | 🇪🇸 [Español](docs/i18n/es/README.md) | 🇫🇷 [Français](docs/i18n/fr/README.md) | 🇮🇹 [Italiano](docs/i18n/it/README.md) | 🇷🇺 [Русский](docs/i18n/ru/README.md) | 🇨🇳 [中文 (简体)](docs/i18n/zh-CN/README.md) | 🇩🇪 [Deutsch](docs/i18n/de/README.md) | 🇮🇳 [हिन्दी](docs/i18n/in/README.md) | 🇹🇭 [ไทย](docs/i18n/th/README.md) | 🇺🇦 [Українська](docs/i18n/uk-UA/README.md) | 🇸🇦 [العربية](docs/i18n/ar/README.md) | 🇯🇵 [日本語](docs/i18n/ja/README.md) | 🇻🇳 [Tiếng Việt](docs/i18n/vi/README.md) | 🇧🇬 [Български](docs/i18n/bg/README.md) | 🇩🇰 [Dansk](docs/i18n/da/README.md) | 🇫🇮 [Suomi](docs/i18n/fi/README.md) | 🇮🇱 [עברית](docs/i18n/he/README.md) | 🇭🇺 [Magyar](docs/i18n/hu/README.md) | 🇮🇩 [Bahasa Indonesia](docs/i18n/id/README.md) | 🇰🇷 [한국어](docs/i18n/ko/README.md) | 🇲🇾 [Bahasa Melayu](docs/i18n/ms/README.md) | 🇳🇱 [Nederlands](docs/i18n/nl/README.md) | 🇳🇴 [Norsk](docs/i18n/no/README.md) | 🇵🇹 [Português (Portugal)](docs/i18n/pt/README.md) | 🇷🇴 [Română](docs/i18n/ro/README.md) | 🇵🇱 [Polski](docs/i18n/pl/README.md) | 🇸🇰 [Slovenčina](docs/i18n/sk/README.md) | 🇸🇪 [Svenska](docs/i18n/sv/README.md) | 🇵🇭 [Filipino](docs/i18n/phi/README.md) | 🇨🇿 [Čeština](docs/i18n/cs/README.md)
|
||||
|
||||
---
|
||||
|
||||
|
||||
374
ZWS_README_V4.md
Normal file
374
ZWS_README_V4.md
Normal file
@@ -0,0 +1,374 @@
|
||||
# ZWS_README_V4 — 启动性能优化:HMR 泄漏修复与 Turbopack 迁移
|
||||
|
||||
## 一、如何发现问题
|
||||
|
||||
### 现象
|
||||
|
||||
- `npm run dev` 后,首次打开浏览器白屏等待 **5-22 秒**不等。
|
||||
- 运行一段时间后 Node 进程内存飙升至 **2.4 GB**,触发 Next.js 内存阈值保护强制重启。
|
||||
- 重启后 `Ready in 82.6s`(正常冷启动仅 3.4s),之后每个页面首次编译需 **7-28 秒**。
|
||||
- 日志中大量重复输出,单次会话内:
|
||||
- `[DB] SQLite database ready` 出现 **485 次**
|
||||
- `[HealthCheck] Starting proactive token health-check` 出现 **586 次**
|
||||
- `[CREDENTIALS] No external credentials file found` 出现 **432 次**
|
||||
|
||||
### 排查过程
|
||||
|
||||
1. **Terminal 日志分析**:统计关键日志出现次数,发现 DB 连接和 HealthCheck 定时器被反复创建。
|
||||
2. **代码审计**:追踪到所有受影响模块使用 `let initialized = false` 作为单例守卫——这在 Next.js dev 模式的 Webpack HMR 下会被重置。
|
||||
3. **对比**:`apiBridgeServer.ts` 使用了 `globalThis.__omnirouteApiBridgeStarted`,在日志中无重复初始化,验证了 `globalThis` 方案的有效性。
|
||||
4. **内存快照**:通过 `Get-Process node` 观察到两个 node 进程分别占用 1.7GB 和 1.0GB。
|
||||
5. **编译时间分析**:日志中 `compile:` 字段显示 Webpack 编译每个路由需 2-26 秒,对比 Turbopack 应在 0.5-3 秒。
|
||||
|
||||
---
|
||||
|
||||
## 二、根因分析
|
||||
|
||||
### 根因 1(P0):模块级单例在 HMR 中丢失
|
||||
|
||||
Next.js dev 模式下,Webpack HMR 会重新执行被修改(或依赖链变化)的模块。模块级 `let` 变量在每次重新执行时被重置为初始值。
|
||||
|
||||
```typescript
|
||||
// 修复前 — 每次 HMR 重新执行时 _db 重置为 null
|
||||
let _db: SqliteDatabase | null = null;
|
||||
|
||||
export function getDbInstance() {
|
||||
if (_db) return _db; // HMR 后这里永远 false
|
||||
// ... 重新打开一个新的 DB 连接(旧连接泄漏)
|
||||
}
|
||||
```
|
||||
|
||||
**受影响的模块与泄漏类型:**
|
||||
|
||||
| 模块 | 泄漏资源 | 累计次数 | 后果 |
|
||||
| ----------------------- | ---------------------- | -------- | ----------------------- |
|
||||
| `db/core.ts` | SQLite 连接 | 485 | 文件句柄泄漏 + 内存占用 |
|
||||
| `tokenHealthCheck.ts` | `setInterval` 定时器 | 586 | CPU 空转 + DB 查询风暴 |
|
||||
| `localHealthCheck.ts` | `setTimeout` 定时器链 | ~400 | 重复 HTTP 请求 + CPU |
|
||||
| `consoleInterceptor.ts` | console 方法包装 | ~400 | 日志 double-write |
|
||||
| `gracefulShutdown.ts` | SIGTERM/SIGINT handler | ~400 | 信号处理器堆叠 |
|
||||
|
||||
**级联效应**:泄漏的资源持续消耗内存和 CPU → 触发 Next.js 内存阈值保护 → 进程重启 → Webpack 从零重建模块图 → **Ready in 82.6s**。
|
||||
|
||||
### 根因 2(P0):强制使用 Webpack 而非 Turbopack
|
||||
|
||||
`scripts/run-next.mjs` 中硬编码了 `--webpack` 标志:
|
||||
|
||||
```javascript
|
||||
if (mode === "dev") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
```
|
||||
|
||||
Next.js 16 默认使用 Turbopack(Rust 编写的增量打包器),dev 编译速度是 Webpack 的 5-10 倍。强制回退到 Webpack 导致:
|
||||
|
||||
| 指标 | Webpack | Turbopack(预期) |
|
||||
| ----------------------- | ------- | ----------------- |
|
||||
| 首页编译 | 3.7s | ~0.5s |
|
||||
| Provider 详情页首次编译 | 22s | ~2-3s |
|
||||
| API route 首次编译 | 2-7s | ~0.3-1s |
|
||||
| 内存重启后 Ready | 82.6s | 不会触发 |
|
||||
|
||||
### 根因 3(P1):`node:crypto` 被拉入客户端 bundle
|
||||
|
||||
`src/lib/db/proxies.ts` 使用了 `import { randomUUID } from "node:crypto"`。通过 `localDb.ts` 的 re-export 链,这个 Node.js 原生模块被间接拉入客户端组件的 bundle,导致 Webpack 报错:
|
||||
|
||||
```
|
||||
UnhandledSchemeError: Reading from "node:crypto" is not handled by plugins
|
||||
Import trace: node:crypto → ./src/lib/db/proxies.ts → ./src/lib/localDb.ts → page.tsx
|
||||
```
|
||||
|
||||
Webpack 无法处理 `node:` URI scheme 前缀。`crypto`(不带 `node:` 前缀)已在 `next.config.mjs` 的 `serverExternalPackages` 中声明为服务端外部包。
|
||||
|
||||
### 根因 4(P1):Edge Runtime 编译警告刷屏
|
||||
|
||||
Next.js 16 会同时为 **Node.js** 和 **Edge** 两种运行时编译 `instrumentation.ts`。虽然 `register()` 函数内有 `process.env.NEXT_RUNTIME === "nodejs"` 的运行时守卫,但 Turbopack 在打包 Edge 版本时仍会**静态追踪**所有动态 `import()` 的依赖链:
|
||||
|
||||
```
|
||||
instrumentation.ts
|
||||
→ import("@/lib/db/secrets")
|
||||
→ @/lib/db/core.ts → fs, path, better-sqlite3
|
||||
→ @/lib/dataPaths.ts → path, os
|
||||
→ @/lib/db/migrationRunner.ts → fs, path, url
|
||||
```
|
||||
|
||||
对每个 Node.js 原生模块,Turbopack 都输出一条 "not supported in Edge Runtime" 警告。每次有新请求触发热编译时,这组 **10+ 条警告重复刷一遍**,严重污染终端输出,干扰开发调试。
|
||||
|
||||
### 根因 5(P2):启动 import 完全串行
|
||||
|
||||
`instrumentation.ts` 中 9 个 `await import()` 完全串行执行,每个都可能触发 Webpack 编译其依赖树:
|
||||
|
||||
```typescript
|
||||
await ensureSecrets(); // 串行 1
|
||||
const { initConsoleInterceptor } = await import(...); // 串行 2
|
||||
const { initGracefulShutdown } = await import(...); // 串行 3
|
||||
const { initApiBridgeServer } = await import(...); // 串行 4
|
||||
const { startBackgroundRefresh } = await import(...); // 串行 5
|
||||
const { getSettings } = await import(...); // 串行 6
|
||||
const { setCustomAliases } = await import(...); // 串行 7
|
||||
const { setDefaultFastServiceTierEnabled } = await import(...); // 串行 8
|
||||
const { initAuditLog, cleanupExpiredLogs } = await import(...); // 串行 9
|
||||
```
|
||||
|
||||
其中 4-6 互不依赖,7-8 互不依赖,完全可以并行。
|
||||
|
||||
---
|
||||
|
||||
## 三、修复方案
|
||||
|
||||
### 修复 1:globalThis 单例守卫(core.ts, tokenHealthCheck.ts, localHealthCheck.ts, consoleInterceptor.ts, gracefulShutdown.ts)
|
||||
|
||||
**原理**:`globalThis` 对象在 Node.js 进程生命周期内全局唯一,不受 Webpack 模块重新执行的影响。
|
||||
|
||||
```typescript
|
||||
// 修复后 — globalThis 在 HMR 后依然保留
|
||||
declare global {
|
||||
var __omnirouteDb: import("better-sqlite3").Database | undefined;
|
||||
}
|
||||
|
||||
function getDb() {
|
||||
return globalThis.__omnirouteDb ?? null;
|
||||
}
|
||||
function setDb(db) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
export function getDbInstance() {
|
||||
const existing = getDb();
|
||||
if (existing) return existing; // HMR 后命中缓存
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**每个模块的具体改动:**
|
||||
|
||||
| 模块 | globalThis key | 守卫内容 |
|
||||
| ----------------------- | ----------------------------------- | ----------------------------------------------------------- |
|
||||
| `db/core.ts` | `__omnirouteDb` | SQLite 连接实例 |
|
||||
| `tokenHealthCheck.ts` | `__omnirouteTokenHC` | `{ initialized, interval }` |
|
||||
| `localHealthCheck.ts` | `__omnirouteLocalHC` | `{ initialized, sweepTimer, healthCache, sweepInProgress }` |
|
||||
| `consoleInterceptor.ts` | `__omnirouteConsoleInterceptorInit` | `boolean` |
|
||||
| `gracefulShutdown.ts` | `__omnirouteShutdownInit` | `boolean` |
|
||||
|
||||
**优点**:
|
||||
|
||||
- 零依赖,无需额外库。
|
||||
- 与 `apiBridgeServer.ts` 已有模式一致。
|
||||
- 对生产环境零影响(非 HMR 场景下行为完全相同)。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- `globalThis` 键名需全局唯一,使用 `__omniroute` 前缀避免冲突。
|
||||
- 需要 `declare global` 类型声明以保持 TypeScript 类型安全。
|
||||
- 生产构建中 `globalThis` 存储略冗余(但仅是一个对象引用,几乎零开销)。
|
||||
|
||||
### 修复 2:支持通过环境变量切换 Turbopack(run-next.mjs)
|
||||
|
||||
```javascript
|
||||
// 修复后 — 默认仍用 webpack(保持原有行为),设置环境变量可启用 Turbopack
|
||||
if (mode === "dev" && process.env.OMNIROUTE_USE_TURBOPACK !== "1") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
```
|
||||
|
||||
**默认行为不变**:dev 模式仍使用 Webpack,与修复前完全一致。设置 `OMNIROUTE_USE_TURBOPACK=1` 可切换到 Turbopack 以获得更快的 dev 编译速度。
|
||||
|
||||
**优点**:
|
||||
|
||||
- 零风险:不改变任何人的现有体验。
|
||||
- 需要时设置 `OMNIROUTE_USE_TURBOPACK=1` 即可获得 5-10 倍编译加速。
|
||||
- `next.config.mjs` 中已有 `turbopack.resolveAlias` 配置,说明项目已在准备 Turbopack 迁移。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- Turbopack 对某些 Webpack 特定配置(如自定义 externals 函数)的支持方式不同,启用前需测试兼容性。
|
||||
- 默认走 Webpack 意味着不主动启用 Turbopack 的用户无法享受编译加速。
|
||||
|
||||
### 修复 3:`node:crypto` → `crypto`(proxies.ts, errorResponse.ts)
|
||||
|
||||
```typescript
|
||||
// 修复前
|
||||
import { randomUUID } from "node:crypto";
|
||||
|
||||
// 修复后
|
||||
import { randomUUID } from "crypto";
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- `crypto`(无 `node:` 前缀)已在 `next.config.mjs` 的 `serverExternalPackages` 列表中,Webpack/Turbopack 会正确将其标记为外部包。
|
||||
- 消除 `UnhandledSchemeError` 构建失败。
|
||||
- Node.js 中 `crypto` 和 `node:crypto` 解析到同一模块。
|
||||
|
||||
**缺点**:
|
||||
|
||||
- 无。`crypto` 是 Node.js 内建模块,两种写法功能完全等价。
|
||||
|
||||
### 修复 4:分离 Edge/Node.js Instrumentation(instrumentation.ts → instrumentation-node.ts)
|
||||
|
||||
**问题**:`instrumentation.ts` 中所有 Node.js 逻辑(`ensureSecrets`、DB 初始化、审计日志等)虽然只在 `NEXT_RUNTIME === "nodejs"` 时执行,但 Turbopack 编译 Edge 版本时仍静态追踪其 import 链,对每个 `fs`/`path`/`os`/`better-sqlite3` 等原生模块输出警告。
|
||||
|
||||
**方案**:将所有 Node.js 专属逻辑提取到 `src/instrumentation-node.ts`,主文件通过**计算的 import 路径**引入,阻止 Turbopack 静态解析:
|
||||
|
||||
```typescript
|
||||
// src/instrumentation.ts — 精简后仅 ~20 行
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === "nodejs") {
|
||||
// 拼接路径阻止 Turbopack 在 Edge 编译时静态解析模块依赖
|
||||
const nodeMod = "./instrumentation-" + "node";
|
||||
const { registerNodejs } = await import(nodeMod);
|
||||
await registerNodejs();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// src/instrumentation-node.ts — 包含全部 Node.js 启动逻辑
|
||||
export async function registerNodejs(): Promise<void> {
|
||||
await ensureSecrets();
|
||||
// initConsoleInterceptor, initGracefulShutdown, initApiBridgeServer, ...
|
||||
// (原 instrumentation.ts 的完整 Node.js 逻辑)
|
||||
}
|
||||
```
|
||||
|
||||
**关键技术**:`"./instrumentation-" + "node"` 是运行时拼接的字符串,Turbopack 无法在编译期确定其值,因此**不会追踪**该 import 的依赖树。Node.js 运行时则正常解析该路径并执行。
|
||||
|
||||
**优点**:
|
||||
|
||||
- Edge 编译时完全跳过 Node.js 模块追踪,**10+ 条重复警告全部消除**。
|
||||
- Node.js 运行时行为与修复前完全一致。
|
||||
- 启动时间从 **13.9s → 1.25s**(Turbopack 不再在 Edge 编译中处理 Node.js 模块图)。
|
||||
|
||||
**缺点/注意**:
|
||||
|
||||
- 新增一个文件 `instrumentation-node.ts`,需同步维护。
|
||||
- 计算 import 路径是有意为之的 bundler 逃逸技巧,需加注释说明原因防止后续重构时被"优化"回静态字符串。
|
||||
|
||||
### 修复 5:并行化 instrumentation.ts 中的启动 import
|
||||
|
||||
```typescript
|
||||
// 修复后 — 4 个独立模块并行导入
|
||||
const [
|
||||
{ initGracefulShutdown },
|
||||
{ initApiBridgeServer },
|
||||
{ startBackgroundRefresh },
|
||||
{ getSettings },
|
||||
] = await Promise.all([
|
||||
import("@/lib/gracefulShutdown"),
|
||||
import("@/lib/apiBridgeServer"),
|
||||
import("@/domain/quotaCache"),
|
||||
import("@/lib/db/settings"),
|
||||
]);
|
||||
|
||||
// 2 个 open-sse 模块也并行导入
|
||||
const [{ setCustomAliases }, { setDefaultFastServiceTierEnabled }] = await Promise.all([
|
||||
import("@omniroute/open-sse/services/modelDeprecation.ts"),
|
||||
import("@omniroute/open-sse/executors/codex.ts"),
|
||||
]);
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- `consoleInterceptor` 仍保持第一个(必须在任何日志前初始化)。
|
||||
- 后续 4 个无依赖模块并行加载,节省 3 次串行等待。
|
||||
- open-sse 的 2 个模块也并行加载。
|
||||
|
||||
**缺点**:
|
||||
|
||||
- 并行 import 的错误堆栈略复杂(Promise.all 中某一个失败会 reject 整个组)。
|
||||
- 这里的 compliance 模块仍保持独立 try/catch 串行,因为它有自己的错误处理逻辑。
|
||||
|
||||
---
|
||||
|
||||
## 四、预期效果
|
||||
|
||||
| 指标 | 修复前 | 修复后(预期) |
|
||||
| ----------------------------- | ------------------------- | ------------------------ |
|
||||
| DB 连接创建次数 | 485 次/会话 | 1 次 |
|
||||
| HealthCheck 定时器 | 586 个泄漏 | 1 个 |
|
||||
| 信号处理器注册 | ~400 次重复 | 1 次 |
|
||||
| Console 拦截层数 | ~400 层嵌套 | 1 层 |
|
||||
| 内存使用峰值 | 2.4 GB → OOM 重启 | 预期 < 500 MB |
|
||||
| 冷启动 Ready | 3.4s | ~3s(略快) |
|
||||
| 内存重启 Ready | 82.6s | 不再触发内存重启 |
|
||||
| Login 页首次编译 | 3.7s | ~0.5s (需启用 Turbopack) |
|
||||
| Provider 详情页首次编译 | 22s | ~2-3s (需启用 Turbopack) |
|
||||
| `node:crypto` 构建错误 | 反复出现 | 消除 |
|
||||
| Edge Runtime 编译警告 | 每次热编译刷出 10+ 条 | **0 条** |
|
||||
| instrumentation 启动耗时 | 13.9s(含 Edge 模块追踪) | **1.25s** |
|
||||
| instrumentation import 并行度 | 9 次串行 import | 3 批并行 import |
|
||||
|
||||
---
|
||||
|
||||
## 五、涉及文件清单
|
||||
|
||||
| 区域 | 文件 | 改动类型 |
|
||||
| ------------------- | ------------------------------- | ------------------------------------------------------------------ |
|
||||
| DB 单例 | `src/lib/db/core.ts` | `let _db` → `globalThis.__omnirouteDb` |
|
||||
| Token 健康检查 | `src/lib/tokenHealthCheck.ts` | `let initialized` → `globalThis.__omnirouteTokenHC` |
|
||||
| 本地节点健康检查 | `src/lib/localHealthCheck.ts` | `let initialized` → `globalThis.__omnirouteLocalHC` |
|
||||
| Console 拦截 | `src/lib/consoleInterceptor.ts` | `let initialized` → `globalThis.__omnirouteConsoleInterceptorInit` |
|
||||
| 优雅关停 | `src/lib/gracefulShutdown.ts` | 新增 `globalThis.__omnirouteShutdownInit` 守卫 |
|
||||
| Dev 启动脚本 | `scripts/run-next.mjs` | 新增 `OMNIROUTE_USE_TURBOPACK=1` 开关 |
|
||||
| Proxy 注册表 | `src/lib/db/proxies.ts` | `node:crypto` → `crypto` |
|
||||
| API 错误响应 | `src/lib/api/errorResponse.ts` | `node:crypto` → `crypto` |
|
||||
| 启动钩子(主入口) | `src/instrumentation.ts` | 精简为 ~20 行,计算 import 路径阻止 Edge 追踪 |
|
||||
| 启动钩子(Node.js) | `src/instrumentation-node.ts` | 新文件,承载全部 Node.js 启动逻辑 + `Promise.all` 并行 |
|
||||
|
||||
---
|
||||
|
||||
## 六、回退方案
|
||||
|
||||
- **启用 Turbopack**:设置 `OMNIROUTE_USE_TURBOPACK=1` 环境变量;不设置则默认使用 Webpack(原有行为不变)。
|
||||
- **globalThis 方案异常**:所有 globalThis key 都以 `__omniroute` 为前缀,可通过 `delete globalThis.__omnirouteDb` 等方式手动重置。
|
||||
- **Edge 警告回退**:若 `instrumentation-node.ts` 拆分导致问题,可将其内容合并回 `instrumentation.ts`,恢复为直接 `import()` 调用(警告会重新出现但不影响功能)。
|
||||
- **生产环境**:以上修复对生产构建无负面影响——生产环境不存在 HMR,globalThis 单例仅在首次调用时初始化一次。计算 import 路径在 `next build` 时由 Node.js 正常解析,不影响打包产物。
|
||||
|
||||
---
|
||||
|
||||
## 七、单元测试与备份恢复(pre-commit 验证通过)
|
||||
|
||||
为保证提交前必须通过验证(不再使用 `--no-verify`),对以下失败用例与生产逻辑做了修复与加固。
|
||||
|
||||
### 问题与根因
|
||||
|
||||
| 失败项 | 根因 |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| bootstrap-env 4 个用例 | Windows 上 DATA_DIR 解析用 `APPDATA`/`homedir()`,测试只设了 `HOME`,脚本读不到测试用的 `.env`。 |
|
||||
| domain-persistence costRules 2 个用例 | `core` 在首次 import 时缓存 `DATA_DIR`;测试每测一个 tmpDir 并在 afterEach 删目录,导致后续 describe 使用的 DB 路径已被删,读写得到 0。 |
|
||||
| fixes-p1 restoreDbBackup | 测试在 DB 仍打开时写 stale 侧文件;`restoreDbBackup` 内 pre-restore 备份未 await 就关库,Windows 上句柄未及时释放,unlink 报 EBUSY。 |
|
||||
| fixes-p1 resetStorage 及后续用例 | 上一测留下 DB 打开,下一测 `resetStorage()` 删目录时文件仍被占用,EBUSY。 |
|
||||
|
||||
### 修复 6:bootstrap-env 测试(tests/unit/bootstrap-env.test.mjs)
|
||||
|
||||
在每个用例的 `withTempEnv` 回调开头增加 `process.env.DATA_DIR = dataDir`,使脚本在任意平台(含 Windows)都使用测试临时目录,而不是依赖 `HOME`/`APPDATA`。
|
||||
|
||||
### 修复 7:domain-persistence 测试(tests/unit/domain-persistence.test.mjs)
|
||||
|
||||
- **单例 tmpDir**:全文件共用一个 `fileTmpDir`,在模块加载时创建并设置 `process.env.DATA_DIR`,与 `core` 首次加载时缓存的路径一致。
|
||||
- **每测清 DB 不清目录**:`beforeEach` 中 `resetDbInstance()` 后删除 `storage.sqlite` 及其 `-wal`/`-shm`/`-journal`,保证每测干净 DB,不在 afterEach 删目录,避免路径失效。
|
||||
- **收尾**:`after()` 中恢复 `DATA_DIR` 并删除 `fileTmpDir`。
|
||||
- **costRules 断言**:改为小容差精确校验(`assertAlmostEqual`),继续验证 `4.5` / `4.0` 这类业务关键值,避免把真实累计错误放过去。
|
||||
|
||||
### 修复 8:fixes-p1 测试(tests/unit/fixes-p1.test.mjs)
|
||||
|
||||
- **restoreDbBackup 用例**:在写入 stale 侧文件前调用 `core.resetDbInstance()`,避免 DB 仍打开时写 `-wal`/`-shm` 触发 Windows 锁错误。
|
||||
- **Windows 跳过**:该用例在 Windows 上仍使用 `test(..., { skip: isWindows })`。原因不是业务逻辑不支持 Windows,而是 better-sqlite3 关闭后底层句柄释放存在时序抖动,这条真实 sidecar 集成测试容易退化成不稳定的文件锁测试;Linux/macOS 上照常运行。
|
||||
- **核心兜底测试**:新增平台无关的 `unlinkFileWithRetry` 单测,直接模拟 `EBUSY` / `EPERM` 后重试并最终成功,确保 Windows 相关的重试删除逻辑被稳定覆盖,而不是完全依赖 flaky 的真实文件锁时序。
|
||||
- **resetStorage**:改为 async,对 `rmSync(TEST_DATA_DIR)` 做最多 10 次、间隔 100ms 的 EBUSY/EPERM 重试,避免下一测因上一测句柄未释放而失败。
|
||||
|
||||
### 修复 9:备份恢复逻辑(src/lib/db/backup.ts)
|
||||
|
||||
- **pre-restore 备份改为同步等待**:在 `restoreDbBackup` 内用内联逻辑做 pre-restore 备份并 `await` 完成,再调用 `resetDbInstance()`,避免异步 backup 未结束就关库导致后续 unlink 失败。
|
||||
- **节流语义保持一致**:pre-restore 备份成功后补回 `_lastBackupAt = Date.now()`,避免恢复后紧接着又触发一轮额外自动备份。
|
||||
- **关库后短延迟**:`resetDbInstance()` 后 `await new Promise(r => setTimeout(r, 500))`,再执行 unlink,给 Windows 等平台释放句柄留时间。
|
||||
- **unlink 重试**:将主库及 `-wal`/`-shm`/`-journal` 的删除提取为 `unlinkFileWithRetry`,统一做最多 10 次、间隔 100ms 的 EBUSY/EPERM 重试,提高恢复流程在锁释放较慢环境下的成功率,也便于单测直接覆盖重试逻辑。
|
||||
|
||||
### 涉及文件(本节)
|
||||
|
||||
| 区域 | 文件 | 改动类型 |
|
||||
| -------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
||||
| 单元测试 | `tests/unit/bootstrap-env.test.mjs` | 各用例内设置 `process.env.DATA_DIR = dataDir` |
|
||||
| 单元测试 | `tests/unit/domain-persistence.test.mjs` | 单例 tmpDir、beforeEach 清 DB 文件、after 删目录;costRules 改为小容差精确断言 |
|
||||
| 单元测试 | `tests/unit/fixes-p1.test.mjs` | restoreDbBackup 前 resetDbInstance、Windows skip 说明、resetStorage 重试、`unlinkFileWithRetry` 核心单测 |
|
||||
| 备份恢复 | `src/lib/db/backup.ts` | pre-restore 内联并 await、恢复 `_lastBackupAt` 节流语义、关库后 500ms 延迟、抽取 `unlinkFileWithRetry` 重试删除 |
|
||||
@@ -1,6 +1,6 @@
|
||||
# API Reference
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](i18n/es/API_REFERENCE.md) | 🇫🇷 [Français](i18n/fr/API_REFERENCE.md) | 🇮🇹 [Italiano](i18n/it/API_REFERENCE.md) | 🇷🇺 [Русский](i18n/ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](i18n/de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](i18n/in/API_REFERENCE.md) | 🇹🇭 [ไทย](i18n/th/API_REFERENCE.md) | 🇺🇦 [Українська](i18n/uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](i18n/ar/API_REFERENCE.md) | 🇯🇵 [日本語](i18n/ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/API_REFERENCE.md) | 🇧🇬 [Български](i18n/bg/API_REFERENCE.md) | 🇩🇰 [Dansk](i18n/da/API_REFERENCE.md) | 🇫🇮 [Suomi](i18n/fi/API_REFERENCE.md) | 🇮🇱 [עברית](i18n/he/API_REFERENCE.md) | 🇭🇺 [Magyar](i18n/hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/API_REFERENCE.md) | 🇰🇷 [한국어](i18n/ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](i18n/nl/API_REFERENCE.md) | 🇳🇴 [Norsk](i18n/no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/API_REFERENCE.md) | 🇷🇴 [Română](i18n/ro/API_REFERENCE.md) | 🇵🇱 [Polski](i18n/pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](i18n/sk/API_REFERENCE.md) | 🇸🇪 [Svenska](i18n/sv/API_REFERENCE.md) | 🇵🇭 [Filipino](i18n/phi/API_REFERENCE.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](i18n/es/API_REFERENCE.md) | 🇫🇷 [Français](i18n/fr/API_REFERENCE.md) | 🇮🇹 [Italiano](i18n/it/API_REFERENCE.md) | 🇷🇺 [Русский](i18n/ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](i18n/de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](i18n/in/API_REFERENCE.md) | 🇹🇭 [ไทย](i18n/th/API_REFERENCE.md) | 🇺🇦 [Українська](i18n/uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](i18n/ar/API_REFERENCE.md) | 🇯🇵 [日本語](i18n/ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/API_REFERENCE.md) | 🇧🇬 [Български](i18n/bg/API_REFERENCE.md) | 🇩🇰 [Dansk](i18n/da/API_REFERENCE.md) | 🇫🇮 [Suomi](i18n/fi/API_REFERENCE.md) | 🇮🇱 [עברית](i18n/he/API_REFERENCE.md) | 🇭🇺 [Magyar](i18n/hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/API_REFERENCE.md) | 🇰🇷 [한국어](i18n/ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](i18n/nl/API_REFERENCE.md) | 🇳🇴 [Norsk](i18n/no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/API_REFERENCE.md) | 🇷🇴 [Română](i18n/ro/API_REFERENCE.md) | 🇵🇱 [Polski](i18n/pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](i18n/sk/API_REFERENCE.md) | 🇸🇪 [Svenska](i18n/sv/API_REFERENCE.md) | 🇵🇭 [Filipino](i18n/phi/API_REFERENCE.md) | 🇨🇿 [Čeština](i18n/cs/API_REFERENCE.md)
|
||||
|
||||
Complete reference for all OmniRoute API endpoints.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OmniRoute Architecture
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](i18n/es/ARCHITECTURE.md) | 🇫🇷 [Français](i18n/fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](i18n/it/ARCHITECTURE.md) | 🇷🇺 [Русский](i18n/ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](i18n/de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](i18n/in/ARCHITECTURE.md) | 🇹🇭 [ไทย](i18n/th/ARCHITECTURE.md) | 🇺🇦 [Українська](i18n/uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](i18n/ar/ARCHITECTURE.md) | 🇯🇵 [日本語](i18n/ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/ARCHITECTURE.md) | 🇧🇬 [Български](i18n/bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](i18n/da/ARCHITECTURE.md) | 🇫🇮 [Suomi](i18n/fi/ARCHITECTURE.md) | 🇮🇱 [עברית](i18n/he/ARCHITECTURE.md) | 🇭🇺 [Magyar](i18n/hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/ARCHITECTURE.md) | 🇰🇷 [한국어](i18n/ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](i18n/nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](i18n/no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/ARCHITECTURE.md) | 🇷🇴 [Română](i18n/ro/ARCHITECTURE.md) | 🇵🇱 [Polski](i18n/pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](i18n/sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](i18n/sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](i18n/phi/ARCHITECTURE.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](i18n/es/ARCHITECTURE.md) | 🇫🇷 [Français](i18n/fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](i18n/it/ARCHITECTURE.md) | 🇷🇺 [Русский](i18n/ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](i18n/de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](i18n/in/ARCHITECTURE.md) | 🇹🇭 [ไทย](i18n/th/ARCHITECTURE.md) | 🇺🇦 [Українська](i18n/uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](i18n/ar/ARCHITECTURE.md) | 🇯🇵 [日本語](i18n/ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/ARCHITECTURE.md) | 🇧🇬 [Български](i18n/bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](i18n/da/ARCHITECTURE.md) | 🇫🇮 [Suomi](i18n/fi/ARCHITECTURE.md) | 🇮🇱 [עברית](i18n/he/ARCHITECTURE.md) | 🇭🇺 [Magyar](i18n/hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/ARCHITECTURE.md) | 🇰🇷 [한국어](i18n/ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](i18n/nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](i18n/no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/ARCHITECTURE.md) | 🇷🇴 [Română](i18n/ro/ARCHITECTURE.md) | 🇵🇱 [Polski](i18n/pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](i18n/sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](i18n/sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](i18n/phi/ARCHITECTURE.md) | 🇨🇿 [Čeština](i18n/cs/ARCHITECTURE.md)
|
||||
|
||||
_Last updated: 2026-03-04_
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# omniroute — Codebase Documentation
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](i18n/es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](i18n/fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](i18n/it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](i18n/ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](i18n/de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](i18n/in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](i18n/th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](i18n/uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](i18n/ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](i18n/ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](i18n/vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](i18n/bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](i18n/da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](i18n/fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](i18n/he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](i18n/hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](i18n/ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](i18n/nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](i18n/no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](i18n/pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](i18n/ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](i18n/pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](i18n/sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](i18n/sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](i18n/phi/CODEBASE_DOCUMENTATION.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](i18n/es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](i18n/fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](i18n/it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](i18n/ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](i18n/de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](i18n/in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](i18n/th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](i18n/uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](i18n/ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](i18n/ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](i18n/vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](i18n/bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](i18n/da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](i18n/fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](i18n/he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](i18n/hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](i18n/ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](i18n/nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](i18n/no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](i18n/pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](i18n/ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](i18n/pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](i18n/sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](i18n/sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](i18n/phi/CODEBASE_DOCUMENTATION.md) | 🇨🇿 [Čeština](i18n/cs/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> A comprehensive, beginner-friendly guide to the **omniroute** multi-provider AI proxy router.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# OmniRoute — Dashboard Features Gallery
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](FEATURES.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/FEATURES.md) | 🇪🇸 [Español](i18n/es/FEATURES.md) | 🇫🇷 [Français](i18n/fr/FEATURES.md) | 🇮🇹 [Italiano](i18n/it/FEATURES.md) | 🇷🇺 [Русский](i18n/ru/FEATURES.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](i18n/de/FEATURES.md) | 🇮🇳 [हिन्दी](i18n/in/FEATURES.md) | 🇹🇭 [ไทย](i18n/th/FEATURES.md) | 🇺🇦 [Українська](i18n/uk-UA/FEATURES.md) | 🇸🇦 [العربية](i18n/ar/FEATURES.md) | 🇯🇵 [日本語](i18n/ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](i18n/vi/FEATURES.md) | 🇧🇬 [Български](i18n/bg/FEATURES.md) | 🇩🇰 [Dansk](i18n/da/FEATURES.md) | 🇫🇮 [Suomi](i18n/fi/FEATURES.md) | 🇮🇱 [עברית](i18n/he/FEATURES.md) | 🇭🇺 [Magyar](i18n/hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/FEATURES.md) | 🇰🇷 [한국어](i18n/ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/FEATURES.md) | 🇳🇱 [Nederlands](i18n/nl/FEATURES.md) | 🇳🇴 [Norsk](i18n/no/FEATURES.md) | 🇵🇹 [Português (Portugal)](i18n/pt/FEATURES.md) | 🇷🇴 [Română](i18n/ro/FEATURES.md) | 🇵🇱 [Polski](i18n/pl/FEATURES.md) | 🇸🇰 [Slovenčina](i18n/sk/FEATURES.md) | 🇸🇪 [Svenska](i18n/sv/FEATURES.md) | 🇵🇭 [Filipino](i18n/phi/FEATURES.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](FEATURES.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/FEATURES.md) | 🇪🇸 [Español](i18n/es/FEATURES.md) | 🇫🇷 [Français](i18n/fr/FEATURES.md) | 🇮🇹 [Italiano](i18n/it/FEATURES.md) | 🇷🇺 [Русский](i18n/ru/FEATURES.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](i18n/de/FEATURES.md) | 🇮🇳 [हिन्दी](i18n/in/FEATURES.md) | 🇹🇭 [ไทย](i18n/th/FEATURES.md) | 🇺🇦 [Українська](i18n/uk-UA/FEATURES.md) | 🇸🇦 [العربية](i18n/ar/FEATURES.md) | 🇯🇵 [日本語](i18n/ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](i18n/vi/FEATURES.md) | 🇧🇬 [Български](i18n/bg/FEATURES.md) | 🇩🇰 [Dansk](i18n/da/FEATURES.md) | 🇫🇮 [Suomi](i18n/fi/FEATURES.md) | 🇮🇱 [עברית](i18n/he/FEATURES.md) | 🇭🇺 [Magyar](i18n/hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/FEATURES.md) | 🇰🇷 [한국어](i18n/ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/FEATURES.md) | 🇳🇱 [Nederlands](i18n/nl/FEATURES.md) | 🇳🇴 [Norsk](i18n/no/FEATURES.md) | 🇵🇹 [Português (Portugal)](i18n/pt/FEATURES.md) | 🇷🇴 [Română](i18n/ro/FEATURES.md) | 🇵🇱 [Polski](i18n/pl/FEATURES.md) | 🇸🇰 [Slovenčina](i18n/sk/FEATURES.md) | 🇸🇪 [Svenska](i18n/sv/FEATURES.md) | 🇵🇭 [Filipino](i18n/phi/FEATURES.md) | 🇨🇿 [Čeština](i18n/cs/FEATURES.md)
|
||||
|
||||
Visual guide to every section of the OmniRoute dashboard.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Troubleshooting
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](i18n/es/TROUBLESHOOTING.md) | 🇫🇷 [Français](i18n/fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](i18n/it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](i18n/ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](i18n/de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](i18n/in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](i18n/th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](i18n/uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](i18n/ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](i18n/ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](i18n/vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](i18n/bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](i18n/da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](i18n/fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](i18n/he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](i18n/hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](i18n/ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](i18n/nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](i18n/no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](i18n/pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](i18n/ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](i18n/pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](i18n/sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](i18n/sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](i18n/phi/TROUBLESHOOTING.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](i18n/es/TROUBLESHOOTING.md) | 🇫🇷 [Français](i18n/fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](i18n/it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](i18n/ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](i18n/de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](i18n/in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](i18n/th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](i18n/uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](i18n/ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](i18n/ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](i18n/vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](i18n/bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](i18n/da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](i18n/fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](i18n/he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](i18n/hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](i18n/ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](i18n/nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](i18n/no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](i18n/pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](i18n/ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](i18n/pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](i18n/sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](i18n/sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](i18n/phi/TROUBLESHOOTING.md) | 🇨🇿 [Čeština](i18n/cs/TROUBLESHOOTING.md)
|
||||
|
||||
Common problems and solutions for OmniRoute.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# User Guide
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](i18n/es/USER_GUIDE.md) | 🇫🇷 [Français](i18n/fr/USER_GUIDE.md) | 🇮🇹 [Italiano](i18n/it/USER_GUIDE.md) | 🇷🇺 [Русский](i18n/ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](i18n/de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](i18n/in/USER_GUIDE.md) | 🇹🇭 [ไทย](i18n/th/USER_GUIDE.md) | 🇺🇦 [Українська](i18n/uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](i18n/ar/USER_GUIDE.md) | 🇯🇵 [日本語](i18n/ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/USER_GUIDE.md) | 🇧🇬 [Български](i18n/bg/USER_GUIDE.md) | 🇩🇰 [Dansk](i18n/da/USER_GUIDE.md) | 🇫🇮 [Suomi](i18n/fi/USER_GUIDE.md) | 🇮🇱 [עברית](i18n/he/USER_GUIDE.md) | 🇭🇺 [Magyar](i18n/hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/USER_GUIDE.md) | 🇰🇷 [한국어](i18n/ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](i18n/nl/USER_GUIDE.md) | 🇳🇴 [Norsk](i18n/no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/USER_GUIDE.md) | 🇷🇴 [Română](i18n/ro/USER_GUIDE.md) | 🇵🇱 [Polski](i18n/pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](i18n/sk/USER_GUIDE.md) | 🇸🇪 [Svenska](i18n/sv/USER_GUIDE.md) | 🇵🇭 [Filipino](i18n/phi/USER_GUIDE.md)
|
||||
🌐 **Languages:** 🇺🇸 [English](USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](i18n/es/USER_GUIDE.md) | 🇫🇷 [Français](i18n/fr/USER_GUIDE.md) | 🇮🇹 [Italiano](i18n/it/USER_GUIDE.md) | 🇷🇺 [Русский](i18n/ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](i18n/de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](i18n/in/USER_GUIDE.md) | 🇹🇭 [ไทย](i18n/th/USER_GUIDE.md) | 🇺🇦 [Українська](i18n/uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](i18n/ar/USER_GUIDE.md) | 🇯🇵 [日本語](i18n/ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/USER_GUIDE.md) | 🇧🇬 [Български](i18n/bg/USER_GUIDE.md) | 🇩🇰 [Dansk](i18n/da/USER_GUIDE.md) | 🇫🇮 [Suomi](i18n/fi/USER_GUIDE.md) | 🇮🇱 [עברית](i18n/he/USER_GUIDE.md) | 🇭🇺 [Magyar](i18n/hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/USER_GUIDE.md) | 🇰🇷 [한국어](i18n/ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](i18n/nl/USER_GUIDE.md) | 🇳🇴 [Norsk](i18n/no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/USER_GUIDE.md) | 🇷🇴 [Română](i18n/ro/USER_GUIDE.md) | 🇵🇱 [Polski](i18n/pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](i18n/sk/USER_GUIDE.md) | 🇸🇪 [Svenska](i18n/sv/USER_GUIDE.md) | 🇵🇭 [Filipino](i18n/phi/USER_GUIDE.md) | 🇨🇿 [Čeština](i18n/cs/USER_GUIDE.md)
|
||||
|
||||
Complete guide for configuring providers, creating combos, integrating CLI tools, and deploying OmniRoute.
|
||||
|
||||
|
||||
@@ -1,69 +1,71 @@
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
# OmniRoute — Deployment Guide on VM with Cloudflare
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
🌐 **Languages:** 🇺🇸 [English](VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](i18n/pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](i18n/es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](i18n/fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](i18n/it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](i18n/ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](i18n/zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](i18n/de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](i18n/in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](i18n/th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](i18n/uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](i18n/ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](i18n/ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](i18n/vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](i18n/bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](i18n/da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](i18n/fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](i18n/he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](i18n/hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](i18n/id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](i18n/ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](i18n/nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](i18n/no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](i18n/pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](i18n/ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](i18n/pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](i18n/sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](i18n/sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](i18n/phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](i18n/cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Complete guide to install and configure OmniRoute on a VM (VPS) with domain managed via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## Prerequisites
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
| Item | Minimum | Recommended |
|
||||
| ---------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Registered on Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
**Tested providers**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## 1. Configurar a VM
|
||||
## 1. Configure the VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
### 1.1 Create the instance
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
On your preferred VPS provider:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
- Choose Ubuntu 24.04 LTS
|
||||
- Select the minimum plan (1 vCPU / 1 GB RAM)
|
||||
- Set a strong root password or configure SSH key
|
||||
- Note the **public IP** (e.g., `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Connect via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Update the system
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Install Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Install nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Configure Firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -74,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tip**: For maximum security, restrict ports 80 and 443 to Cloudflare IPs only. See the [Advanced Security](#advanced-security) section.
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Install OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Create configuration directory
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Create environment variables file
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -108,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANT**: Generate unique secret keys! Use `openssl rand -hex 32` for each key.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Start the container
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -134,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verify that it is running
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
It should display: `[DB] SQLite database ready` and `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configure nginx (Reverse Proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generate SSL certificate (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
In the Cloudflare dashboard:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Go to **SSL/TLS → Origin Server**
|
||||
2. Click **Create Certificate**
|
||||
3. Keep the defaults (15 years, \*.yourdomain.com)
|
||||
4. Copy the **Origin Certificate** and the **Private Key**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx Configuration
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -188,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -206,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -220,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Enable and Test
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configure Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Add DNS record
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
In the Cloudflare dashboard → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ---------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Proxied |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Configure SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Under **SSL/TLS → Overview**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mode: **Full (Strict)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Under **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testing
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Operations and Maintenance
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Upgrade to a new version
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -286,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### View logs
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manual database backup
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restore from backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Advanced Security
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restrict nginx to Cloudflare IPs
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -342,7 +344,7 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Add the following to `nginx.conf` inside the `http {}` block:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
@@ -355,45 +357,45 @@ apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Block direct access to the Docker port
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Deploy to Cloudflare Workers (Optional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
For remote access via Cloudflare Workers (without exposing the VM directly):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
See the full documentation at [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Port Summary
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Service | Access |
|
||||
| ----- | ----------- | -------------------------- |
|
||||
| 22 | SSH | Public (with fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Localhost only (via nginx) |
|
||||
|
||||
@@ -2,20 +2,12 @@
|
||||
|
||||
This directory contains machine-assisted translations based on the English docs.
|
||||
|
||||
- **API_REFERENCE.md**: 🇺🇸 [English](../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](./es/API_REFERENCE.md) | 🇫🇷 [Français](./fr/API_REFERENCE.md) | 🇮🇹 [Italiano](./it/API_REFERENCE.md) | 🇷🇺 [Русский](./ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](./zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](./de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](./in/API_REFERENCE.md) | 🇹🇭 [ไทย](./th/API_REFERENCE.md) | 🇺🇦 [Українська](./uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](./ar/API_REFERENCE.md) | 🇯🇵 [日本語](./ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](./vi/API_REFERENCE.md) | 🇧🇬 [Български](./bg/API_REFERENCE.md) | 🇩🇰 [Dansk](./da/API_REFERENCE.md) | 🇫🇮 [Suomi](./fi/API_REFERENCE.md) | 🇮🇱 [עברית](./he/API_REFERENCE.md) | 🇭🇺 [Magyar](./hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](./id/API_REFERENCE.md) | 🇰🇷 [한국어](./ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](./ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](./nl/API_REFERENCE.md) | 🇳🇴 [Norsk](./no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](./pt/API_REFERENCE.md) | 🇷🇴 [Română](./ro/API_REFERENCE.md) | 🇵🇱 [Polski](./pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](./sk/API_REFERENCE.md) | 🇸🇪 [Svenska](./sv/API_REFERENCE.md) | 🇵🇭 [Filipino](./phi/API_REFERENCE.md)
|
||||
- **ARCHITECTURE.md**: 🇺🇸 [English](../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](./es/ARCHITECTURE.md) | 🇫🇷 [Français](./fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](./it/ARCHITECTURE.md) | 🇷🇺 [Русский](./ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](./zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](./de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](./in/ARCHITECTURE.md) | 🇹🇭 [ไทย](./th/ARCHITECTURE.md) | 🇺🇦 [Українська](./uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](./ar/ARCHITECTURE.md) | 🇯🇵 [日本語](./ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](./vi/ARCHITECTURE.md) | 🇧🇬 [Български](./bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](./da/ARCHITECTURE.md) | 🇫🇮 [Suomi](./fi/ARCHITECTURE.md) | 🇮🇱 [עברית](./he/ARCHITECTURE.md) | 🇭🇺 [Magyar](./hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](./id/ARCHITECTURE.md) | 🇰🇷 [한국어](./ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](./ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](./nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](./no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](./pt/ARCHITECTURE.md) | 🇷🇴 [Română](./ro/ARCHITECTURE.md) | 🇵🇱 [Polski](./pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](./sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](./sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](./phi/ARCHITECTURE.md)
|
||||
- **CODEBASE_DOCUMENTATION.md**: 🇺🇸 [English](../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](./pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](./es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](./fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](./it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](./ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](./zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](./de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](./in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](./th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](./uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](./ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](./ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](./vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](./bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](./da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](./fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](./he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](./hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](./id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](./ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](./ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](./nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](./no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](./pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](./ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](./pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](./sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](./sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](./phi/CODEBASE_DOCUMENTATION.md)
|
||||
- **FEATURES.md**: 🇺🇸 [English](../FEATURES.md) | 🇧🇷 [Português (Brasil)](./pt-BR/FEATURES.md) | 🇪🇸 [Español](./es/FEATURES.md) | 🇫🇷 [Français](./fr/FEATURES.md) | 🇮🇹 [Italiano](./it/FEATURES.md) | 🇷🇺 [Русский](./ru/FEATURES.md) | 🇨🇳 [中文 (简体)](./zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](./de/FEATURES.md) | 🇮🇳 [हिन्दी](./in/FEATURES.md) | 🇹🇭 [ไทย](./th/FEATURES.md) | 🇺🇦 [Українська](./uk-UA/FEATURES.md) | 🇸🇦 [العربية](./ar/FEATURES.md) | 🇯🇵 [日本語](./ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](./vi/FEATURES.md) | 🇧🇬 [Български](./bg/FEATURES.md) | 🇩🇰 [Dansk](./da/FEATURES.md) | 🇫🇮 [Suomi](./fi/FEATURES.md) | 🇮🇱 [עברית](./he/FEATURES.md) | 🇭🇺 [Magyar](./hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](./id/FEATURES.md) | 🇰🇷 [한국어](./ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](./ms/FEATURES.md) | 🇳🇱 [Nederlands](./nl/FEATURES.md) | 🇳🇴 [Norsk](./no/FEATURES.md) | 🇵🇹 [Português (Portugal)](./pt/FEATURES.md) | 🇷🇴 [Română](./ro/FEATURES.md) | 🇵🇱 [Polski](./pl/FEATURES.md) | 🇸🇰 [Slovenčina](./sk/FEATURES.md) | 🇸🇪 [Svenska](./sv/FEATURES.md) | 🇵🇭 [Filipino](./phi/FEATURES.md)
|
||||
- **TROUBLESHOOTING.md**: 🇺🇸 [English](../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](./pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](./es/TROUBLESHOOTING.md) | 🇫🇷 [Français](./fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](./it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](./ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](./zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](./de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](./in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](./th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](./uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](./ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](./ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](./vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](./bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](./da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](./fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](./he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](./hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](./id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](./ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](./ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](./nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](./no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](./pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](./ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](./pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](./sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](./sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](./phi/TROUBLESHOOTING.md)
|
||||
- **USER_GUIDE.md**: 🇺🇸 [English](../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](./es/USER_GUIDE.md) | 🇫🇷 [Français](./fr/USER_GUIDE.md) | 🇮🇹 [Italiano](./it/USER_GUIDE.md) | 🇷🇺 [Русский](./ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](./zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](./de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](./in/USER_GUIDE.md) | 🇹🇭 [ไทย](./th/USER_GUIDE.md) | 🇺🇦 [Українська](./uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](./ar/USER_GUIDE.md) | 🇯🇵 [日本語](./ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](./vi/USER_GUIDE.md) | 🇧🇬 [Български](./bg/USER_GUIDE.md) | 🇩🇰 [Dansk](./da/USER_GUIDE.md) | 🇫🇮 [Suomi](./fi/USER_GUIDE.md) | 🇮🇱 [עברית](./he/USER_GUIDE.md) | 🇭🇺 [Magyar](./hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](./id/USER_GUIDE.md) | 🇰🇷 [한국어](./ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](./ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](./nl/USER_GUIDE.md) | 🇳🇴 [Norsk](./no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](./pt/USER_GUIDE.md) | 🇷🇴 [Română](./ro/USER_GUIDE.md) | 🇵🇱 [Polski](./pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](./sk/USER_GUIDE.md) | 🇸🇪 [Svenska](./sv/USER_GUIDE.md) | 🇵🇭 [Filipino](./phi/USER_GUIDE.md)
|
||||
- **API_REFERENCE.md**: 🇺🇸 [English](../API_REFERENCE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](./es/API_REFERENCE.md) | 🇫🇷 [Français](./fr/API_REFERENCE.md) | 🇮🇹 [Italiano](./it/API_REFERENCE.md) | 🇷🇺 [Русский](./ru/API_REFERENCE.md) | 🇨🇳 [中文 (简体)](./zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](./de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](./in/API_REFERENCE.md) | 🇹🇭 [ไทย](./th/API_REFERENCE.md) | 🇺🇦 [Українська](./uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](./ar/API_REFERENCE.md) | 🇯🇵 [日本語](./ja/API_REFERENCE.md) | 🇻🇳 [Tiếng Việt](./vi/API_REFERENCE.md) | 🇧🇬 [Български](./bg/API_REFERENCE.md) | 🇩🇰 [Dansk](./da/API_REFERENCE.md) | 🇫🇮 [Suomi](./fi/API_REFERENCE.md) | 🇮🇱 [עברית](./he/API_REFERENCE.md) | 🇭🇺 [Magyar](./hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonesia](./id/API_REFERENCE.md) | 🇰🇷 [한국어](./ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](./ms/API_REFERENCE.md) | 🇳🇱 [Nederlands](./nl/API_REFERENCE.md) | 🇳🇴 [Norsk](./no/API_REFERENCE.md) | 🇵🇹 [Português (Portugal)](./pt/API_REFERENCE.md) | 🇷🇴 [Română](./ro/API_REFERENCE.md) | 🇵🇱 [Polski](./pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](./sk/API_REFERENCE.md) | 🇸🇪 [Svenska](./sv/API_REFERENCE.md) | 🇵🇭 [Filipino](./phi/API_REFERENCE.md) | 🇨🇿 [Čeština](./cs/API_REFERENCE.md)
|
||||
- **ARCHITECTURE.md**: 🇺🇸 [English](../ARCHITECTURE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](./es/ARCHITECTURE.md) | 🇫🇷 [Français](./fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](./it/ARCHITECTURE.md) | 🇷🇺 [Русский](./ru/ARCHITECTURE.md) | 🇨🇳 [中文 (简体)](./zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](./de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](./in/ARCHITECTURE.md) | 🇹🇭 [ไทย](./th/ARCHITECTURE.md) | 🇺🇦 [Українська](./uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](./ar/ARCHITECTURE.md) | 🇯🇵 [日本語](./ja/ARCHITECTURE.md) | 🇻🇳 [Tiếng Việt](./vi/ARCHITECTURE.md) | 🇧🇬 [Български](./bg/ARCHITECTURE.md) | 🇩🇰 [Dansk](./da/ARCHITECTURE.md) | 🇫🇮 [Suomi](./fi/ARCHITECTURE.md) | 🇮🇱 [עברית](./he/ARCHITECTURE.md) | 🇭🇺 [Magyar](./hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonesia](./id/ARCHITECTURE.md) | 🇰🇷 [한국어](./ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](./ms/ARCHITECTURE.md) | 🇳🇱 [Nederlands](./nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](./no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugal)](./pt/ARCHITECTURE.md) | 🇷🇴 [Română](./ro/ARCHITECTURE.md) | 🇵🇱 [Polski](./pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](./sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](./sv/ARCHITECTURE.md) | 🇵🇭 [Filipino](./phi/ARCHITECTURE.md) | 🇨🇿 [Čeština](./cs/ARCHITECTURE.md)
|
||||
- **CODEBASE_DOCUMENTATION.md**: 🇺🇸 [English](../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brasil)](./pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](./es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](./fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](./it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](./ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳 [中文 (简体)](./zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](./de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](./in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](./th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](./uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](./ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵 [日本語](./ja/CODEBASE_DOCUMENTATION.md) | 🇻🇳 [Tiếng Việt](./vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](./bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dansk](./da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](./fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](./he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [Magyar](./hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonesia](./id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](./ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](./ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nederlands](./nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](./no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugal)](./pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](./ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](./pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](./sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](./sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipino](./phi/CODEBASE_DOCUMENTATION.md) | 🇨🇿 [Čeština](./cs/CODEBASE_DOCUMENTATION.md)
|
||||
- **FEATURES.md**: 🇺🇸 [English](../FEATURES.md) | 🇧🇷 [Português (Brasil)](./pt-BR/FEATURES.md) | 🇪🇸 [Español](./es/FEATURES.md) | 🇫🇷 [Français](./fr/FEATURES.md) | 🇮🇹 [Italiano](./it/FEATURES.md) | 🇷🇺 [Русский](./ru/FEATURES.md) | 🇨🇳 [中文 (简体)](./zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](./de/FEATURES.md) | 🇮🇳 [हिन्दी](./in/FEATURES.md) | 🇹🇭 [ไทย](./th/FEATURES.md) | 🇺🇦 [Українська](./uk-UA/FEATURES.md) | 🇸🇦 [العربية](./ar/FEATURES.md) | 🇯🇵 [日本語](./ja/FEATURES.md) | 🇻🇳 [Tiếng Việt](./vi/FEATURES.md) | 🇧🇬 [Български](./bg/FEATURES.md) | 🇩🇰 [Dansk](./da/FEATURES.md) | 🇫🇮 [Suomi](./fi/FEATURES.md) | 🇮🇱 [עברית](./he/FEATURES.md) | 🇭🇺 [Magyar](./hu/FEATURES.md) | 🇮🇩 [Bahasa Indonesia](./id/FEATURES.md) | 🇰🇷 [한국어](./ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](./ms/FEATURES.md) | 🇳🇱 [Nederlands](./nl/FEATURES.md) | 🇳🇴 [Norsk](./no/FEATURES.md) | 🇵🇹 [Português (Portugal)](./pt/FEATURES.md) | 🇷🇴 [Română](./ro/FEATURES.md) | 🇵🇱 [Polski](./pl/FEATURES.md) | 🇸🇰 [Slovenčina](./sk/FEATURES.md) | 🇸🇪 [Svenska](./sv/FEATURES.md) | 🇵🇭 [Filipino](./phi/FEATURES.md) | 🇨🇿 [Čeština](./cs/FEATURES.md)
|
||||
- **TROUBLESHOOTING.md**: 🇺🇸 [English](../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brasil)](./pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](./es/TROUBLESHOOTING.md) | 🇫🇷 [Français](./fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](./it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](./ru/TROUBLESHOOTING.md) | 🇨🇳 [中文 (简体)](./zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](./de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](./in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](./th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](./uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](./ar/TROUBLESHOOTING.md) | 🇯🇵 [日本語](./ja/TROUBLESHOOTING.md) | 🇻🇳 [Tiếng Việt](./vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](./bg/TROUBLESHOOTING.md) | 🇩🇰 [Dansk](./da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](./fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](./he/TROUBLESHOOTING.md) | 🇭🇺 [Magyar](./hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonesia](./id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](./ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](./ms/TROUBLESHOOTING.md) | 🇳🇱 [Nederlands](./nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](./no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugal)](./pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](./ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](./pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](./sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](./sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipino](./phi/TROUBLESHOOTING.md) | 🇨🇿 [Čeština](./cs/TROUBLESHOOTING.md)
|
||||
- **USER_GUIDE.md**: 🇺🇸 [English](../USER_GUIDE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](./es/USER_GUIDE.md) | 🇫🇷 [Français](./fr/USER_GUIDE.md) | 🇮🇹 [Italiano](./it/USER_GUIDE.md) | 🇷🇺 [Русский](./ru/USER_GUIDE.md) | 🇨🇳 [中文 (简体)](./zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](./de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](./in/USER_GUIDE.md) | 🇹🇭 [ไทย](./th/USER_GUIDE.md) | 🇺🇦 [Українська](./uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](./ar/USER_GUIDE.md) | 🇯🇵 [日本語](./ja/USER_GUIDE.md) | 🇻🇳 [Tiếng Việt](./vi/USER_GUIDE.md) | 🇧🇬 [Български](./bg/USER_GUIDE.md) | 🇩🇰 [Dansk](./da/USER_GUIDE.md) | 🇫🇮 [Suomi](./fi/USER_GUIDE.md) | 🇮🇱 [עברית](./he/USER_GUIDE.md) | 🇭🇺 [Magyar](./hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](./id/USER_GUIDE.md) | 🇰🇷 [한국어](./ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](./ms/USER_GUIDE.md) | 🇳🇱 [Nederlands](./nl/USER_GUIDE.md) | 🇳🇴 [Norsk](./no/USER_GUIDE.md) | 🇵🇹 [Português (Portugal)](./pt/USER_GUIDE.md) | 🇷🇴 [Română](./ro/USER_GUIDE.md) | 🇵🇱 [Polski](./pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](./sk/USER_GUIDE.md) | 🇸🇪 [Svenska](./sv/USER_GUIDE.md) | 🇵🇭 [Filipino](./phi/USER_GUIDE.md) | 🇨🇿 [Čeština](./cs/USER_GUIDE.md)
|
||||
- **VM_DEPLOYMENT_GUIDE.md**: 🇺🇸 [English](../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](./pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](./es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](./fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](./it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](./ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](./zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](./de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](./in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](./th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](./uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](./ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](./ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](./vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](./bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](./da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](./fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](./he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](./hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](./id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](./ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](./ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](./nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](./no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](./pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](./ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](./pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](./sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](./sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](./phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](./cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
## Recent note: Codex account limit policy
|
||||
|
||||
Documentation now includes Codex account-level quota policy behavior:
|
||||
|
||||
- Per-account toggles: `5h` and `Weekly` (ON/OFF).
|
||||
- Threshold policy: enabled window reaching >=90% marks account as ineligible for selection.
|
||||
- Auto-rotation: traffic moves to the next eligible Codex account.
|
||||
- Auto-reuse: account becomes eligible again after provider `resetAt` passes.
|
||||
|
||||
Generated on 2026-02-26.
|
||||
Generated on 2026-03-19.
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — دليل النشر على VM باستخدام Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
الدليل الكامل لتثبيت OmniRoute وتكوينه على VM (VPS) مع المجال المُدار عبر Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## المتطلبات الأساسية
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| العنصر | الحد الأدنى | موصى به |
|
||||
| ---------------------------- | ----------------------------------- | ----------------------------------- |
|
||||
| ** وحدة المعالجة المركزية ** | 1 وحدة المعالجة المركزية الافتراضية | 2 وحدة المعالجة المركزية الافتراضية |
|
||||
| **ذاكرة الوصول العشوائي** | 1 جيجا | 2 جيجا |
|
||||
| **القرص** | 10 جيجا اس اس دي | 25 جيجا اس اس دي |
|
||||
| **نظام التشغيل** | أوبونتو 22.04 LTS | أوبونتو 24.04 LTS |
|
||||
| **المجال** | مسجل في Cloudflare | — |
|
||||
| ** عامل الميناء ** | محرك دوكر 24+ | عامل الميناء 27+ |
|
||||
|
||||
**المزودون الذين تم اختبارهم**: Akamai (Linode)، DigitalOcean، Vultr، Hetzner، AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. قم بتكوين الجهاز الافتراضي
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 إنشاء المثيل
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
على موفر VPS المفضل لديك:
|
||||
|
||||
---
|
||||
- اختر Ubuntu 24.04 LTS
|
||||
- حدد الحد الأدنى للخطة (1 vCPU / 1 جيجابايت من ذاكرة الوصول العشوائي)
|
||||
- قم بتعيين كلمة مرور جذر قوية أو قم بتكوين مفتاح SSH
|
||||
- لاحظ **عنوان IP العام** (على سبيل المثال، `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 الاتصال عبر SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 تحديث النظام
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 تثبيت عامل الميناء
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 تثبيت nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 تكوين جدار الحماية (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **نصيحة**: للحصول على الحد الأقصى من الأمان، قم بتقييد المنفذين 80 و443 بعناوين Cloudflare IP فقط. راجع قسم [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. قم بتثبيت OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 إنشاء دليل التكوين
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 إنشاء ملف متغيرات البيئة
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **هام**: أنشئ مفاتيح سرية فريدة! استخدم `openssl rand -hex 32` لكل مفتاح.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 ابدأ الحاوية
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 التحقق من أنه قيد التشغيل
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
يجب أن يعرض: `[DB] SQLite database ready` و`listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. تكوين nginx (الوكيل العكسي)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 إنشاء شهادة SSL (أصل Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
في لوحة معلومات Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. انتقل إلى **SSL/TLS → خادم الأصل**
|
||||
2. انقر **إنشاء شهادة**
|
||||
3. احتفظ بالإعدادات الافتراضية (15 عامًا، \*.yourdomain.com)
|
||||
4. انسخ **شهادة المنشأ** و**المفتاح الخاص**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 تكوين إنجينكس
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 تمكين واختبار
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. تكوين Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 إضافة سجل DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
في لوحة معلومات Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| اكتب | الاسم | المحتوى | الوكيل |
|
||||
| ---- | ------ | ---------------------- | -------- |
|
||||
| أ | `llms` | `203.0.113.10` (VM IP) | ✅ توكيل |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 تكوين SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
ضمن **SSL/TLS → نظرة عامة**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- الوضع: **كامل (صارم)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
ضمن **SSL/TLS → شهادات الحافة**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- استخدم HTTPS دائمًا: ✅ قيد التشغيل
|
||||
- الحد الأدنى لإصدار TLS: TLS 1.2
|
||||
- إعادة كتابة HTTPS تلقائيًا: ✅ تشغيل
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 الاختبار
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. العمليات والصيانة
|
||||
|
||||
### Atualizar para nova versão
|
||||
### الترقية إلى الإصدار الجديد
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### عرض السجلات
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### النسخ الاحتياطي لقاعدة البيانات يدويا
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### الاستعادة من النسخة الاحتياطية
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. الأمان المتقدم
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### تقييد nginx على عناوين IP الخاصة بـ Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
أضف ما يلي إلى `nginx.conf` داخل الكتلة `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### تثبيت Fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### منع الوصول المباشر إلى منفذ Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. النشر إلى عمال Cloudflare (اختياري)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
للوصول عن بعد عبر Cloudflare Workers (دون الكشف عن الجهاز الافتراضي مباشرة):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
راجع الوثائق الكاملة على [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## ملخص المنفذ
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| ميناء | الخدمة | الوصول |
|
||||
| ----- | ------------- | ----------------------------- |
|
||||
| 22 | سش | عام (مع Fail2ban) |
|
||||
| 80 | إنجينكس HTTP | إعادة التوجيه → HTTPS |
|
||||
| 443 | إنجينكس HTTPS | عبر وكيل Cloudflare |
|
||||
| 20128 | أومنيروتي | المضيف المحلي فقط (عبر nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Ръководство за внедряване на VM с Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Пълно ръководство за инсталиране и конфигуриране на OmniRoute на VM (VPS) с домейн, управляван чрез Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Предпоставки
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Артикул | Минимум | Препоръчва се |
|
||||
| ---------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Диск** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Домейн** | Регистриран в Cloudflare | — |
|
||||
| **Докер** | Docker Engine 24+ | Докер 27+ |
|
||||
|
||||
**Тествани доставчици**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Конфигурирайте VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Създайте екземпляра
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
На предпочитания от вас VPS доставчик:
|
||||
|
||||
---
|
||||
- Изберете Ubuntu 24.04 LTS
|
||||
- Изберете минималния план (1 vCPU / 1 GB RAM)
|
||||
- Задайте силна root парола или конфигурирайте SSH ключ
|
||||
- Обърнете внимание на **публичния IP** (напр. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Свързване чрез SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Актуализирайте системата
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Инсталирайте Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Инсталирайте nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Конфигуриране на защитна стена (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Съвет**: За максимална сигурност ограничете портове 80 и 443 само до IP адреси на Cloudflare. Вижте раздела [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Инсталирайте OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Създайте конфигурационна директория
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Създайте файл с променливи на средата
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **ВАЖНО**: Генерирайте уникални секретни ключове! Използвайте `openssl rand -hex 32` за всеки ключ.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Стартирайте контейнера
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Проверете дали работи
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Трябва да показва: `[DB] SQLite database ready` и `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Конфигурирайте nginx (обратен прокси)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Генериране на SSL сертификат (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
В таблото за управление на Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Отидете на **SSL/TLS → Origin Server**
|
||||
2. Щракнете върху **Създаване на сертификат**
|
||||
3. Запазете настройките по подразбиране (15 години, \*.yourdomain.com)
|
||||
4. Копирайте **Сертификата за произход** и **Личния ключ**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Конфигурация на Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Активиране и тестване
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Конфигурирайте Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Добавете DNS запис
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
В таблото за управление на Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Тип | Име | Съдържание | Прокси |
|
||||
| --- | ------ | ---------------------- | ------------ |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Проксиран |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Конфигурирайте SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Под **SSL/TLS → Общ преглед**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Режим: **Пълен (строг)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Под **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Винаги използвайте HTTPS: ✅ Вкл
|
||||
- Минимална TLS версия: TLS 1.2
|
||||
- Автоматично пренаписване на HTTPS: ✅ Включено
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Тестване
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Операции и поддръжка
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Надстройте до нова версия
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Преглед на регистрационни файлове
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Ръчно архивиране на база данни
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Възстановяване от резервно копие
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Разширена сигурност
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Ограничете nginx до IP адреси на Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Добавете следното към `nginx.conf` в блока `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Инсталирайте fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Блокирайте директния достъп до порта на Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Разположете в Cloudflare Workers (по избор)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
За отдалечен достъп чрез Cloudflare Workers (без директно излагане на VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Вижте пълната документация на [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Резюме на порта
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Пристанище | Обслужване | Достъп |
|
||||
| ---------- | ----------- | ------------------------------ |
|
||||
| 22 | SSH | Публичен (с fail2ban) |
|
||||
| 80 | nginx HTTP | Пренасочване → HTTPS |
|
||||
| 443 | nginx HTTPS | Чрез прокси Cloudflare |
|
||||
| 20128 | OmniRoute | Само локален хост (чрез nginx) |
|
||||
|
||||
196
docs/i18n/cs/A2A-SERVER.md
Normal file
196
docs/i18n/cs/A2A-SERVER.md
Normal file
@@ -0,0 +1,196 @@
|
||||
# Dokumentace k serveru OmniRoute A2A
|
||||
|
||||
> Protokol Agent-to-Agent v0.3 — OmniRoute jako inteligentní směrovací agent
|
||||
|
||||
## Objevování agentů
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/.well-known/agent.json
|
||||
```
|
||||
|
||||
Vrátí kartu agenta popisující schopnosti, dovednosti a požadavky na ověřování OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Ověřování
|
||||
|
||||
Všechny požadavky `/a2a` vyžadují klíč API zadaný prostřednictvím hlavičky `Authorization` :
|
||||
|
||||
```
|
||||
Authorization: Bearer YOUR_OMNIROUTE_API_KEY
|
||||
```
|
||||
|
||||
Pokud na serveru není nakonfigurován žádný klíč API, ověřování se obejde.
|
||||
|
||||
---
|
||||
|
||||
## Metody JSON-RPC 2.0
|
||||
|
||||
### `message/send` — synchronní spuštění
|
||||
|
||||
Odešle zprávu dovednosti a čeká na úplnou odpověď.
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/a2a \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_KEY" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "1",
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"skill": "smart-routing",
|
||||
"messages": [{"role": "user", "content": "Write a hello world in Python"}],
|
||||
"metadata": {"model": "auto", "combo": "fast-coding"}
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Odpověď:**
|
||||
|
||||
```json
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "1",
|
||||
"result": {
|
||||
"task": { "id": "uuid", "state": "completed" },
|
||||
"artifacts": [{ "type": "text", "content": "..." }],
|
||||
"metadata": {
|
||||
"routing_explanation": "Selected claude-sonnet via provider \"anthropic\" (latency: 1200ms, cost: $0.003)",
|
||||
"cost_envelope": { "estimated": 0.005, "actual": 0.003, "currency": "USD" },
|
||||
"resilience_trace": [
|
||||
{ "event": "primary_selected", "provider": "anthropic", "timestamp": "..." }
|
||||
],
|
||||
"policy_verdict": { "allowed": true, "reason": "within budget and quota limits" }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `message/stream` — SSE streamování
|
||||
|
||||
Stejné jako `message/send` , ale vrací události odeslané serverem pro streamování v reálném čase.
|
||||
|
||||
```bash
|
||||
curl -N -X POST http://localhost:20128/a2a \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_KEY" \
|
||||
-d '{
|
||||
"jsonrpc": "2.0",
|
||||
"id": "1",
|
||||
"method": "message/stream",
|
||||
"params": {
|
||||
"skill": "smart-routing",
|
||||
"messages": [{"role": "user", "content": "Explain quantum computing"}]
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
**Události SSE:**
|
||||
|
||||
```
|
||||
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"working"},"chunk":{"type":"text","content":"..."}}}
|
||||
|
||||
: heartbeat 2026-03-03T17:00:00Z
|
||||
|
||||
data: {"jsonrpc":"2.0","method":"message/stream","params":{"task":{"id":"...","state":"completed"},"metadata":{...}}}
|
||||
```
|
||||
|
||||
### `tasks/get` — Dotaz na stav úlohy
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/a2a \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_KEY" \
|
||||
-d '{"jsonrpc":"2.0","id":"2","method":"tasks/get","params":{"taskId":"TASK_UUID"}}'
|
||||
```
|
||||
|
||||
### `tasks/cancel` — Zrušit úkol
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/a2a \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer YOUR_KEY" \
|
||||
-d '{"jsonrpc":"2.0","id":"3","method":"tasks/cancel","params":{"taskId":"TASK_UUID"}}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dostupné dovednosti
|
||||
|
||||
Dovednost | Popis
|
||||
:-- | :--
|
||||
`smart-routing` | Směruje výzvy prostřednictvím inteligentního kanálu OmniRoute. Vrací odpověď s vysvětlením směrování, náklady a trasou odolnosti.
|
||||
`quota-management` | Odpovídá na dotazy v přirozeném jazyce týkající se kvót poskytovatelů, navrhuje bezplatné kombinace a poskytuje hodnocení kvót.
|
||||
|
||||
---
|
||||
|
||||
## Životní cyklus úkolu
|
||||
|
||||
```
|
||||
submitted → working → completed
|
||||
→ failed
|
||||
→ cancelled
|
||||
```
|
||||
|
||||
- Úkoly vyprší po 5 minutách (konfigurovatelné)
|
||||
- Stavy terminálu: `completed` , `failed` , `cancelled`
|
||||
- Záznam událostí sleduje každý přechod stavu
|
||||
|
||||
---
|
||||
|
||||
## Chybové kódy
|
||||
|
||||
Kód | Význam
|
||||
:-- | :--
|
||||
-32700 | Chyba při analýze (neplatný JSON)
|
||||
-32600 | Neplatný požadavek / Neautorizovaný
|
||||
-32601 | Metoda nebo dovednost nenalezena
|
||||
-32602 | Neplatné parametry
|
||||
-32603 | Interní chyba
|
||||
|
||||
---
|
||||
|
||||
## Příklady integrace
|
||||
|
||||
### Python (požadavky)
|
||||
|
||||
```python
|
||||
import requests
|
||||
|
||||
resp = requests.post("http://localhost:20128/a2a", json={
|
||||
"jsonrpc": "2.0", "id": "1",
|
||||
"method": "message/send",
|
||||
"params": {
|
||||
"skill": "smart-routing",
|
||||
"messages": [{"role": "user", "content": "Hello"}]
|
||||
}
|
||||
}, headers={"Authorization": "Bearer YOUR_KEY"})
|
||||
|
||||
result = resp.json()["result"]
|
||||
print(result["artifacts"][0]["content"])
|
||||
print(result["metadata"]["routing_explanation"])
|
||||
```
|
||||
|
||||
### TypeScript (načtení)
|
||||
|
||||
```typescript
|
||||
const resp = await fetch("http://localhost:20128/a2a", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: "Bearer YOUR_KEY",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
jsonrpc: "2.0",
|
||||
id: "1",
|
||||
method: "message/send",
|
||||
params: {
|
||||
skill: "smart-routing",
|
||||
messages: [{ role: "user", content: "Hello" }],
|
||||
},
|
||||
}),
|
||||
});
|
||||
const { result } = await resp.json();
|
||||
console.log(result.metadata.routing_explanation);
|
||||
```
|
||||
451
docs/i18n/cs/API_REFERENCE.md
Normal file
451
docs/i18n/cs/API_REFERENCE.md
Normal file
@@ -0,0 +1,451 @@
|
||||
# Referenční informace k API
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](API_REFERENCE.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](i18n/es/API_REFERENCE.md) | 🇫🇷 [Français](i18n/fr/API_REFERENCE.md) | 🇮🇹 [Italiano](i18n/it/API_REFERENCE.md) | 🇷🇺 [Русский](i18n/ru/API_REFERENCE.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](i18n/de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](i18n/in/API_REFERENCE.md) | 🇹🇭 [ไทย](i18n/th/API_REFERENCE.md) | 🇺🇦 [Українська](i18n/uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](i18n/ar/API_REFERENCE.md) | 🇯🇵[日本語](i18n/ja/API_REFERENCE.md)| 🇻🇳 [Tiếng Việt](i18n/vi/API_REFERENCE.md) | 🇧🇬 [Български](i18n/bg/API_REFERENCE.md) | 🇩🇰 [Dánsko](i18n/da/API_REFERENCE.md) | 🇫🇮 [Suomi](i18n/fi/API_REFERENCE.md) | 🇮🇱 [עברית](i18n/he/API_REFERENCE.md) | 🇭🇺 [maďarština](i18n/hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/API_REFERENCE.md) | 🇰🇷 [한국어](i18n/ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/API_REFERENCE.md) | 🇳🇱 [Nizozemsko](i18n/nl/API_REFERENCE.md) | 🇳🇴 [Norsk](i18n/no/API_REFERENCE.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/API_REFERENCE.md) | 🇷🇴 [Română](i18n/ro/API_REFERENCE.md) | 🇵🇱 [Polski](i18n/pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](i18n/sk/API_REFERENCE.md) | 🇸🇪 [Svenska](i18n/sv/API_REFERENCE.md) | 🇵🇭 [Filipínec](i18n/phi/API_REFERENCE.md) | 🇨🇿 [Čeština](i18n/cs/API_REFERENCE.md)
|
||||
|
||||
Kompletní referenční příručka pro všechny koncové body rozhraní OmniRoute API.
|
||||
|
||||
---
|
||||
|
||||
## Obsah
|
||||
|
||||
- [Dokončení chatu](#chat-completions)
|
||||
- [Vložení](#embeddings)
|
||||
- [Generování obrázků](#image-generation)
|
||||
- [Seznam modelů](#list-models)
|
||||
- [Koncové body kompatibility](#compatibility-endpoints)
|
||||
- [Sémantická mezipaměť](#semantic-cache)
|
||||
- [Řídicí panel a správa](#dashboard--management)
|
||||
- [Zpracování žádosti](#request-processing)
|
||||
- [Ověřování](#authentication)
|
||||
|
||||
---
|
||||
|
||||
## Dokončení chatu
|
||||
|
||||
```bash
|
||||
POST /v1/chat/completions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "cc/claude-opus-4-6",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Write a function to..."}
|
||||
],
|
||||
"stream": true
|
||||
}
|
||||
```
|
||||
|
||||
### Vlastní záhlaví
|
||||
|
||||
Záhlaví | Směr | Popis
|
||||
--- | --- | ---
|
||||
`X-OmniRoute-No-Cache` | Žádost | Nastavením na `true` se vynechá mezipaměť
|
||||
`X-OmniRoute-Progress` | Žádost | Nastaveno na `true` pro události průběhu
|
||||
`Idempotency-Key` | Žádost | Klíč pro deduplikaci (okno 5 s)
|
||||
`X-Request-Id` | Žádost | Alternativní klíč pro odstranění duplicitních dat
|
||||
`X-OmniRoute-Cache` | Odpověď | `HIT` or `MISS` (nestreamované)
|
||||
`X-OmniRoute-Idempotent` | Odpověď | `true` , pokud je odstraněna duplikace
|
||||
`X-OmniRoute-Progress` | Odpověď | `enabled` pokud je zapnuto sledování průběhu
|
||||
|
||||
---
|
||||
|
||||
## Vložení
|
||||
|
||||
```bash
|
||||
POST /v1/embeddings
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "nebius/Qwen/Qwen3-Embedding-8B",
|
||||
"input": "The food was delicious"
|
||||
}
|
||||
```
|
||||
|
||||
Dostupní poskytovatelé: Nebius, OpenAI, Mistral, Together AI, Fireworks, NVIDIA.
|
||||
|
||||
```bash
|
||||
# List all embedding models
|
||||
GET /v1/embeddings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Generování obrázků
|
||||
|
||||
```bash
|
||||
POST /v1/images/generations
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "openai/dall-e-3",
|
||||
"prompt": "A beautiful sunset over mountains",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
```
|
||||
|
||||
Dostupní poskytovatelé: OpenAI (DALL-E), xAI (Grok Image), Together AI (FLUX), Fireworks AI.
|
||||
|
||||
```bash
|
||||
# List all image models
|
||||
GET /v1/images/generations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Seznam modelů
|
||||
|
||||
```bash
|
||||
GET /v1/models
|
||||
Authorization: Bearer your-api-key
|
||||
|
||||
→ Returns all chat, embedding, and image models + combos in OpenAI format
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Koncové body kompatibility
|
||||
|
||||
Metoda | Cesta | Formát
|
||||
--- | --- | ---
|
||||
ZVEŘEJNIT | `/v1/chat/completions` | OpenAI
|
||||
ZVEŘEJNIT | `/v1/messages` | Antropický
|
||||
ZVEŘEJNIT | `/v1/responses` | Reakce OpenAI
|
||||
ZVEŘEJNIT | `/v1/embeddings` | OpenAI
|
||||
ZVEŘEJNIT | `/v1/images/generations` | OpenAI
|
||||
ZÍSKAT | `/v1/models` | OpenAI
|
||||
ZVEŘEJNIT | `/v1/messages/count_tokens` | Antropický
|
||||
ZÍSKAT | `/v1beta/models` | Blíženci
|
||||
ZVEŘEJNIT | `/v1beta/models/{...path}` | Gemini generuje obsah
|
||||
ZVEŘEJNIT | `/v1/api/chat` | Ollama
|
||||
|
||||
### Vyhrazené trasy poskytovatelů
|
||||
|
||||
```bash
|
||||
POST /v1/providers/{provider}/chat/completions
|
||||
POST /v1/providers/{provider}/embeddings
|
||||
POST /v1/providers/{provider}/images/generations
|
||||
```
|
||||
|
||||
Pokud chybí prefix poskytovatele, automaticky se přidá. Neshodné modely vrátí chybu `400` .
|
||||
|
||||
---
|
||||
|
||||
## Sémantická mezipaměť
|
||||
|
||||
```bash
|
||||
# Get cache stats
|
||||
GET /api/cache
|
||||
|
||||
# Clear all caches
|
||||
DELETE /api/cache
|
||||
```
|
||||
|
||||
Příklad odpovědi:
|
||||
|
||||
```json
|
||||
{
|
||||
"semanticCache": {
|
||||
"memorySize": 42,
|
||||
"memoryMaxSize": 500,
|
||||
"dbSize": 128,
|
||||
"hitRate": 0.65
|
||||
},
|
||||
"idempotency": {
|
||||
"activeKeys": 3,
|
||||
"windowMs": 5000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Řídicí panel a správa
|
||||
|
||||
### Ověřování
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/auth/login` | ZVEŘEJNIT | Přihlášení
|
||||
`/api/auth/logout` | ZVEŘEJNIT | Odhlásit se
|
||||
`/api/settings/require-login` | ZÍSKAT/VLOŽIT | Vyžaduje se přepnutí přihlášení
|
||||
|
||||
### Správa poskytovatelů
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/providers` | ZÍSKAT/ODESLAT | Seznam / vytvoření poskytovatelů
|
||||
`/api/providers/[id]` | ZÍSKAT/VLOŽIT/ODSTRANIT | Správa poskytovatele
|
||||
`/api/providers/[id]/test` | ZVEŘEJNIT | Testovací připojení poskytovatele
|
||||
`/api/providers/[id]/models` | ZÍSKAT | Seznam modelů poskytovatelů
|
||||
`/api/providers/validate` | ZVEŘEJNIT | Ověření konfigurace poskytovatele
|
||||
`/api/provider-nodes*` | Různé | Správa uzlů poskytovatelů
|
||||
`/api/provider-models` | ZÍSKAT/ODESLAT/SMAZAT | Vlastní modely
|
||||
|
||||
### Toky OAuth
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/oauth/[provider]/[action]` | Různé | OAuth specifický pro poskytovatele
|
||||
|
||||
### Směrování a konfigurace
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/models/alias` | ZÍSKAT/ODESLAT | Aliasy modelů
|
||||
`/api/models/catalog` | ZÍSKAT | Všechny modely podle poskytovatele + typu
|
||||
`/api/combos*` | Různé | Správa kombinací
|
||||
`/api/keys*` | Různé | Správa klíčů API
|
||||
`/api/pricing` | ZÍSKAT | Cena modelu
|
||||
|
||||
### Využití a analýzy
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/usage/history` | ZÍSKAT | Historie používání
|
||||
`/api/usage/logs` | ZÍSKAT | Protokoly používání
|
||||
`/api/usage/request-logs` | ZÍSKAT | Protokoly na úrovni požadavků
|
||||
`/api/usage/[connectionId]` | ZÍSKAT | Využití na připojení
|
||||
|
||||
### Nastavení
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/settings` | ZÍSKAT/VLOŽIT | Obecná nastavení
|
||||
`/api/settings/proxy` | ZÍSKAT/VLOŽIT | Konfigurace síťového proxy serveru
|
||||
`/api/settings/proxy/test` | ZVEŘEJNIT | Testovací připojení k proxy serveru
|
||||
`/api/settings/ip-filter` | ZÍSKAT/VLOŽIT | Seznam povolených/blokovaných IP adres
|
||||
`/api/settings/thinking-budget` | ZÍSKAT/VLOŽIT | Zdůvodnění rozpočtu tokenů
|
||||
`/api/settings/system-prompt` | ZÍSKAT/VLOŽIT | Globální systémový výzva
|
||||
|
||||
### Monitorování
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/sessions` | ZÍSKAT | Sledování aktivních relací
|
||||
`/api/rate-limits` | ZÍSKAT | Limity sazeb na účet
|
||||
`/api/monitoring/health` | ZÍSKAT | Kontrola stavu
|
||||
`/api/cache` | ZÍSKAT/SMAZAT | Statistiky mezipaměti / vymazat
|
||||
|
||||
### Zálohování a export/import
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/db-backups` | ZÍSKAT | Seznam dostupných záloh
|
||||
`/api/db-backups` | DÁT | Vytvořte ruční zálohu
|
||||
`/api/db-backups` | ZVEŘEJNIT | Obnovení z konkrétní zálohy
|
||||
`/api/db-backups/export` | ZÍSKAT | Stáhnout databázi jako soubor .sqlite
|
||||
`/api/db-backups/import` | ZVEŘEJNIT | Nahrajte soubor .sqlite pro nahrazení databáze
|
||||
`/api/db-backups/exportAll` | ZÍSKAT | Stáhnout plnou zálohu jako archiv .tar.gz
|
||||
|
||||
### Synchronizace s cloudem
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/sync/cloud` | Různé | Operace synchronizace s cloudem
|
||||
`/api/sync/initialize` | ZVEŘEJNIT | Inicializovat synchronizaci
|
||||
`/api/cloud/*` | Různé | Správa cloudu
|
||||
|
||||
### Nástroje CLI
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/cli-tools/claude-settings` | ZÍSKAT | Stav Clauda CLI
|
||||
`/api/cli-tools/codex-settings` | ZÍSKAT | Stav příkazového řádku Codexu
|
||||
`/api/cli-tools/droid-settings` | ZÍSKAT | Stav příkazového řádku Droidu
|
||||
`/api/cli-tools/openclaw-settings` | ZÍSKAT | Stav rozhraní příkazového řádku OpenClaw
|
||||
`/api/cli-tools/runtime/[toolId]` | ZÍSKAT | Generické běhové prostředí CLI
|
||||
|
||||
Mezi odpovědi CLI patří: `installed` , `runnable` , `command` , `commandPath` , `runtimeMode` , `reason` .
|
||||
|
||||
### Agenti ACP
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/acp/agents` | ZÍSKAT | Zobrazit seznam všech detekovaných agentů (vestavěných + vlastních) se stavem
|
||||
`/api/acp/agents` | ZVEŘEJNIT | Přidat vlastního agenta nebo obnovit mezipaměť detekce
|
||||
`/api/acp/agents` | VYMAZAT | Odebrání vlastního agenta podle parametru dotazu `id`
|
||||
|
||||
Odpověď GET obsahuje `agents[]` (id, name, binary, version, installed, protocol, isCustom) a `summary` (total, installed, notFound, builtIn, custom).
|
||||
|
||||
### Odolnost a limity rychlosti
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/resilience` | ZÍSKAT/VLOŽIT | Získání/aktualizace profilů odolnosti
|
||||
`/api/resilience/reset` | ZVEŘEJNIT | Resetujte jističe
|
||||
`/api/rate-limits` | ZÍSKAT | Stav limitu sazby na účet
|
||||
`/api/rate-limit` | ZÍSKAT | Konfigurace globálního limitu rychlosti
|
||||
|
||||
### Evals
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/evals` | ZÍSKAT/ODESLAT | Vypsat eval sady / spustit vyhodnocení
|
||||
|
||||
### Zásady
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/policies` | ZÍSKAT/ODESLAT/SMAZAT | Správa směrovacích zásad
|
||||
|
||||
### Dodržování
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/compliance/audit-log` | ZÍSKAT | Protokol auditu shody (poslední N)
|
||||
|
||||
### v1beta (kompatibilní s Gemini)
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/v1beta/models` | ZÍSKAT | Seznam modelů ve formátu Gemini
|
||||
`/v1beta/models/{...path}` | ZVEŘEJNIT | Koncový bod Gemini `generateContent`
|
||||
|
||||
Tyto koncové body zrcadlí formát API Gemini pro klienty, kteří očekávají nativní kompatibilitu sady Gemini SDK.
|
||||
|
||||
### Interní / systémová API
|
||||
|
||||
Koncový bod | Metoda | Popis
|
||||
--- | --- | ---
|
||||
`/api/init` | ZÍSKAT | Kontrola inicializace aplikace (používá se při prvním spuštění)
|
||||
`/api/tags` | ZÍSKAT | Tagy modelů kompatibilní s Ollamou (pro klienty Ollamy)
|
||||
`/api/restart` | ZVEŘEJNIT | Spustit řádný restart serveru
|
||||
`/api/shutdown` | ZVEŘEJNIT | Spustit řádné vypnutí serveru
|
||||
|
||||
> **Poznámka:** Tyto koncové body používá interně systém nebo pro kompatibilitu s klienty Ollama. Koncoví uživatelé je obvykle nevolají.
|
||||
|
||||
---
|
||||
|
||||
## Přepis zvuku
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
```
|
||||
|
||||
Přepisujte zvukové soubory pomocí Deepgramu nebo AssemblyAI.
|
||||
|
||||
**Žádost:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@recording.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
**Odpověď:**
|
||||
|
||||
```json
|
||||
{
|
||||
"text": "Hello, this is the transcribed audio content.",
|
||||
"task": "transcribe",
|
||||
"language": "en",
|
||||
"duration": 12.5
|
||||
}
|
||||
```
|
||||
|
||||
**Podporovaní poskytovatelé:** `deepgram/nova-3` , `assemblyai/best` .
|
||||
|
||||
**Podporované formáty:** `mp3` , `wav` , `m4a` , `flac` , `ogg` , `webm` .
|
||||
|
||||
---
|
||||
|
||||
## Kompatibilita s Ollamou
|
||||
|
||||
Pro klienty, kteří používají formát API od Ollamy:
|
||||
|
||||
```bash
|
||||
# Chat endpoint (Ollama format)
|
||||
POST /v1/api/chat
|
||||
|
||||
# Model listing (Ollama format)
|
||||
GET /api/tags
|
||||
```
|
||||
|
||||
Požadavky jsou automaticky překládány mezi formátem Ollama a interním formátem.
|
||||
|
||||
---
|
||||
|
||||
## Telemetrie
|
||||
|
||||
```bash
|
||||
# Get latency telemetry summary (p50/p95/p99 per provider)
|
||||
GET /api/telemetry/summary
|
||||
```
|
||||
|
||||
**Odpověď:**
|
||||
|
||||
```json
|
||||
{
|
||||
"providers": {
|
||||
"claudeCode": { "p50": 245, "p95": 890, "p99": 1200, "count": 150 },
|
||||
"github": { "p50": 180, "p95": 620, "p99": 950, "count": 320 }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rozpočet
|
||||
|
||||
```bash
|
||||
# Get budget status for all API keys
|
||||
GET /api/usage/budget
|
||||
|
||||
# Set or update a budget
|
||||
POST /api/usage/budget
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"keyId": "key-123",
|
||||
"limit": 50.00,
|
||||
"period": "monthly"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Dostupnost modelu
|
||||
|
||||
```bash
|
||||
# Get real-time model availability across all providers
|
||||
GET /api/models/availability
|
||||
|
||||
# Check availability for a specific model
|
||||
POST /api/models/availability
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"model": "claude-sonnet-4-5-20250929"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Zpracování žádosti
|
||||
|
||||
1. Klient odesílá požadavek na `/v1/*`
|
||||
2. Obslužná rutina trasy volá `handleChat` , `handleEmbedding` , `handleAudioTranscription` nebo `handleImageGeneration`
|
||||
3. Model je vyřešen (přímý poskytovatel/model nebo alias/kombinace)
|
||||
4. Přihlašovací údaje vybrané z lokální databáze s filtrováním dostupnosti účtů
|
||||
5. Pro chat: `handleChatCore` — detekce formátu, překlad, kontrola mezipaměti, kontrola idempotence
|
||||
6. Prováděcí program poskytovatele odesílá požadavek nadřazenému serveru
|
||||
7. Odpověď přeložena zpět do klientského formátu (chat) nebo vrácena tak, jak je (vložené prvky/obrázky/zvuk)
|
||||
8. Zaznamenáno použití/protokolování
|
||||
9. Záložní metoda se použije na chyby podle pravidel kombinace.
|
||||
|
||||
Úplný referenční popis architektury: [`ARCHITECTURE.md`](ARCHITECTURE.md)
|
||||
|
||||
---
|
||||
|
||||
## Ověřování
|
||||
|
||||
- Trasy dashboardu ( `/dashboard/*` ) používají soubor cookie `auth_token`
|
||||
- Přihlášení používá uložený hash hesla; záložní nastavení je `INITIAL_PASSWORD`
|
||||
- `requireLogin` lze přepínat přes `/api/settings/require-login`
|
||||
- Trasy `/v1/*` volitelně vyžadují klíč API nosiče, pokud `REQUIRE_API_KEY=true`
|
||||
782
docs/i18n/cs/ARCHITECTURE.md
Normal file
782
docs/i18n/cs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,782 @@
|
||||
# Architektura OmniRoute
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](ARCHITECTURE.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](i18n/es/ARCHITECTURE.md) | 🇫🇷 [Français](i18n/fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](i18n/it/ARCHITECTURE.md) | 🇷🇺 [Русский](i18n/ru/ARCHITECTURE.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](i18n/de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](i18n/in/ARCHITECTURE.md) | 🇹🇭 [ไทย](i18n/th/ARCHITECTURE.md) | 🇺🇦 [Українська](i18n/uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](i18n/ar/ARCHITECTURE.md) | 🇯🇵[日本語](i18n/ja/ARCHITECTURE.md)| 🇻🇳 [Tiếng Việt](i18n/vi/ARCHITECTURE.md) | 🇧🇬 [Български](i18n/bg/ARCHITECTURE.md) | 🇩🇰 [Dánsko](i18n/da/ARCHITECTURE.md) | 🇫🇮 [Suomi](i18n/fi/ARCHITECTURE.md) | 🇮🇱 [עברית](i18n/he/ARCHITECTURE.md) | 🇭🇺 [maďarština](i18n/hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/ARCHITECTURE.md) | 🇰🇷 [한국어](i18n/ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/ARCHITECTURE.md) | 🇳🇱 [Nizozemsko](i18n/nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](i18n/no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/ARCHITECTURE.md) | 🇷🇴 [Română](i18n/ro/ARCHITECTURE.md) | 🇵🇱 [Polski](i18n/pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](i18n/sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](i18n/sv/ARCHITECTURE.md) | 🇵🇭 [Filipínec](i18n/phi/ARCHITECTURE.md) | 🇨🇿 [Čeština](i18n/cs/ARCHITECTURE.md)
|
||||
|
||||
*Poslední aktualizace: 2026-03-04*
|
||||
|
||||
## Shrnutí pro manažery
|
||||
|
||||
OmniRoute je lokální směrovací brána a dashboard s umělou inteligencí postavený na Next.js. Poskytuje jeden koncový bod kompatibilní s OpenAI ( `/v1/*` ) a směruje provoz napříč několika upstreamovými poskytovateli s překladem, záložními funkcemi, obnovou tokenů a sledováním využití.
|
||||
|
||||
Základní schopnosti:
|
||||
|
||||
- API prostředí kompatibilní s OpenAI pro CLI/nástroje (28 poskytovatelů)
|
||||
- Překlad požadavků/odpovědí napříč formáty poskytovatelů
|
||||
- Záložní kombinace modelů (sekvence s více modely)
|
||||
- Záložní řešení na úrovni účtu (více účtů na poskytovatele)
|
||||
- Správa připojení poskytovatele OAuth + API klíčů
|
||||
- Generování embeddingů pomocí `/v1/embeddings` (6 poskytovatelů, 9 modelů)
|
||||
- Generování obrázků pomocí `/v1/images/generations` (4 poskytovatelé, 9 modelů)
|
||||
- Pro modely uvažování zvažte analýzu tagů ( `<think>...</think>` ).
|
||||
- Sanitizace odpovědí pro striktní kompatibilitu s OpenAI SDK
|
||||
- Normalizace rolí (vývojář→systém, systém→uživatel) pro kompatibilitu mezi poskytovateli
|
||||
- Konverze strukturovaného výstupu (json_schema → Gemini responseSchema)
|
||||
- Lokální perzistence pro poskytovatele, klíče, aliasy, kombinace, nastavení, ceny
|
||||
- Sledování využití/nákladů a protokolování požadavků
|
||||
- Volitelná cloudová synchronizace pro synchronizaci více zařízení/stavů
|
||||
- Seznam povolených/blokovaných IP adres pro řízení přístupu k API
|
||||
- Řízení rozpočtu (průchozí/automatické/vlastní/adaptivní)
|
||||
- Globální systémová výzva k vložení
|
||||
- Sledování relací a otisky prstů
|
||||
- Vylepšené omezení sazeb pro jednotlivé účty s profily specifickými pro poskytovatele
|
||||
- Vzor jističů pro odolnost poskytovatele
|
||||
- Ochrana stáda proti hromům s uzamčením mutexů
|
||||
- Mezipaměť pro deduplikaci požadavků založená na podpisech
|
||||
- Vrstva domény: dostupnost modelu, pravidla nákladů, záložní politika, politika blokování
|
||||
- Perzistence stavu domény (mezipaměť SQLite pro zápis pro záložní funkce, rozpočty, uzamčení, jističe)
|
||||
- Modul zásad pro centralizované vyhodnocování požadavků (uzamčení → rozpočet → záložní)
|
||||
- Vyžádat telemetrii s agregací latence p50/p95/p99
|
||||
- Korelační ID (X-Request-Id) pro trasování typu end-to-end
|
||||
- Protokolování auditu shody s předpisy s možností odhlášení pro každý klíč API
|
||||
- Evaluační rámec pro zajištění kvality LLM
|
||||
- Řídicí panel uživatelského rozhraní Resilience se stavem jističe v reálném čase
|
||||
- Modulární poskytovatelé OAuth (12 jednotlivých modulů v adresáři `src/lib/oauth/providers/` )
|
||||
|
||||
Primární běhový model:
|
||||
|
||||
- Trasy aplikace Next.js v `src/app/api/*` implementují jak API dashboardů, tak i API kompatibility.
|
||||
- Sdílené jádro SSE/routing v `src/sse/*` + `open-sse/*` zvládá spouštění poskytovatelů, překlad, streamování, záložní operace a využití.
|
||||
|
||||
## Rozsah a hranice
|
||||
|
||||
### V rozsahu
|
||||
|
||||
- Běhové prostředí lokální brány
|
||||
- Rozhraní API pro správu řídicích panelů
|
||||
- Ověřování poskytovatele a aktualizace tokenu
|
||||
- Žádost o překlad a streamování SSE
|
||||
- Lokální stav + perzistence využití
|
||||
- Volitelná orchestrace synchronizace s cloudem
|
||||
|
||||
### Mimo rozsah
|
||||
|
||||
- Implementace cloudové služby za `NEXT_PUBLIC_CLOUD_URL`
|
||||
- SLA/řídicí rovina poskytovatele mimo lokální proces
|
||||
- Samotné externí binární soubory CLI (Claude CLI, Codex CLI atd.)
|
||||
|
||||
## Kontext systému na vysoké úrovni
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Clients[Developer Clients]
|
||||
C1[Claude Code]
|
||||
C2[Codex CLI]
|
||||
C3[OpenClaw / Droid / Cline / Continue / Roo]
|
||||
C4[Custom OpenAI-compatible clients]
|
||||
BROWSER[Browser Dashboard]
|
||||
end
|
||||
|
||||
subgraph Router[OmniRoute Local Process]
|
||||
API[V1 Compatibility API\n/v1/*]
|
||||
DASH[Dashboard + Management API\n/api/*]
|
||||
CORE[SSE + Translation Core\nopen-sse + src/sse]
|
||||
DB[(storage.sqlite)]
|
||||
UDB[(usage tables + log artifacts)]
|
||||
end
|
||||
|
||||
subgraph Upstreams[Upstream Providers]
|
||||
P1[OAuth Providers\nClaude/Codex/Gemini/Qwen/iFlow/GitHub/Kiro/Cursor/Antigravity]
|
||||
P2[API Key Providers\nOpenAI/Anthropic/OpenRouter/GLM/Kimi/MiniMax\nDeepSeek/Groq/xAI/Mistral/Perplexity\nTogether/Fireworks/Cerebras/Cohere/NVIDIA]
|
||||
P3[Compatible Nodes\nOpenAI-compatible / Anthropic-compatible]
|
||||
end
|
||||
|
||||
subgraph Cloud[Optional Cloud Sync]
|
||||
CLOUD[Cloud Sync Endpoint\nNEXT_PUBLIC_CLOUD_URL]
|
||||
end
|
||||
|
||||
C1 --> API
|
||||
C2 --> API
|
||||
C3 --> API
|
||||
C4 --> API
|
||||
BROWSER --> DASH
|
||||
|
||||
API --> CORE
|
||||
DASH --> DB
|
||||
CORE --> DB
|
||||
CORE --> UDB
|
||||
|
||||
CORE --> P1
|
||||
CORE --> P2
|
||||
CORE --> P3
|
||||
|
||||
DASH --> CLOUD
|
||||
```
|
||||
|
||||
## Základní běhové komponenty
|
||||
|
||||
## 1) API a směrovací vrstva (trasy aplikací Next.js)
|
||||
|
||||
Hlavní adresáře:
|
||||
|
||||
- `src/app/api/v1/*` a `src/app/api/v1beta/*` pro rozhraní API pro zajištění kompatibility
|
||||
- `src/app/api/*` pro API pro správu/konfiguraci
|
||||
- Další přepisy v `next.config.mjs` mapují `/v1/*` na `/api/v1/*`
|
||||
|
||||
Důležité způsoby kompatibility:
|
||||
|
||||
- `src/app/api/v1/chat/completions/route.ts`
|
||||
- `src/app/api/v1/messages/route.ts`
|
||||
- `src/app/api/v1/responses/route.ts`
|
||||
- `src/app/api/v1/models/route.ts` — obsahuje vlastní modely s `custom: true`
|
||||
- `src/app/api/v1/embeddings/route.ts` — generování embeddingů (6 poskytovatelů)
|
||||
- `src/app/api/v1/images/generations/route.ts` — generování obrázků (4+ poskytovatelů včetně Antigravity/Nebius)
|
||||
- `src/app/api/v1/messages/count_tokens/route.ts`
|
||||
- `src/app/api/v1/providers/[provider]/chat/completions/route.ts` — vyhrazený chat pro jednotlivé poskytovatele
|
||||
- `src/app/api/v1/providers/[provider]/embeddings/route.ts` — vyhrazená vkládání pro jednotlivé poskytovatele
|
||||
- `src/app/api/v1/providers/[provider]/images/generations/route.ts` — vyhrazené obrazy pro jednotlivé poskytovatele
|
||||
- `src/app/api/v1beta/models/route.ts`
|
||||
- `src/app/api/v1beta/models/[...path]/route.ts`
|
||||
|
||||
Domény správy:
|
||||
|
||||
- Auth/settings: `src/app/api/auth/*` , `src/app/api/settings/*`
|
||||
- Poskytovatelé/připojení: `src/app/api/providers*`
|
||||
- Uzly poskytovatele: `src/app/api/provider-nodes*`
|
||||
- Vlastní modely: `src/app/api/provider-models` (GET/POST/DELETE)
|
||||
- Katalog modelů: `src/app/api/models/route.ts` (GET)
|
||||
- Konfigurace proxy: `src/app/api/settings/proxy` (GET/PUT/DELETE) + `src/app/api/settings/proxy/test` (POST)
|
||||
- OAuth: `src/app/api/oauth/*`
|
||||
- Klíče/aliasy/kombinace/ceny: `src/app/api/keys*` , `src/app/api/models/alias` , `src/app/api/combos*` , `src/app/api/pricing`
|
||||
- Použití: `src/app/api/usage/*`
|
||||
- Synchronizace/cloud: `src/app/api/sync/*` , `src/app/api/cloud/*`
|
||||
- Pomocné nástroje pro CLI: `src/app/api/cli-tools/*`
|
||||
- IP filtr: `src/app/api/settings/ip-filter` (GET/PUT)
|
||||
- Rozpočet pro myšlení: `src/app/api/settings/thinking-budget` (GET/PUT)
|
||||
- Systémový příkaz: `src/app/api/settings/system-prompt` (GET/PUT)
|
||||
- Relace: `src/app/api/sessions` (GET)
|
||||
- Limity rychlosti: `src/app/api/rate-limits` (GET)
|
||||
- Odolnost: `src/app/api/resilience` (GET/PATCH) — profily poskytovatelů, jistič, stav limitu rychlosti
|
||||
- Reset odolnosti: `src/app/api/resilience/reset` (POST) — reset jističů + doby zchlazení
|
||||
- Statistiky mezipaměti: `src/app/api/cache/stats` (GET/DELETE)
|
||||
- Dostupnost modelu: `src/app/api/models/availability` (GET/POST)
|
||||
- Telemetrie: `src/app/api/telemetry/summary` (GET)
|
||||
- Rozpočet: `src/app/api/usage/budget` (GET/POST)
|
||||
- Záložní řetězce: `src/app/api/fallback/chains` (GET/POST/DELETE)
|
||||
- Audit shody: `src/app/api/compliance/audit-log` (GET)
|
||||
- Evals: `src/app/api/evals` (GET/POST), `src/app/api/evals/[suiteId]` (GET)
|
||||
- Zásady: `src/app/api/policies` (GET/POST)
|
||||
|
||||
## 2) SSE + Překladatelské jádro
|
||||
|
||||
Hlavní moduly toku:
|
||||
|
||||
- Záznam: `src/sse/handlers/chat.ts`
|
||||
- Orchestrace jádra: `open-sse/handlers/chatCore.ts`
|
||||
- Adaptéry pro spuštění poskytovatelů: `open-sse/executors/*`
|
||||
- Detekce formátu/konfigurace poskytovatele: `open-sse/services/provider.ts`
|
||||
- Analýza/řešení modelu: `src/sse/services/model.ts` , `open-sse/services/model.ts`
|
||||
- Logika záložního účtu: `open-sse/services/accountFallback.ts`
|
||||
- Registr překladů: `open-sse/translator/index.ts`
|
||||
- Transformace streamů: `open-sse/utils/stream.ts` , `open-sse/utils/streamHandler.ts`
|
||||
- Extrakce/normalizace využití: `open-sse/utils/usageTracking.ts`
|
||||
- Analyzátor tagů Think: `open-sse/utils/thinkTagParser.ts`
|
||||
- Obslužná rutina pro vkládání: `open-sse/handlers/embeddings.ts`
|
||||
- Registr poskytovatelů vkládání: `open-sse/config/embeddingRegistry.ts`
|
||||
- Obslužná rutina generování obrázků: `open-sse/handlers/imageGeneration.ts`
|
||||
- Registr poskytovatelů obrázků: `open-sse/config/imageRegistry.ts`
|
||||
- Sanitizace odpovědí: `open-sse/handlers/responseSanitizer.ts`
|
||||
- Normalizace rolí: `open-sse/services/roleNormalizer.ts`
|
||||
|
||||
Služby (obchodní logika):
|
||||
|
||||
- Výběr/skórování účtu: `open-sse/services/accountSelector.ts`
|
||||
- Správa životního cyklu kontextu: `open-sse/services/contextManager.ts`
|
||||
- Vynucení filtrování IP adres: `open-sse/services/ipFilter.ts`
|
||||
- Sledování relací: `open-sse/services/sessionManager.ts`
|
||||
- Požadavek na deduplikaci: `open-sse/services/signatureCache.ts`
|
||||
- Vložení systémového promptu: `open-sse/services/systemPrompt.ts`
|
||||
- Řízení rozpočtu v duchu myšlenek: `open-sse/services/thinkingBudget.ts`
|
||||
- Směrování pomocí modelu zástupných znaků: `open-sse/services/wildcardRouter.ts`
|
||||
- Správa limitů rychlosti: `open-sse/services/rateLimitManager.ts`
|
||||
- Jistič: `open-sse/services/circuitBreaker.ts`
|
||||
|
||||
Moduly doménové vrstvy:
|
||||
|
||||
- Dostupnost modelu: `src/lib/domain/modelAvailability.ts`
|
||||
- Pravidla/rozpočty nákladů: `src/lib/domain/costRules.ts`
|
||||
- Záložní zásady: `src/lib/domain/fallbackPolicy.ts`
|
||||
- Kombinovaný resolver: `src/lib/domain/comboResolver.ts`
|
||||
- Zásady uzamčení: `src/lib/domain/lockoutPolicy.ts`
|
||||
- Modul zásad: `src/domain/policyEngine.ts` — centralizované uzamčení → rozpočet → vyhodnocení záložního režimu
|
||||
- Katalog chybových kódů: `src/lib/domain/errorCodes.ts`
|
||||
- ID požadavku: `src/lib/domain/requestId.ts`
|
||||
- Časový limit načtení: `src/lib/domain/fetchTimeout.ts`
|
||||
- Požadovat telemetrii: `src/lib/domain/requestTelemetry.ts`
|
||||
- Shoda/audit: `src/lib/domain/compliance/index.ts`
|
||||
- Zkušební běžec: `src/lib/domain/evalRunner.ts`
|
||||
- Perzistence stavu domény: `src/lib/db/domainState.ts` — SQLite CRUD pro záložní řetězce, rozpočty, historii nákladů, stav uzamčení, jističe
|
||||
|
||||
Moduly poskytovatelů OAuth (12 jednotlivých souborů v adresáři `src/lib/oauth/providers/` ):
|
||||
|
||||
- Index registru: `src/lib/oauth/providers/index.ts`
|
||||
- Jednotliví poskytovatelé: `claude.ts` , `codex.ts` , `gemini.ts` , `antigravity.ts` , `iflow.ts` , `qwen.ts` , `kimi-coding.ts` , `github.ts` , `kiro.ts` , `cursor.ts` , `kilocode.ts` , `cline.ts`
|
||||
- Thin wrapper: `src/lib/oauth/providers.ts` — reexporty z jednotlivých modulů
|
||||
|
||||
## 3) Vrstva perzistence
|
||||
|
||||
Primární stavová databáze (SQLite):
|
||||
|
||||
- Základní infrastruktura: `src/lib/db/core.ts` (better-sqlite3, migrace, WAL)
|
||||
- Reexportní fasáda: `src/lib/localDb.ts` (tenká vrstva kompatibility pro volající)
|
||||
- soubor: `${DATA_DIR}/storage.sqlite` (nebo `$XDG_CONFIG_HOME/omniroute/storage.sqlite` pokud je nastaveno, jinak `~/.omniroute/storage.sqlite` )
|
||||
- entity (tabulky + jmenné prostory KV): providerConnections, providerNodes, modelAliases, combos, apiKeys, settings, pricing, **customModels** , **proxyConfig** , **ipFilter** , **thinkingBudget** , **systemPrompt**
|
||||
|
||||
Trvalost používání:
|
||||
|
||||
- fasáda: `src/lib/usageDb.ts` (dekomponované moduly v `src/lib/usage/*` )
|
||||
- SQLite tabulky v `storage.sqlite` : `usage_history` , `call_logs` , `proxy_logs`
|
||||
- Volitelné artefakty souborů zůstávají pro účely kompatibility/ladění ( `${DATA_DIR}/log.txt` , `${DATA_DIR}/call_logs/` , `<repo>/logs/...` )
|
||||
- Starší soubory JSON jsou migrovány do SQLite při migracích při spuštění, pokud jsou k dispozici.
|
||||
|
||||
Databáze stavu domény (SQLite):
|
||||
|
||||
- `src/lib/db/domainState.ts` — CRUD operace pro stav domény
|
||||
- Tabulky (vytvořené v `src/lib/db/core.ts` ): `domain_fallback_chains` , `domain_budgets` , `domain_cost_history` , `domain_lockout_state` , `domain_circuit_breakers`
|
||||
- Vzor mezipaměti pro zápis: mapy v paměti jsou autoritativní za běhu; mutace se zapisují synchronně do SQLite; stav se obnovuje z databáze při studeném startu.
|
||||
|
||||
## 4) Ověřovací a bezpečnostní povrchy
|
||||
|
||||
- Autorizace souborů cookie v dashboardu: `src/proxy.ts` , `src/app/api/auth/login/route.ts`
|
||||
- Generování/ověření klíče API: `src/shared/utils/apiKey.ts`
|
||||
- Tajné kódy poskytovatele přetrvávaly v položkách `providerConnections`
|
||||
- Podpora odchozí proxy přes `open-sse/utils/proxyFetch.ts` (proměnné prostředí) a `open-sse/utils/networkProxy.ts` (konfigurovatelné pro jednotlivé poskytovatele nebo globálně)
|
||||
|
||||
## 5) Synchronizace s cloudem
|
||||
|
||||
- Inicializace plánovače: `src/lib/initCloudSync.ts` , `src/shared/services/initializeCloudSync.ts`
|
||||
- Periodická úloha: `src/shared/services/cloudSyncScheduler.ts`
|
||||
- Řídicí trasa: `src/app/api/sync/cloud/route.ts`
|
||||
|
||||
## Životní cyklus požadavku ( `/v1/chat/completions` )
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Client as CLI/SDK Client
|
||||
participant Route as /api/v1/chat/completions
|
||||
participant Chat as src/sse/handlers/chat
|
||||
participant Core as open-sse/handlers/chatCore
|
||||
participant Model as Model Resolver
|
||||
participant Auth as Credential Selector
|
||||
participant Exec as Provider Executor
|
||||
participant Prov as Upstream Provider
|
||||
participant Stream as Stream Translator
|
||||
participant Usage as usageDb
|
||||
|
||||
Client->>Route: POST /v1/chat/completions
|
||||
Route->>Chat: handleChat(request)
|
||||
Chat->>Model: parse/resolve model or combo
|
||||
|
||||
alt Combo model
|
||||
Chat->>Chat: iterate combo models (handleComboChat)
|
||||
end
|
||||
|
||||
Chat->>Auth: getProviderCredentials(provider)
|
||||
Auth-->>Chat: active account + tokens/api key
|
||||
|
||||
Chat->>Core: handleChatCore(body, modelInfo, credentials)
|
||||
Core->>Core: detect source format
|
||||
Core->>Core: translate request to target format
|
||||
Core->>Exec: execute(provider, transformedBody)
|
||||
Exec->>Prov: upstream API call
|
||||
Prov-->>Exec: SSE/JSON response
|
||||
Exec-->>Core: response + metadata
|
||||
|
||||
alt 401/403
|
||||
Core->>Exec: refreshCredentials()
|
||||
Exec-->>Core: updated tokens
|
||||
Core->>Exec: retry request
|
||||
end
|
||||
|
||||
Core->>Stream: translate/normalize stream to client format
|
||||
Stream-->>Client: SSE chunks / JSON response
|
||||
|
||||
Stream->>Usage: extract usage + persist history/log
|
||||
```
|
||||
|
||||
## Kombinovaný + záložní proces pro účet
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[Incoming model string] --> B{Is combo name?}
|
||||
B -- Yes --> C[Load combo models sequence]
|
||||
B -- No --> D[Single model path]
|
||||
|
||||
C --> E[Try model N]
|
||||
E --> F[Resolve provider/model]
|
||||
D --> F
|
||||
|
||||
F --> G[Select account credentials]
|
||||
G --> H{Credentials available?}
|
||||
H -- No --> I[Return provider unavailable]
|
||||
H -- Yes --> J[Execute request]
|
||||
|
||||
J --> K{Success?}
|
||||
K -- Yes --> L[Return response]
|
||||
K -- No --> M{Fallback-eligible error?}
|
||||
|
||||
M -- No --> N[Return error]
|
||||
M -- Yes --> O[Mark account unavailable cooldown]
|
||||
O --> P{Another account for provider?}
|
||||
P -- Yes --> G
|
||||
P -- No --> Q{In combo with next model?}
|
||||
Q -- Yes --> E
|
||||
Q -- No --> R[Return all unavailable]
|
||||
```
|
||||
|
||||
Rozhodnutí o záložních metodách jsou řízena souborem `open-sse/services/accountFallback.ts` s využitím stavových kódů a heuristik chybových zpráv.
|
||||
|
||||
## Životní cyklus aktualizace OAuth a onboardingu tokenu
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Dashboard UI
|
||||
participant OAuth as /api/oauth/[provider]/[action]
|
||||
participant ProvAuth as Provider Auth Server
|
||||
participant DB as localDb
|
||||
participant Test as /api/providers/[id]/test
|
||||
participant Exec as Provider Executor
|
||||
|
||||
UI->>OAuth: GET authorize or device-code
|
||||
OAuth->>ProvAuth: create auth/device flow
|
||||
ProvAuth-->>OAuth: auth URL or device code payload
|
||||
OAuth-->>UI: flow data
|
||||
|
||||
UI->>OAuth: POST exchange or poll
|
||||
OAuth->>ProvAuth: token exchange/poll
|
||||
ProvAuth-->>OAuth: access/refresh tokens
|
||||
OAuth->>DB: createProviderConnection(oauth data)
|
||||
OAuth-->>UI: success + connection id
|
||||
|
||||
UI->>Test: POST /api/providers/[id]/test
|
||||
Test->>Exec: validate credentials / optional refresh
|
||||
Exec-->>Test: valid or refreshed token info
|
||||
Test->>DB: update status/tokens/errors
|
||||
Test-->>UI: validation result
|
||||
```
|
||||
|
||||
Obnovení během živého provozu se provádí uvnitř `open-sse/handlers/chatCore.ts` pomocí exekutoru `refreshCredentials()` .
|
||||
|
||||
## Životní cyklus synchronizace s cloudem (Povolit / Synchronizovat / Zakázat)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant UI as Endpoint Page UI
|
||||
participant Sync as /api/sync/cloud
|
||||
participant DB as localDb
|
||||
participant Cloud as External Cloud Sync
|
||||
participant Claude as ~/.claude/settings.json
|
||||
|
||||
UI->>Sync: POST action=enable
|
||||
Sync->>DB: set cloudEnabled=true
|
||||
Sync->>DB: ensure API key exists
|
||||
Sync->>Cloud: POST /sync/{machineId} (providers/aliases/combos/keys)
|
||||
Cloud-->>Sync: sync result
|
||||
Sync->>Cloud: GET /{machineId}/v1/verify
|
||||
Sync-->>UI: enabled + verification status
|
||||
|
||||
UI->>Sync: POST action=sync
|
||||
Sync->>Cloud: POST /sync/{machineId}
|
||||
Cloud-->>Sync: remote data
|
||||
Sync->>DB: update newer local tokens/status
|
||||
Sync-->>UI: synced
|
||||
|
||||
UI->>Sync: POST action=disable
|
||||
Sync->>DB: set cloudEnabled=false
|
||||
Sync->>Cloud: DELETE /sync/{machineId}
|
||||
Sync->>Claude: switch ANTHROPIC_BASE_URL back to local (if needed)
|
||||
Sync-->>UI: disabled
|
||||
```
|
||||
|
||||
Pravidelnou synchronizaci spouští `CloudSyncScheduler` , když je povolen cloud.
|
||||
|
||||
## Datový model a mapa úložiště
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
SETTINGS ||--o{ PROVIDER_CONNECTION : controls
|
||||
PROVIDER_NODE ||--o{ PROVIDER_CONNECTION : backs_compatible_provider
|
||||
PROVIDER_CONNECTION ||--o{ USAGE_ENTRY : emits_usage
|
||||
|
||||
SETTINGS {
|
||||
boolean cloudEnabled
|
||||
number stickyRoundRobinLimit
|
||||
boolean requireLogin
|
||||
string password_hash
|
||||
string fallbackStrategy
|
||||
json rateLimitDefaults
|
||||
json providerProfiles
|
||||
}
|
||||
|
||||
PROVIDER_CONNECTION {
|
||||
string id
|
||||
string provider
|
||||
string authType
|
||||
string name
|
||||
number priority
|
||||
boolean isActive
|
||||
string apiKey
|
||||
string accessToken
|
||||
string refreshToken
|
||||
string expiresAt
|
||||
string testStatus
|
||||
string lastError
|
||||
string rateLimitedUntil
|
||||
json providerSpecificData
|
||||
}
|
||||
|
||||
PROVIDER_NODE {
|
||||
string id
|
||||
string type
|
||||
string name
|
||||
string prefix
|
||||
string apiType
|
||||
string baseUrl
|
||||
}
|
||||
|
||||
MODEL_ALIAS {
|
||||
string alias
|
||||
string targetModel
|
||||
}
|
||||
|
||||
COMBO {
|
||||
string id
|
||||
string name
|
||||
string[] models
|
||||
}
|
||||
|
||||
API_KEY {
|
||||
string id
|
||||
string name
|
||||
string key
|
||||
string machineId
|
||||
}
|
||||
|
||||
USAGE_ENTRY {
|
||||
string provider
|
||||
string model
|
||||
number prompt_tokens
|
||||
number completion_tokens
|
||||
string connectionId
|
||||
string timestamp
|
||||
}
|
||||
|
||||
CUSTOM_MODEL {
|
||||
string id
|
||||
string name
|
||||
string providerId
|
||||
}
|
||||
|
||||
PROXY_CONFIG {
|
||||
string global
|
||||
json providers
|
||||
}
|
||||
|
||||
IP_FILTER {
|
||||
string mode
|
||||
string[] allowlist
|
||||
string[] blocklist
|
||||
}
|
||||
|
||||
THINKING_BUDGET {
|
||||
string mode
|
||||
number customBudget
|
||||
string effortLevel
|
||||
}
|
||||
|
||||
SYSTEM_PROMPT {
|
||||
boolean enabled
|
||||
string prompt
|
||||
string position
|
||||
}
|
||||
```
|
||||
|
||||
Soubory fyzického úložiště:
|
||||
|
||||
- primární běhová databáze: `${DATA_DIR}/storage.sqlite`
|
||||
- řádky protokolu požadavku: `${DATA_DIR}/log.txt` (artefakt kompatibility/ladění)
|
||||
- Archivy strukturovaných dat volání: `${DATA_DIR}/call_logs/`
|
||||
- volitelné relace překladače/vyžádání ladění: `<repo>/logs/...`
|
||||
|
||||
## Topologie nasazení
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph LocalHost[Developer Host]
|
||||
CLI[CLI Tools]
|
||||
Browser[Dashboard Browser]
|
||||
end
|
||||
|
||||
subgraph ContainerOrProcess[OmniRoute Runtime]
|
||||
Next[Next.js Server\nPORT=20128]
|
||||
Core[SSE Core + Executors]
|
||||
MainDB[(storage.sqlite)]
|
||||
UsageDB[(usage tables + log artifacts)]
|
||||
end
|
||||
|
||||
subgraph External[External Services]
|
||||
Providers[AI Providers]
|
||||
SyncCloud[Cloud Sync Service]
|
||||
end
|
||||
|
||||
CLI --> Next
|
||||
Browser --> Next
|
||||
Next --> Core
|
||||
Next --> MainDB
|
||||
Core --> MainDB
|
||||
Core --> UsageDB
|
||||
Core --> Providers
|
||||
Next --> SyncCloud
|
||||
```
|
||||
|
||||
## Mapování modulů (kritické pro rozhodnutí)
|
||||
|
||||
### Moduly tras a API
|
||||
|
||||
- `src/app/api/v1/*` , `src/app/api/v1beta/*` : API pro zajištění kompatibility
|
||||
- `src/app/api/v1/providers/[provider]/*` : vyhrazené trasy pro jednotlivé poskytovatele (chat, vkládání, obrázky)
|
||||
- `src/app/api/providers*` : CRUD poskytovatele, validace, testování
|
||||
- `src/app/api/provider-nodes*` : správa uzlů kompatibilních s vlastními nástroji
|
||||
- `src/app/api/provider-models` : správa vlastních modelů (CRUD)
|
||||
- `src/app/api/models/route.ts` : API katalogu modelů (aliasy + vlastní modely)
|
||||
- `src/app/api/oauth/*` : Toky OAuth/kódu zařízení
|
||||
- `src/app/api/keys*` : životní cyklus lokálního klíče API
|
||||
- `src/app/api/models/alias` : správa aliasů
|
||||
- `src/app/api/combos*` : správa záložních kombinací
|
||||
- `src/app/api/pricing` : přepsání cen pro výpočet nákladů
|
||||
- `src/app/api/settings/proxy` : konfigurace proxy (GET/PUT/DELETE)
|
||||
- `src/app/api/settings/proxy/test` : test připojení odchozí proxy (POST)
|
||||
- `src/app/api/usage/*` : API pro použití a protokoly
|
||||
- `src/app/api/sync/*` + `src/app/api/cloud/*` : synchronizace s cloudem a pomocníci pro práci s cloudem
|
||||
- `src/app/api/cli-tools/*` : lokální programy pro zápis/kontrolu konfigurace CLI
|
||||
- `src/app/api/settings/ip-filter` : Seznam povolených/blokovaných IP adres (GET/PUT)
|
||||
- `src/app/api/settings/thinking-budget` : konfigurace rozpočtu tokenu thinking (GET/PUT)
|
||||
- `src/app/api/settings/system-prompt` : globální systémový příkaz (GET/PUT)
|
||||
- `src/app/api/sessions` : výpis aktivních relací (GET)
|
||||
- `src/app/api/rate-limits` : stav limitu rychlosti pro účet (GET)
|
||||
|
||||
### Směrovací a spouštěcí jádro
|
||||
|
||||
- `src/sse/handlers/chat.ts` : parsování požadavků, zpracování kombinací, smyčka výběru účtu
|
||||
- `open-sse/handlers/chatCore.ts` : překlad, odeslání exekutoru, zpracování opakování/obnovení, nastavení streamu
|
||||
- `open-sse/executors/*` : chování sítě a formátu specifické pro poskytovatele
|
||||
|
||||
### Registr překladů a převodníky formátů
|
||||
|
||||
- `open-sse/translator/index.ts` : registr a orchestrace překladačů
|
||||
- Žádost o překladatele: `open-sse/translator/request/*`
|
||||
- Překladače odpovědí: `open-sse/translator/response/*`
|
||||
- Formátovací konstanty: `open-sse/translator/formats.ts`
|
||||
|
||||
### Perzistence
|
||||
|
||||
- `src/lib/db/*` : perzistentní ukládání konfigurace/stavu a domény v SQLite
|
||||
- `src/lib/localDb.ts` : reexport kompatibility pro databázové moduly
|
||||
- `src/lib/usageDb.ts` : fasáda historie použití/záznamů volání nad tabulkami SQLite
|
||||
|
||||
## Pokrytí poskytovatele a vykonavatele (strategický vzorec)
|
||||
|
||||
Každý poskytovatel má specializovaný exekutor rozšiřující `BaseExecutor` (v `open-sse/executors/base.ts` ), který zajišťuje vytváření URL adres, konstrukci hlaviček, opakování s exponenciálním odkladem, hooky pro obnovení pověření a orchestrační metodu `execute()` .
|
||||
|
||||
Vykonavatel | Poskytovatel(é) | Speciální manipulace
|
||||
--- | --- | ---
|
||||
`DefaultExecutor` | OpenAI, Claude, Gemini, Qwen, iFlow, OpenRouter, GLM, Kimi, MiniMax, DeepSeek, Groq, xAI, Mistral, Perplexity, Together, Fireworks, Cerebras, Cohere, NVIDIA | Konfigurace dynamické adresy URL/záhlaví pro každého poskytovatele
|
||||
`AntigravityExecutor` | Google Antigravitace | Vlastní ID projektů/relací, analýza Opakování po
|
||||
`CodexExecutor` | Kodex OpenAI | Vkládá systémové instrukce, vynucuje úsilí k uvažování
|
||||
`CursorExecutor` | IDE kurzoru | Protokol ConnectRPC, kódování Protobuf, podepisování požadavků pomocí kontrolního součtu
|
||||
`GithubExecutor` | GitHub Copilot | Aktualizace tokenu Copilot, hlavičky napodobující VSCode
|
||||
`KiroExecutor` | AWS CodeWhisperer/Kiro | Binární formát AWS EventStream → konverze SSE
|
||||
`GeminiCLIExecutor` | Rozhraní příkazového řádku Gemini | Cyklus obnovy tokenu Google OAuth
|
||||
|
||||
Všichni ostatní poskytovatelé (včetně uzlů kompatibilních s vlastními funkcemi) používají `DefaultExecutor` .
|
||||
|
||||
## Matice kompatibility poskytovatelů
|
||||
|
||||
Poskytovatel | Formát | Autorizace | Proud | Nestreamované | Obnovení tokenu | API pro použití
|
||||
--- | --- | --- | --- | --- | --- | ---
|
||||
Claude | Claude | Klíč API / OAuth | ✅ | ✅ | ✅ | ⚠️ Pouze pro administrátory
|
||||
Blíženci | Blíženci | Klíč API / OAuth | ✅ | ✅ | ✅ | ⚠️ Cloudová konzole
|
||||
Rozhraní příkazového řádku Gemini | gemini-cli | OAuth | ✅ | ✅ | ✅ | ⚠️ Cloudová konzole
|
||||
Antigravitace | antigravitace | OAuth | ✅ | ✅ | ✅ | ✅ Plná kvóta API
|
||||
OpenAI | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Kodex | openai-odpovědi | OAuth | ✅ vynucený | ❌ | ✅ | ✅ Limity sazeb
|
||||
GitHub Copilot | otevřeno | OAuth + token Copilota | ✅ | ✅ | ✅ | ✅ Snímky kvót
|
||||
Kurzor | kurzor | Vlastní kontrolní součet | ✅ | ✅ | ❌ | ❌
|
||||
Kiro | Kiro | OIDC pro jednotné přihlašování AWS | ✅ (Stream událostí) | ❌ | ✅ | ✅ Limity použití
|
||||
Qwen | otevřeno | OAuth | ✅ | ✅ | ✅ | ⚠️ Na vyžádání
|
||||
iFlow | otevřeno | OAuth (základní) | ✅ | ✅ | ✅ | ⚠️ Na vyžádání
|
||||
OpenRouter | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
GLM/Kimi/MiniMax | Claude | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Hluboké vyhledávání | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Groq | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
xAI (Grok) | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Mistral | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Zmatek | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Společně s umělou inteligencí | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Ohňostroj s umělou inteligencí | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Mozky | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
Soudržný | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
NVIDIA NIM | otevřeno | Klíč API | ✅ | ✅ | ❌ | ❌
|
||||
|
||||
## Pokrytí překladů formátů
|
||||
|
||||
Mezi detekované zdrojové formáty patří:
|
||||
|
||||
- `openai`
|
||||
- `openai-responses`
|
||||
- `claude`
|
||||
- `gemini`
|
||||
|
||||
Cílové formáty zahrnují:
|
||||
|
||||
- Chat/Odpovědi v OpenAI
|
||||
- Claude
|
||||
- Obálka Gemini/Gemini-CLI/Antigravitace
|
||||
- Kiro
|
||||
- Kurzor
|
||||
|
||||
Překlady používají **jako ústřední formát OpenAI** – všechny konverze procházejí OpenAI jako zprostředkovatel:
|
||||
|
||||
```
|
||||
Source Format → OpenAI (hub) → Target Format
|
||||
```
|
||||
|
||||
Překlady jsou vybírány dynamicky na základě tvaru zdrojového datového obsahu a formátu cílového poskytovatele.
|
||||
|
||||
Další vrstvy zpracování v překladovém kanálu:
|
||||
|
||||
- **Sanitizace odpovědí** – Odstraňuje nestandardní pole z odpovědí ve formátu OpenAI (streamovaných i nestreamovaných), aby byla zajištěna přísná shoda se SDK.
|
||||
- **Normalizace rolí** — Převádí `developer` → `system` pro cíle mimo OpenAI; slučuje `system` → `user` pro modely, které odmítají systémovou roli (GLM, ERNIE)
|
||||
- **Extrakce tagu Think** — Analyzuje bloky `<think>...</think>` z obsahu do pole `reasoning_content`
|
||||
- **Strukturovaný výstup** — Převede OpenAI `response_format.json_schema` na `responseMimeType` + `responseSchema` z Gemini.
|
||||
|
||||
## Podporované koncové body API
|
||||
|
||||
Koncový bod | Formát | Psovod
|
||||
--- | --- | ---
|
||||
`POST /v1/chat/completions` | Chat s OpenAI | `src/sse/handlers/chat.ts`
|
||||
`POST /v1/messages` | Claude Messages | Stejný obslužný program (automaticky detekováno)
|
||||
`POST /v1/responses` | Reakce OpenAI | `open-sse/handlers/responsesHandler.ts`
|
||||
`POST /v1/embeddings` | Vkládání OpenAI | `open-sse/handlers/embeddings.ts`
|
||||
`GET /v1/embeddings` | Seznam modelů | Trasa API
|
||||
`POST /v1/images/generations` | Obrázky OpenAI | `open-sse/handlers/imageGeneration.ts`
|
||||
`GET /v1/images/generations` | Seznam modelů | Trasa API
|
||||
`POST /v1/providers/{provider}/chat/completions` | Chat s OpenAI | Vyhrazené pro každého poskytovatele s ověřováním modelu
|
||||
`POST /v1/providers/{provider}/embeddings` | Vkládání OpenAI | Vyhrazené pro každého poskytovatele s ověřováním modelu
|
||||
`POST /v1/providers/{provider}/images/generations` | Obrázky OpenAI | Vyhrazené pro každého poskytovatele s ověřováním modelu
|
||||
`POST /v1/messages/count_tokens` | Počet žetonů Claude | Trasa API
|
||||
`GET /v1/models` | Seznam modelů OpenAI | Trasa API (chat + vkládání + obrázek + vlastní modely)
|
||||
`GET /api/models/catalog` | Katalog | Všechny modely seskupené podle poskytovatele + typu
|
||||
`POST /v1beta/models/*:streamGenerateContent` | Rodák z Blíženců | Trasa API
|
||||
`GET/PUT/DELETE /api/settings/proxy` | Konfigurace proxy serveru | Konfigurace síťového proxy serveru
|
||||
`POST /api/settings/proxy/test` | Připojení proxy serveru | Koncový bod testu stavu/připojení proxy serveru
|
||||
`GET/POST/DELETE /api/provider-models` | Vlastní modely | Správa vlastních modelů pro každého poskytovatele
|
||||
|
||||
## Obejít obslužnou rutinu
|
||||
|
||||
Obslužná rutina bypassu ( `open-sse/utils/bypassHandler.ts` ) zachycuje známé „throwaway“ požadavky z Claude CLI – warmup pingy, extrakce titulků a počty tokenů – a vrací **falešnou odpověď** bez spotřebování tokenů upstreamového poskytovatele. Toto se spustí pouze tehdy, když `User-Agent` obsahuje `claude-cli` .
|
||||
|
||||
## Kanál protokolování požadavků
|
||||
|
||||
Záznamník požadavků ( `open-sse/utils/requestLogger.ts` ) poskytuje 7stupňový kanál protokolování ladění, ve výchozím nastavení zakázaný a povolený pomocí `ENABLE_REQUEST_LOGS=true` :
|
||||
|
||||
```
|
||||
1_req_client.json → 2_req_source.json → 3_req_openai.json → 4_req_target.json
|
||||
→ 5_res_provider.txt → 6_res_openai.txt → 7_res_client.txt
|
||||
```
|
||||
|
||||
Soubory se zapisují do `<repo>/logs/<session>/` pro každou relaci požadavku.
|
||||
|
||||
## Způsoby selhání a odolnost
|
||||
|
||||
## 1) Dostupnost účtu/poskytovatele
|
||||
|
||||
- Doba ochlazování účtu poskytovatele při přechodných chybách/chybách rychlosti/autentizace
|
||||
- záložní účet před selháním požadavku
|
||||
- záložní kombinovaný model, když je aktuální cesta modelu/poskytovatele vyčerpána
|
||||
|
||||
## 2) Platnost tokenu
|
||||
|
||||
- předběžná kontrola a obnovení s opakovaným pokusem o obnovení poskytovatelů
|
||||
- Opakování 401/403 po pokusu o obnovení v hlavní cestě
|
||||
|
||||
## 3) Bezpečnost streamu
|
||||
|
||||
- streamovací řadič s vědomím odpojení
|
||||
- překladový proud s vyprázdněním konce proudu a zpracováním `[DONE]`
|
||||
- Záložní odhad využití, když chybí metadata využití poskytovatele
|
||||
|
||||
## 4) Zhoršení cloudové synchronizace
|
||||
|
||||
- Zobrazují se chyby synchronizace, ale lokální běhové prostředí pokračuje.
|
||||
- Plánovač má logiku umožňující opakování, ale periodické provádění v současné době ve výchozím nastavení volá synchronizaci s jedním pokusem.
|
||||
|
||||
## 5) Integrita dat
|
||||
|
||||
- Migrace schématu SQLite a automatické aktualizace hooků při spuštění
|
||||
- Cesta kompatibility migrace starší verze JSON → SQLite
|
||||
|
||||
## Pozorovatelnost a provozní signály
|
||||
|
||||
Zdroje viditelnosti za běhu:
|
||||
|
||||
- protokoly konzole ze `src/sse/utils/logger.ts`
|
||||
- Agregace využití na požadavek v SQLite ( `usage_history` , `call_logs` , `proxy_logs` )
|
||||
- textový stav požadavku přihlášení `log.txt` (volitelné/kompatibilní)
|
||||
- volitelné hluboké protokoly požadavků/překladů v `logs/` pokud `ENABLE_REQUEST_LOGS=true`
|
||||
- Koncové body použití dashboardu ( `/api/usage/*` ) pro spotřebu v uživatelském rozhraní
|
||||
|
||||
## Hranice citlivé z hlediska zabezpečení
|
||||
|
||||
- Tajný kód JWT ( `JWT_SECRET` ) zajišťuje ověřování/podepisování souborů cookie relace dashboardu.
|
||||
- Počáteční bootstrap hesla ( `INITIAL_PASSWORD` ) by měl být explicitně nakonfigurován pro zřizování při prvním spuštění.
|
||||
- Tajný klíč API HMAC ( `API_KEY_SECRET` ) zabezpečuje formát vygenerovaného lokálního klíče API.
|
||||
- Tajné klíče/tokeny poskytovatele (klíče/tokeny API) jsou uloženy v lokální databázi a měly by být chráněny na úrovni souborového systému.
|
||||
- Koncové body synchronizace cloudu se spoléhají na sémantiku ověřování klíče API + ID počítače.
|
||||
|
||||
## Matice prostředí a běhového prostředí
|
||||
|
||||
Proměnné prostředí aktivně používané kódem:
|
||||
|
||||
- Aplikace/autentizace: `JWT_SECRET` , `INITIAL_PASSWORD`
|
||||
- Úložiště: `DATA_DIR`
|
||||
- Chování kompatibilního uzlu: `ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE`
|
||||
- Volitelné přepsání úložné základny (Linux/macOS, když `DATA_DIR` není nastaveno): `XDG_CONFIG_HOME`
|
||||
- Bezpečnostní hashování: `API_KEY_SECRET` , `MACHINE_ID_SALT`
|
||||
- Protokolování: `ENABLE_REQUEST_LOGS`
|
||||
- Synchronizace/cloudové URL: `NEXT_PUBLIC_BASE_URL` , `NEXT_PUBLIC_CLOUD_URL`
|
||||
- Odchozí proxy: `HTTP_PROXY` , `HTTPS_PROXY` , `ALL_PROXY` , `NO_PROXY` a varianty s malými písmeny
|
||||
- Příznaky funkcí SOCKS5: `ENABLE_SOCKS5_PROXY` , `NEXT_PUBLIC_ENABLE_SOCKS5_PROXY`
|
||||
- Pomocníci pro platformu/běhové prostředí (ne konfigurace specifická pro aplikaci): `APPDATA` , `NODE_ENV` , `PORT` , `HOSTNAME`
|
||||
|
||||
## Známé architektonické poznámky
|
||||
|
||||
1. `usageDb` a `localDb` sdílejí stejnou základní adresářovou politiku ( `DATA_DIR` -> `XDG_CONFIG_HOME/omniroute` -> `~/.omniroute` ) se starší migrací souborů.
|
||||
2. `/api/v1/route.ts` deleguje na stejný jednotný nástroj pro tvorbu katalogů, který používá `/api/v1/models` ( `src/app/api/v1/models/catalog.ts` ), aby se zabránilo sémantickému posunu.
|
||||
3. Pokud je povoleno, zaznamenávač požadavků zapisuje celé záhlaví/tělo; adresář protokolu je považován za citlivý.
|
||||
4. Chování cloudu závisí na správné adrese `NEXT_PUBLIC_BASE_URL` a dosažitelnosti cloudového koncového bodu.
|
||||
5. Adresář `open-sse/` je publikován jako **balíček npm workspace** `@omniroute/open-sse` . Zdrojový kód jej importuje přes `@omniroute/open-sse/...` (vyřešeno pomocí `transpilePackages` v Next.js). Cesty k souborům v tomto dokumentu stále používají název adresáře `open-sse/` pro účely konzistence.
|
||||
6. Grafy v dashboardu používají **Recharts** (založené na SVG) pro přístupné a interaktivní vizualizace analytiky (sloupcové grafy využití modelu, tabulky s rozpisem poskytovatelů s mírou úspěšnosti).
|
||||
7. E2E testy používají **Playwright** ( `tests/e2e/` ), spouštěné pomocí `npm run test:e2e` . Unit testy používají **Node.js test runner** ( `tests/unit/` ), spouštěné pomocí `npm run test:unit` . Zdrojový kód pod `src/` je **TypeScript** ( `.ts` / `.tsx` ); pracovní prostor `open-sse/` zůstává JavaScript ( `.js` ).
|
||||
8. Stránka nastavení je uspořádána do 5 záložek: Zabezpečení, Směrování (6 globálních strategií: fill-first, round robin, p2c, náhodné, nejméně používané, nákladově optimalizované), Odolnost (upravitelné limity rychlosti, jistič, zásady), AI (rozpočet promyšlený, systémový výzva, mezipaměť výzev), Pokročilé (proxy).
|
||||
|
||||
## Kontrolní seznam provozního ověření
|
||||
|
||||
- Sestavení ze zdroje: `npm run build`
|
||||
- Sestavení obrazu Dockeru: `docker build -t omniroute .`
|
||||
- Spusťte službu a ověřte:
|
||||
- `GET /api/settings`
|
||||
- `GET /api/v1/models`
|
||||
- Základní URL cíle CLI by měla být `http://<host>:20128/v1` , pokud `PORT=20128`
|
||||
63
docs/i18n/cs/AUTO-COMBO.md
Normal file
63
docs/i18n/cs/AUTO-COMBO.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# OmniRoute Auto-Combo Engine
|
||||
|
||||
> Samosprávné řetězce modelů s adaptivním bodováním
|
||||
|
||||
## Jak to funguje
|
||||
|
||||
Systém Auto-Combo dynamicky vybírá nejlepšího poskytovatele/model pro každý požadavek pomocí **6faktorové skórovací funkce** :
|
||||
|
||||
Faktor | Hmotnost | Popis
|
||||
:-- | :-- | :--
|
||||
Kvóta | 0,20 | Zbývající kapacita [0..1]
|
||||
Zdraví | 0,25 | Jistič: ZAVŘENO=1,0, POLOVINA=0,5, OTEVŘENO=0,0
|
||||
Náklady na fakturu | 0,20 | Inverzní náklady (levnější = vyšší skóre)
|
||||
LatencyInv | 0,15 | Inverzní latence p95 (rychlejší = vyšší)
|
||||
TaskFit | 0,10 | Skóre zdatnost modelu × typu úlohy
|
||||
Stabilita | 0,10 | Nízká variabilita latence/chyb
|
||||
|
||||
## Balíčky módů
|
||||
|
||||
Balíček | Soustředit | Hmotnost klíče
|
||||
:-- | :-- | :--
|
||||
🚀 **Rychlé odeslání** | Rychlost | latenceInv: 0,35
|
||||
💰 **Úspora nákladů** | Ekonomika | Náklady na účet: 0,40
|
||||
🎯 **Kvalita na prvním místě** | Nejlepší model | taskFit: 0,40
|
||||
📡 **Vhodné pro offline použití** | Dostupnost | kvóta: 0,40
|
||||
|
||||
## Samoléčení
|
||||
|
||||
- **Dočasné vyloučení** : Skóre < 0,2 → vyloučeno na 5 minut (postupné oddlužování, max. 30 minut)
|
||||
- **Upozornění na jistič** : OTEVŘENO → automatické vyloučení; POLOVIČNÍ OTEVŘENO → požadavky sondy
|
||||
- **Režim incidentu** : >50% OTEVŘENO → deaktivovat průzkum, maximalizovat stabilitu
|
||||
- **Obnova po zchlazení** : Po vyloučení je první požadavek „sonda“ se zkráceným časovým limitem.
|
||||
|
||||
## Průzkum banditů
|
||||
|
||||
5 % požadavků (konfigurovatelných) je směrováno k náhodným poskytovatelům k prozkoumání. V režimu incidentu je toto nastavení zakázáno.
|
||||
|
||||
## API
|
||||
|
||||
```bash
|
||||
# Create auto-combo
|
||||
curl -X POST http://localhost:20128/api/combos/auto \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"id":"my-auto","name":"Auto Coder","candidatePool":["anthropic","google","openai"],"modePack":"ship-fast"}'
|
||||
|
||||
# List auto-combos
|
||||
curl http://localhost:20128/api/combos/auto
|
||||
```
|
||||
|
||||
## Úkol Fitness
|
||||
|
||||
Více než 30 modelů hodnocených v 6 typech úkolů ( `coding` , `review` , `planning` , `analysis` , `debugging` , `documentation` ). Podporuje zástupné znaky (např. `*-coder` → vysoké skóre kódování).
|
||||
|
||||
## Soubory
|
||||
|
||||
Soubor | Účel
|
||||
:-- | :--
|
||||
`open-sse/services/autoCombo/scoring.ts` | Skórovací funkce a normalizace poolu
|
||||
`open-sse/services/autoCombo/taskFitness.ts` | Vyhledávání vhodnosti modelu × úkolu
|
||||
`open-sse/services/autoCombo/engine.ts` | Logika výběru, bandita, rozpočtový strop
|
||||
`open-sse/services/autoCombo/selfHealing.ts` | Vyloučení, sondy, režim incidentu
|
||||
`open-sse/services/autoCombo/modePacks.ts` | 4 hmotnostní profily
|
||||
`src/app/api/combos/auto/route.ts` | REST API
|
||||
715
docs/i18n/cs/CHANGELOG.md
Normal file
715
docs/i18n/cs/CHANGELOG.md
Normal file
@@ -0,0 +1,715 @@
|
||||
# Seznam změn
|
||||
|
||||
## [Nevydané]
|
||||
|
||||
---
|
||||
|
||||
## [2.7.8] — 18. 3. 2026
|
||||
|
||||
> Sprint: Chyba ukládání rozpočtu + funkce kombinovaného agenta v uživatelském rozhraní + oprava zabezpečení tagu omniModel.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(budget)** : „Uložit limity“ již nevrací chybu 422 — `warningThreshold` se nyní správně odesílá jako zlomek (0–1) místo procenta (0–100) (#451)
|
||||
- **oprava(kombinace)** : interní tag mezipaměti `<omniModel>` je nyní odstraněn před přeposíláním požadavků poskytovatelům, čímž se zabrání přerušení relace mezipaměti (#454)
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(combos)** : Do modálního okna pro vytváření/úpravy komb přidána sekce Funkce agenta – zpřístupnění přepsání `system_message` , `tool_filter_regex` a `context_cache_protection` přímo z dashboardu (#454)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.7] — 18. 3. 2026
|
||||
|
||||
> Sprint: Pád Dockeru pino, oprava workeru Codex CLI responses, synchronizace zámků balíčků.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava(docker)** : `pino-abstract-transport` a `pino-pretty` jsou nyní explicitně kopírovány ve fázi Docker Runner — Samostatné trasování Next.js tyto závislosti peerů přehlíží, což způsobuje pád `Cannot find module pino-abstract-transport` při spuštění (#449)
|
||||
- **fix(responses)** : Odstranění `initTranslators()` z trasy `/v1/responses` — worker Next.js `the worker has exited` uncaughtException při požadavcích Codex CLI (#450)
|
||||
|
||||
### 🔧 Údržba
|
||||
|
||||
- **chore(deps)** : `package-lock.json` je nyní commitován při každém upgradu verze, aby se zajistilo, že Docker `npm ci` použije přesné verze závislostí.
|
||||
|
||||
---
|
||||
|
||||
## [2.7.5] — 18. 3. 2026
|
||||
|
||||
> Sprint: Vylepšení uživatelského rozhraní a oprava kontroly stavu rozhraní Windows CLI.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(ux)** : Zobrazit na přihlašovací stránce nápovědu k výchozímu heslu — noví uživatelé nyní pod polem pro zadání hesla vidí `"Default password: 123456"` (#437)
|
||||
- **fix(cli)** : Claude CLI a další nástroje nainstalované npm jsou nyní správně detekovány jako spustitelné ve Windows — spawn používá `shell:true` k rozpoznání `.cmd` wrapperů přes PATHEXT (#447)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.4] — 18. 3. 2026
|
||||
|
||||
> Sprint: Panel vyhledávacích nástrojů, opravy i18n, limity Copilota, oprava validace Serperu.
|
||||
|
||||
### 🚀 Vlastnosti
|
||||
|
||||
- **feat(search)** : Přidáno hřiště pro vyhledávání (10. koncový bod), stránka s nástroji pro vyhledávání s porovnáním poskytovatelů/kanálovým přeřazením/historií vyhledávání, lokální směrování pro přeřazení, ochrana autorizace ve vyhledávacím API (#443 od @Regis-RCR)
|
||||
- Nová trasa: `/dashboard/search-tools`
|
||||
- Položka postranního panelu v sekci Ladění
|
||||
- `GET /api/search/providers` a `GET /api/search/stats` s ochranou autorizace
|
||||
- Lokální směrování provider_nodes pro `/v1/rerank`
|
||||
- 30+ klíčů i18n ve vyhledávacím jmenném prostoru
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(search)** : Oprava normalizátoru Brave News (vracel 0 výsledků), vynucení zkrácení max_results po normalizaci, oprava URL pro načítání stránek z koncových bodů (#443 od @Regis-RCR)
|
||||
- **fix(analytics)** : Lokalizace popisků dnů/dat v analytických nástrojích — nahrazení pevně zakódovaných portugalských řetězců pomocí `Intl.DateTimeFormat(locale)` (#444 od @hijak)
|
||||
- **oprava(copilot)** : Oprava zobrazení typu účtu GitHub Copilot, filtrování zavádějících řádků neomezených kvót z dashboardu limitů (#445 od @hijak)
|
||||
- **oprava(poskytovatelé)** : Zastavit odmítání platných klíčů Serper API – odpovědi jiné než 4xx považovat za platné ověřování (#446 od @hijak)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.3] — 18. 3. 2026
|
||||
|
||||
> Sprint: Oprava záložní kvóty pro přímé API Codexu.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava(codex)** : Blokování týdenních vyčerpávajících účtů v přímém záložním rozhraní API (#440)
|
||||
- Porovnávání prefixů `resolveQuotaWindow()` : `"weekly"` nyní odpovídá klíčům mezipaměti `"weekly (7d)"`
|
||||
- `applyCodexWindowPolicy()` správně vynucuje přepínání `useWeekly` / `use5h`
|
||||
- 4 nové regresní testy (celkem 766)
|
||||
|
||||
---
|
||||
|
||||
## [2.7.2] — 18. 3. 2026
|
||||
|
||||
> Sprint: Opravy kontrastu uživatelského rozhraní v režimu Light.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(logs)** : Oprava kontrastu světelného režimu v protokolech požadavků, tlačítek filtrů a kombinovaného odznaku (#378)
|
||||
- Tlačítka filtrů Chyba/Úspěch/Kombinace jsou nyní čitelná i ve světlém režimu.
|
||||
- Odznak kombinované řady používá ve světlém režimu silnější fialovou barvu
|
||||
|
||||
---
|
||||
|
||||
## [2.7.1] — 17. 3. 2026
|
||||
|
||||
> Sprint: Sjednocené směrování webového vyhledávání (POST /v1/search) s 5 poskytovateli + opravy zabezpečení Next.js 16.1.7 (6 CVE).
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **feat(search)** : Sjednocené směrování webového vyhledávání — `POST /v1/search` s 5 poskytovateli (Serper, Brave, Perplexity, Exa, Tavily)
|
||||
- Automatické přepnutí napříč poskytovateli, více než 6 500 bezplatných vyhledávání/měsíc
|
||||
- Mezipaměť v paměti se slučováním požadavků (konfigurovatelné TTL)
|
||||
- Dashboard: Karta Analytika vyhledávání v `/dashboard/analytics` s rozpisem poskytovatelů, mírou zásahů do mezipaměti a sledováním nákladů
|
||||
- Nové API: `GET /api/v1/search/analytics` pro statistiky vyhledávacích požadavků
|
||||
- Migrace databáze: sloupec `request_type` v `call_logs` pro sledování požadavků mimo chat
|
||||
- Ověření Zod ( `v1SearchSchema` ), chráněné autorizací, náklady zaznamenány pomocí `recordCost()`
|
||||
|
||||
### 🔒 Bezpečnost
|
||||
|
||||
- **deps** : Next.js 16.1.6 → 16.1.7 — opravuje 6 CVE:
|
||||
- **Kritické** : CVE-2026-29057 (pašování HTTP požadavků přes http-proxy)
|
||||
- **Vysoká** : CVE-2026-27977, CVE-2026-27978 (WebSocket + akce serveru)
|
||||
- **Médium** : CVE-2026-27979, CVE-2026-27980, CVE-2026-jcc7
|
||||
|
||||
### 📁 Nové soubory
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`open-sse/handlers/search.ts` | Vyhledávací obslužná rutina s routováním 5 poskytovatelů
|
||||
`open-sse/config/searchRegistry.ts` | Registr poskytovatelů (autorizace, náklady, kvóta, TTL)
|
||||
`open-sse/services/searchCache.ts` | Mezipaměť v paměti se slučováním požadavků
|
||||
`src/app/api/v1/search/route.ts` | Trasa Next.js (POST + GET)
|
||||
`src/app/api/v1/search/analytics/route.ts` | API pro statistiky vyhledávání
|
||||
`src/app/(dashboard)/dashboard/analytics/SearchAnalyticsTab.tsx` | Karta analytického panelu
|
||||
`src/lib/db/migrations/007_search_request_type.sql` | Migrace databáze
|
||||
`tests/unit/search-registry.test.mjs` | 277 řádků jednotkových testů
|
||||
|
||||
---
|
||||
|
||||
## [2.7.0] — 17. 3. 2026
|
||||
|
||||
> Sprint: Funkce inspirované ClawRouterem – příznak volání toolCalling, vícejazyčná detekce záměru, benchmarkem řízený fallback, deduplikace požadavků, plugin RouterStrategy, ceny Grok-4 Fast + GLM-5 + MiniMax M2.5 + Kimi K2.5.
|
||||
|
||||
### ✨ Nové modely a ceny
|
||||
|
||||
- **feat. (ceny)** : xAI Grok-4 Fast — `$0.20/$0.50 per 1M tokens` , latence 1143 ms p50, podpora volání nástrojů
|
||||
- **feat. (ceny)** : xAI Grok-4 (standardní) — `$0.20/$1.50 per 1M tokens` , což je důvodem k odmítnutí.
|
||||
- **výkon (ceny)** : GLM-5 přes Z.AI — `$0.5/1M` , 128 tisíc výstupních kontextů
|
||||
- **výkon (ceny)** : MiniMax M2.5 — `$0.30/1M input` , uvažování + agentní úkoly
|
||||
- **feat.(ceny)** : DeepSeek V3.2 — aktualizované ceny `$0.27/$1.10 per 1M`
|
||||
- **výkon (cena)** : Kimi K2.5 přes Moonshot API — přímý přístup k Moonshot API
|
||||
- **feat(providers)** : Přidán poskytovatel Z.AI (alias `zai` ) — rodina GLM-5 s výstupem 128K
|
||||
|
||||
### 🧠 Směrovací inteligence
|
||||
|
||||
- **feat(registry)** : příznak `toolCalling` pro každý model v registru poskytovatelů – kombinace nyní mohou preferovat/vyžadovat modely s možností volání nástrojů
|
||||
- **feat(scoring)** : Detekce vícejazyčného záměru pro skórování AutoCombo — skriptové/jazykové vzory PT/ZH/ES/AR ovlivňují výběr modelu podle kontextu požadavku
|
||||
- **feat(fallback)** : Řetězce záložních metod řízené benchmarky — skutečná data o latenci (p50 z `comboMetrics` ) používaná k dynamickému přeskupení priorit záložních metod
|
||||
- **feat(dedup)** : Vyžádání deduplikace pomocí content-hash — 5sekundové okno idempotence zabraňuje duplicitním voláním poskytovatele v opakovaném pokusu o odeslání klientům
|
||||
- **feat(router)** : Připojitelné rozhraní `RouterStrategy` v `autoCombo/routerStrategy.ts` — lze vložit vlastní logiku směrování bez úpravy jádra
|
||||
|
||||
### 🔧 Vylepšení serveru MCP
|
||||
|
||||
- **feat(mcp)** : 2 nová pokročilá schémata nástrojů: `omniroute_get_provider_metrics` (p50/p95/p99 na poskytovatele) a `omniroute_explain_route` (vysvětlení rozhodnutí o směrování)
|
||||
- **feat(mcp)** : Aktualizovány rozsahy autorizace nástroje MCP – přidán rozsah `metrics:read` pro nástroje pro metriky poskytovatelů
|
||||
- **feat(mcp)** : `omniroute_best_combo_for_task` nyní akceptuje parametr `languageHint` pro vícejazyčné směrování
|
||||
|
||||
### 📊 Pozorovatelnost
|
||||
|
||||
- **feat(metrics)** : Soubor `comboMetrics.ts` rozšířen o sledování percentilů latence v reálném čase pro každého poskytovatele/účet.
|
||||
- **feat(health)** : Rozhraní Health API ( `/api/monitoring/health` ) nyní vrací pole `p50Latency` a `errorRate` pro každého poskytovatele.
|
||||
- **feat(usage)** : Migrace historie použití pro sledování latence pro jednotlivé modely
|
||||
|
||||
### 🗄️ Migrace databází
|
||||
|
||||
- **feat(migrations)** : Nový sloupec `latency_p50` v tabulce `combo_metrics` — nulový, bezpečný pro stávající uživatele
|
||||
|
||||
### 🐛 Opravy chyb / Uzavření
|
||||
|
||||
- **close(#411)** : rozlišení hašovaných modulů better-sqlite3 ve Windows — opraveno ve verzi 2.6.10 (f02c5b5)
|
||||
- **close(#409)** : Dokončení chatu GitHub Copilot selhává u modelů Claude při připojení souborů – opraveno ve verzi 2.6.9 (838f1d6)
|
||||
- **close(#405)** : Duplikát #411 – vyřešeno
|
||||
|
||||
## [2.6.10] — 17. 3. 2026
|
||||
|
||||
> Oprava pro Windows: stažení předkompilovaného better-sqlite3 bez node-gyp/Pythonu/MSVC (#426).
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(install/#426)** : Ve Windows dříve selhával příkaz `npm install -g omniroute` s `better_sqlite3.node is not a valid Win32 application` , protože přiložený nativní binární soubor byl zkompilován pro Linux. Přidává **strategii 1.5** do `scripts/postinstall.mjs` : používá `@mapbox/node-pre-gyp install --fallback-to-build=false` (přiloženo v rámci `better-sqlite3` ) ke stažení správného předkompilovaného binárního souboru pro aktuální OS/arch bez nutnosti použití jakýchkoli nástrojů pro sestavení (žádný node-gyp, žádný Python, žádný MSVC). Vrací se k `npm rebuild` pouze v případě, že stahování selže. Přidává chybové zprávy specifické pro platformu s jasnými pokyny k ruční opravě.
|
||||
|
||||
---
|
||||
|
||||
## [2.6.9] — 17. 3. 2026
|
||||
|
||||
> Opravy CI (t11 s libovolným rozpočtem), oprava chyby č. 409 (souborové přílohy přes Copilot+Claude), korekce pracovního postupu vydání.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(ci)** : Odstranění slova „any“ z komentářů v `openai-responses.ts` a `chatCore.ts` , které neprošly kontrolou rozpočtu t11 `\bany\b` (falešně pozitivní výsledek z počítání regexů v komentářích).
|
||||
- **oprava(chatCore)** : Normalizovat nepodporované typy částí obsahu před přeposláním poskytovatelům (#409 — Kurzor odesílá `{type:"file"}` když jsou připojeny soubory `.md` ; Copilot a další poskytovatelé kompatibilní s OpenAI odmítají s "type musí být buď 'image_url', nebo 'text'"; oprava převádí bloky `file` / `document` na `text` a odstraňuje neznámé typy)
|
||||
|
||||
### 🔧 Pracovní postup
|
||||
|
||||
- **chore(generate-release)** : Přidat pravidlo pro atomický commit — navýšení verze ( `npm version patch` ) MUSÍ proběhnout před commitem souborů funkcí, aby se zajistilo, že tag vždy ukazuje na commit obsahující všechny změny verzí dohromady.
|
||||
|
||||
---
|
||||
|
||||
## [2.6.8] — 17. 3. 2026
|
||||
|
||||
> Sprint: Kombinace jako agent (systémový příkaz + filtr nástrojů), ochrana kontextového ukládání do mezipaměti, automatická aktualizace, podrobné protokoly, MITM Kiro IDE.
|
||||
|
||||
### 🗄️ Migrace databází (bez nutnosti aktualizace – bezpečné pro stávající uživatele)
|
||||
|
||||
- **005_combo_agent_fields.sql** : `ALTER TABLE combos ADD COLUMN system_message TEXT DEFAULT NULL` , `tool_filter_regex TEXT DEFAULT NULL` , `context_cache_protection INTEGER DEFAULT 0`
|
||||
- **006_detailed_request_logs.sql** : Nová tabulka `request_detail_logs` s triggerem kruhového bufferu s 500 záznamy, možnost přihlášení přes přepínač nastavení
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(combo)** : Přepsání systémových zpráv pro Combo (#399 — pole `system_message` nahrazuje nebo vkládá systémový výzvu před přesměrováním poskytovateli)
|
||||
- **feat(combo)** : Regulární výraz filtru nástrojů pro každou kombinaci (#399 — `tool_filter_regex` uchovává pouze nástroje odpovídající vzoru; podporuje formáty OpenAI + Anthropic)
|
||||
- **feat(combo)** : Ochrana před ukládáním do mezipaměti kontextu (#401 — `context_cache_protection` označuje odpovědi s `<omniModel>provider/model</omniModel>` a modelem pins pro zajištění kontinuity relace)
|
||||
- **feat(settings)** : Automatická aktualizace přes Nastavení (#320 — `GET /api/system/version` + `POST /api/system/update` — kontroluje registr npm a aktualizuje na pozadí s restartem pm2)
|
||||
- **feat(logs)** : Podrobné protokoly požadavků (#378 — zachycuje kompletní těla procesů ve 4 fázích: požadavek klienta, přeložený požadavek, odpověď poskytovatele, odpověď klienta — přepínání přihlášení, ořezávání na 64 kB, kruhová vyrovnávací paměť s 500 záznamy)
|
||||
- **feat(mitm)** : Profil MITM Kiro IDE (#336 — `src/mitm/targets/kiro.ts` cílí na api.anthropic.com, znovu využívá stávající infrastrukturu MITM)
|
||||
|
||||
---
|
||||
|
||||
## [2.6.7] — 17. 3. 2026
|
||||
|
||||
> Sprint: Vylepšení SSE, rozšíření lokálních provider_nodes, registr proxy, opravy Claude passthrough.
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(health)** : Kontrola stavu lokálních `provider_nodes` na pozadí s exponenciálním zpožděním (30s→300s) a `Promise.allSettled` pro zamezení blokování (#423, @Regis-RCR)
|
||||
- **feat(embeddings)** : Směrování `/v1/embeddings` do lokálních uzlů `provider_nodes` — `buildDynamicEmbeddingProvider()` s ověřením názvu hostitele (#422, @Regis-RCR)
|
||||
- **feat(audio)** : Směrování TTS/STT do lokálních `provider_nodes` — `buildDynamicAudioProvider()` s ochranou SSRF (#416, @Regis-RCR)
|
||||
- **feat(proxy)** : Registr proxy, API pro správu a zobecnění limitů kvót (#429, @Regis-RCR)
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(sse)** : Odstranění polí specifických pro Claude ( `metadata` , `anthropic_version` ), pokud je cíl kompatibilní s OpenAI (#421, @prakersh)
|
||||
- **fix(sse)** : Extrahuje využití Claude SSE ( `input_tokens` , `output_tokens` , cache tokeny) v režimu průchozího streamu (#420, @prakersh)
|
||||
- **fix(sse)** : Generování záložního `call_id` pro volání nástrojů s chybějícími/prázdnými ID (#419, @prakersh)
|
||||
- **oprava(sse)** : Průchod mezi Claudey a Claudey — přední tělo zcela nedotčeno, bez opětovného překladu (#418, @prakersh)
|
||||
- **fix(sse)** : Filtrovat osiřelé položky `tool_result` po zhuštění kontextu Claude Code, aby se zabránilo chybám 400 (#417, @prakersh)
|
||||
- **fix(sse)** : Přeskočit volání nástrojů s prázdnými názvy v překladači Responses API, aby se zabránilo nekonečným smyčkám `placeholder_tool` (#415, @prakersh)
|
||||
- **fix(sse)** : Odstranění prázdných bloků textového obsahu před překladem (#427, @prakersh)
|
||||
- **fix(api)** : Přidáno `refreshable: true` do testovací konfigurace Claude OAuth (#428, @prakersh)
|
||||
|
||||
### 📦 Závislosti
|
||||
|
||||
- Zvýšení `vitest` , `@vitest/*` a související devDependencies (#414, @dependabot)
|
||||
|
||||
---
|
||||
|
||||
## [2.6.6] — 17. 3. 2026
|
||||
|
||||
> Oprava: Kompatibilita s Turbopackem/Dockerem — odebrání protokolu `node:` ze všech importů `src/` .
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(build)** : Z příkazů `import` v 17 souborech v `src/` byl odstraněn prefix `node:` protocol. Importy `node:fs` , `node:path` , `node:url` , `node:os` atd. způsobovaly, že `Ecmascript file had an error` v sestaveních Turbopack (Next.js 15 Docker) a při upgradech ze starších globálních instalací npm. Dotčené soubory: `migrationRunner.ts` , `core.ts` , `backup.ts` , `prompts.ts` , `dataPaths.ts` a 12 dalších v `src/app/api/` a `src/lib/` .
|
||||
- **chore(workflow)** : Aktualizován `generate-release.md` , aby synchronizace Docker Hubu a nasazení duálního VPS zahrnovaly **povinné** kroky v každé verzi.
|
||||
|
||||
---
|
||||
|
||||
## [2.6.5] — 17. 3. 2026
|
||||
|
||||
> Sprint: filtrování parametrů modelu uvažování, oprava chyby 404 lokálního poskytovatele, poskytovatel Kilo Gateway, vylepšení závislostí.
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **feat(api)** : Přidán **Kilo Gateway** ( `api.kilo.ai` ) jako nový poskytovatel API klíčů (alias `kg` ) — více než 335 modelů, 6 bezplatných modelů, 3 modely automatického směrování ( `kilo-auto/frontier` , `kilo-auto/balanced` , `kilo-auto/free` ). Průchozí modely podporovány přes endpoint `/api/gateway/models` . (PR #408 od @Regis-RCR)
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(sse)** : Odstranění nepodporovaných parametrů pro modely uvažování (o1, o1-mini, o1-pro, o3, o3-mini). Modely v rodině `o1` / `o3` odmítají `temperature` , `top_p` , `frequency_penalty` , `presence_penalty` , `logprobs` , `top_logprobs` a `n` s HTTP 400. Parametry jsou nyní odstraňovány na vrstvě `chatCore` před přeposíláním. Používá deklarativní pole `unsupportedParams` pro každý model a předpočítanou mapu O(1) pro vyhledávání. (PR #412 od @Regis-RCR)
|
||||
- **fix(sse)** : Kód 404 lokálního poskytovatele nyní vede k **uzamčení pouze modelu (5 sekund)** namísto uzamčení na úrovni připojení (2 minuty). Když lokální inferenční backend (Ollama, LM Studio, oMLX) vrátí kód 404 pro neznámý model, připojení zůstane aktivní a ostatní modely okamžitě pokračují v práci. Také opravuje již existující chybu, kdy `model` nebyl předán funkci `markAccountUnavailable()` . Lokální poskytovatelé detekováni pomocí názvu hostitele ( `localhost` , `127.0.0.1` , `::1` , rozšiřitelné pomocí proměnné prostředí `LOCAL_HOSTNAMES` ). (PR #410 od @Regis-RCR)
|
||||
|
||||
### 📦 Závislosti
|
||||
|
||||
- `better-sqlite3` 12.6.2 → 12.8.0
|
||||
- `undici` 7.24.2 → 7.24.4
|
||||
- `https-proxy-agent` 7 → 8
|
||||
- `agent-base` 7 → 8
|
||||
|
||||
---
|
||||
|
||||
## [2.6.4] — 17. 3. 2026
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(providers)** : Odstraněny neexistující názvy modelů u 5 poskytovatelů:
|
||||
- **gemini / gemini-cli** : odstraněny `gemini-3.1-pro/flash` a `gemini-3-*-preview` (neexistují v Google API v1beta); nahrazeny `gemini-2.5-pro` , `gemini-2.5-flash` , `gemini-2.0-flash` , `gemini-1.5-pro/flash`
|
||||
- **antigravity** : odstraněny `gemini-3.1-pro-high/low` a `gemini-3-flash` (neplatné interní aliasy); nahrazeny skutečnými modely z verze 2.x
|
||||
- **github (Copilot)** : odstraněny `gemini-3-flash-preview` a `gemini-3-pro-preview` ; nahrazeny `gemini-2.5-flash`
|
||||
- **nvidia** : opraveno `nvidia/llama-3.3-70b-instruct` → `meta/llama-3.3-70b-instruct` (NVIDIA NIM používá pro modely Meta jmenný prostor `meta/` /); přidány `nvidia/llama-3.1-70b-instruct` a `nvidia/llama-3.1-405b-instruct`
|
||||
- **fix(db/combo)** : Aktualizováno `free-stack` combo na vzdálené databázi: odstraněno `qw/qwen3-coder-plus` (prošlý obnovovací token), opraveno `nvidia/llama-3.3-70b-instruct` → `nvidia/meta/llama-3.3-70b-instruct` , opraveno `gemini/gemini-3.1-flash` → `gemini/gemini-2.5-flash` , přidáno `if/deepseek-v3.2`
|
||||
|
||||
---
|
||||
|
||||
## [2.6.3] — 16. 3. 2026
|
||||
|
||||
> Sprint: hash-strip zod/pino zapečený do build pipeline, přidán syntetický poskytovatel, opravena cesta VPS PM2.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(build)** : Turbopack hash-strip se nyní spouští při **kompilaci** pro VŠECHNY balíčky — nejen `better-sqlite3` . Krok 5.6 v `prepublish.mjs` prochází každý `.js` v `app/.next/server/` a odstraňuje 16znakovou hexadecimální příponu z jakékoli hashované `require()` . Opravuje `zod-dcb22c...` , `pino-...` atd. MODULE_NOT_FOUND u globálních instalací npm. Zavírá #398.
|
||||
- **Oprava (nasazení)** : PM2 na obou VPS ukazoval na zastaralé adresáře git-clone. V globálním balíčku npm překonfigurováno na `app/server.js` . Aktualizován pracovní postup `/deploy-vps` pro použití `npm pack + scp` (registr npm odmítá balíčky o velikosti 299 MB).
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(provider)** : Synthetic ( [synthetic.new](https://synthetic.new) ) — inference kompatibilní s OpenAI zaměřená na soukromí. `passthroughModels: true` pro dynamický katalog modelů HuggingFace. Počáteční modely: Kimi K2.5, MiniMax M2.5, GLM 4.7, DeepSeek V3.2. (PR #404 od @Regis-RCR)
|
||||
|
||||
### 📋 Problémy uzavřeny
|
||||
|
||||
- **zavřít #398** : regrese hashování npm — opraveno hashováním při kompilaci v prepublish
|
||||
- **triáž č. 324** : Snímek obrazovky s chybou bez kroků – požadovány podrobnosti o reprodukci
|
||||
|
||||
---
|
||||
|
||||
## [2.6.2] — 16. 3. 2026
|
||||
|
||||
> Sprint: hashování modulů kompletně opraveno, sloučeny 2 PR (filtr Anthropic tools + vlastní cesty k endpointům), přidán poskytovatel Alibaba Cloud DashScope, uzavřeny 3 zastaralé problémy.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(build)** : Rozšířeno hashování `externals` webpacku tak, aby zahrnovalo VŠECHNY `serverExternalPackages` , nejen `better-sqlite3` . Next.js 16 Turbopack hashuje `zod` , `pino` a všechny ostatní externí balíčky serveru do názvů jako `zod-dcb22c6336e0bc69` , které za běhu v `node_modules` neexistují. HASH_PATTERN regex catch-all nyní odstraňuje 16znakovou příponu a vrací se k základnímu názvu balíčku. Také přidána `NEXT_PRIVATE_BUILD_WORKER=0` v `prepublish.mjs` pro posílení režimu webpacku a následné skenování po sestavení, které hlásí všechny zbývající hashované reference. (#396, #398, PR #403)
|
||||
- **fix(chat)** : Názvy nástrojů v anthropic formátu ( `tool.name` bez wrapperu `.function` ) byly tiše vynechány filtrem prázdných názvů zavedeným v bodě #346. LiteLLM proxyuje požadavky s prefixem `anthropic/` ve formátu Anthropic Messages API, což způsobuje filtrování všech nástrojů a Anthropic vrací chybu `400: tool_choice.any may only be specified while providing tools` . Opraveno návratem k `tool.name` , když chybí `tool.function.name` . Přidáno 8 regresních jednotkových testů. (PR #397)
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(api)** : Vlastní cesty koncových bodů pro uzly poskytovatelů kompatibilní s OpenAI — konfigurace `chatPath` a `modelsPath` pro každý uzel (např. `/v4/chat/completions` ) v uživatelském rozhraní pro připojení poskytovatele. Zahrnuje migraci databáze ( `003_provider_node_custom_paths.sql` ) a sanitizaci cesty URL (bez `..` traversal, musí začínat znakem `/` ). (PR #400)
|
||||
- **feat(provider)** : Alibaba Cloud DashScope přidán jako poskytovatel kompatibilní s OpenAI. Mezinárodní endpoint: `dashscope-intl.aliyuncs.com/compatible-mode/v1` . 12 modelů: `qwen-max` , `qwen-plus` , `qwen-turbo` , `qwen3-coder-plus/flash` , `qwq-plus` , `qwq-32b` , `qwen3-32b` , `qwen3-235b-a22b` . Autorizace: Nosný API klíč.
|
||||
|
||||
### 📋 Problémy uzavřeny
|
||||
|
||||
- **zavřít #323** : Chyba připojení Cline `[object Object]` – opraveno ve verzi 2.3.7; uživateli bylo doručeno pokyny k upgradu z verze 2.2.9
|
||||
- **zavřít #337** : Sledování úvěru Kiro — implementováno ve verzi 2.5.5 (#381); odkázalo uživatele na Dashboard → Použití
|
||||
- **triage #402** : Poškozený soubor ARM64 macOS DMG – požadovaná verze macOS, přesná chyba a doporučené alternativní řešení `xattr -d com.apple.quarantine`
|
||||
|
||||
---
|
||||
|
||||
## [2.6.1] — 15. 3. 2026
|
||||
|
||||
> Kritická oprava při spuštění: Globální instalace npm v2.6.0 havarovaly s chybou 500 kvůli chybě hashování názvů modulů Turbopack/webpack v instrumentačním hooku Next.js 16.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(build)** : Vynutit, aby byl `better-sqlite3` vždy vyžadován přesným názvem balíčku v balíčku webpack server. Next.js 16 zkompiloval instrumentační hook do samostatného chunku a vygeneroval `require('better-sqlite3-<hash>')` — hashovaný název modulu, který neexistuje v `node_modules` — přestože byl balíček uveden v `serverExternalPackages` . Do konfigurace webpacku serveru byla přidána explicitní funkce `externals` , takže bundler vždy vygeneruje `require('better-sqlite3')` , čímž se vyřeší `500 Internal Server Error` při spuštění čistých globálních instalací. (#394, PR #395)
|
||||
|
||||
### 🔧 CI
|
||||
|
||||
- **ci** : Do `npm-publish.yml` přidána `workflow_dispatch` se zabezpečením synchronizace verzí pro manuální spouštěče (#392).
|
||||
- **ci** : Přidán `workflow_dispatch` do `docker-publish.yml` , aktualizovány akce GitHubu na nejnovější verze (#392)
|
||||
|
||||
---
|
||||
|
||||
## [2.6.0] - 15. 3. 2026
|
||||
|
||||
> Sprint řešení problémů: Opraveny 4 chyby, vylepšeno uživatelské rozhraní protokolů, přidáno sledování kreditů Kiro.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava(média)** : ComfyUI a SD WebUI se již nezobrazují v seznamu poskytovatelů na stránce Média, pokud nejsou nakonfigurovány — při připojení načtou `/api/providers` a skryjí lokální poskytovatele bez připojení (#390)
|
||||
- **oprava(auth)** : Round-robin již po zpoždění znovu nevybírá účty s omezenou rychlostí ihned – `backoffLevel` se nyní používá jako primární třídicí klíč v rotaci LRU (#340)
|
||||
- **oprava(oauth)** : iFlow (a další poskytovatelé, kteří přesměrovávají na své vlastní uživatelské rozhraní) již nenechávají modální okno OAuth zaseknuté na „Čekání na autorizaci“ – detektor zavřených vyskakovacích oken automaticky přechází do režimu ručního zadávání URL (#344)
|
||||
- **oprava(logy)** : Tabulka protokolů požadavků je nyní čitelná ve světlém režimu – stavové odznaky, počty tokenů a kombinované tagy používají adaptivní `dark:` barevné třídy (#378)
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **feat(kiro)** : Do fetcheru využití přidáno sledování kreditů Kiro — dotazy `getUserCredits` z endpointu AWS CodeWhisperer (#337)
|
||||
|
||||
### 🛠 Domácí práce
|
||||
|
||||
- **chore(tests)** : Zarovnání `test:plan3` , `test:fixes` , `test:security` pro použití stejného zavaděče `tsx/esm` jako u `npm test` – eliminuje falešně negativní výsledky rozlišení modulů v cílených bězích (PR #386)
|
||||
|
||||
---
|
||||
|
||||
## [2.5.9] - 15. 3. 2026
|
||||
|
||||
> Oprava nativní passthrough Codexu + posílení validace těla trasy.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(codex)** : Zachovává nativní průchod Responses API pro klienty Codexu – zabraňuje zbytečným mutacím překladu (PR #387)
|
||||
- **fix(api)** : Ověřování těl požadavků na trasách pro stanovení cen/synchronizaci a směrování úloh – zabraňuje pádům způsobeným chybně formátovanými vstupy (PR #388)
|
||||
- **fix(auth)** : Tajné hodnoty JWT přetrvávají i po restartech pomocí `src/lib/db/secrets.ts` — eliminuje chyby 401 po restartu PM2 (PR #388)
|
||||
|
||||
---
|
||||
|
||||
## [2.5.8] - 15. 3. 2026
|
||||
|
||||
> Oprava sestavení: obnovení připojení VPS přerušeného nedokončeným publikováním v2.5.7.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava(build)** : `scripts/prepublish.mjs` se stále používají, zastaralý příznak `--webpack` způsobuje tiché selhání samostatného sestavení Next.js — publikování npm dokončeno bez `app/server.js` , což narušuje nasazení VPS
|
||||
|
||||
---
|
||||
|
||||
## [2.5.7] - 15. 3. 2026
|
||||
|
||||
> Opravy chyb při zpracování v Media Playground.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava(média)** : Přepis „Vyžadován klíč API“ falešně pozitivní, pokud zvuk neobsahuje žádnou řeč (hudba, ticho) – nyní se místo toho zobrazuje „Není detekována žádná řeč“
|
||||
- **oprava(media)** : `upstreamErrorResponse` v `audioTranscription.ts` a `audioSpeech.ts` nyní vrací správný JSON ( `{error:{message}}` ), což umožňuje správnou detekci chyb přihlašovacích údajů 401/403 v MediaPageClient
|
||||
- **oprava(média)** : `parseApiError` nyní zpracovává pole `err_msg` v Deepgramu a detekuje `"api key"` v chybových zprávách pro přesnou klasifikaci chyb přihlašovacích údajů.
|
||||
|
||||
---
|
||||
|
||||
## [2.5.6] - 15. 3. 2026
|
||||
|
||||
> Kritické opravy zabezpečení/autentizace: OAuth v Antigravity nefunkční + relace JWT ztraceny po restartu.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(oauth) #384** : Antigravity Google OAuth nyní správně odesílá `client_secret` do koncového bodu tokenu. Záložní volbou pro `ANTIGRAVITY_OAUTH_CLIENT_SECRET` byl prázdný řetězec, což je chyba – `client_secret` tedy nebyl v požadavku nikdy zahrnut, což způsobovalo chyby `"client_secret is missing"` u všech uživatelů bez vlastní proměnné prostředí. Zavírá #383.
|
||||
- **fix(auth) #385** : `JWT_SECRET` je nyní ukládán do SQLite ( `namespace='secrets'` ) při první generaci a znovu načten při následných spuštěních. Dříve byl při každém spuštění procesu generován nový náhodný tajný klíč, který po jakémkoli restartu nebo upgradu zneplatňoval všechny existující soubory cookie/relace. Ovlivňuje `JWT_SECRET` i `API_KEY_SECRET` . Zavírá #382.
|
||||
|
||||
---
|
||||
|
||||
## [2.5.5] - 15. 3. 2026
|
||||
|
||||
> Oprava odstranění duplicitních dat v seznamu modelů, posílení samostatného sestavení Electronu a sledování kreditů Kiro.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **fix(models) #380** : `GET /api/models` nyní zahrnuje aliasy poskytovatelů při sestavování filtru aktivního poskytovatele — modely pro `claude` (alias `cc` ) a `github` (alias `gh` ) se vždy zobrazovaly bez ohledu na to, zda bylo nakonfigurováno připojení, protože klíče `PROVIDER_MODELS` jsou aliasy, ale připojení k databázi jsou uložena pod ID poskytovatelů. Opraveno rozšířením každého aktivního ID poskytovatele o jeho alias pomocí `PROVIDER_ID_TO_ALIAS` . Zavírá #353.
|
||||
- **fix(electron) #379** : Nové `scripts/prepare-electron-standalone.mjs` připraví vyhrazený balíček `/.next/electron-standalone` před zabalením Electronu. Pokud je `node_modules` symbolický odkaz, dojde k ukončení s chybou (electron-builder by na sestavovací stroj odeslal běhovou závislost). Multiplatformní sanitizace cest pomocí `path.basename` . Od @kfiramar.
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **feat(kiro) #381** : Sledování zůstatku kreditů Kiro — koncový bod využití nyní vrací data o kreditech pro Kiro účty voláním `codewhisperer.us-east-1.amazonaws.com/getUserCredits` (stejný koncový bod, který Kiro IDE používá interně). Vrací zbývající kredity, celkový limit, datum obnovení a úroveň předplatného. Uzavírá #337.
|
||||
|
||||
## [2.5.4] - 15. 3. 2026
|
||||
|
||||
> Oprava spouštění loggeru, oprava zabezpečení přihlašovacího bootstrapu a vylepšení spolehlivosti vývojářského HMR. Zlepšení infrastruktury CI.
|
||||
|
||||
### 🐛 Opravy chyb (PR #374, #375, #376 od @kfiramar)
|
||||
|
||||
- **oprava(logger) #376** : Obnovit cestu k protokolovacímu modulu pino transport — `formatters.level` v kombinaci s `transport.targets` je odmítnut modulem pino. Konfigurace založené na transportu nyní odstraňují formátovač úrovní pomocí funkce `getTransportCompatibleConfig()` . Také opravuje numerické mapování úrovní v `/api/logs/console` : `30→info, 40→warn, 50→error` (bylo posunuto o jednu).
|
||||
- **oprava(login) #375** : Přihlašovací stránka se nyní bootuje z veřejného endpointu `/api/settings/require-login` namísto chráněného `/api/settings` . V nastaveních chráněných heslem dostávala stránka předběžného ověřování chybu 401 a zbytečně se vracela k bezpečným výchozím hodnotám. Veřejná trasa nyní vrací všechna bootstrapová metadata ( `requireLogin` , `hasPassword` , `setupComplete` ) s konzervativní fallback chybou 200.
|
||||
- **oprava(dev) #374** : Přidání `localhost` a `127.0.0.1` do `allowedDevOrigins` v `next.config.mjs` — HMR websocket byl blokován při přístupu k aplikaci přes loopback adresu, což opakovaně produkovalo varování cross-origin.
|
||||
|
||||
### 🔧 CI a infrastruktura
|
||||
|
||||
- **Oprava chyb ESLint OOM** : `eslint.config.mjs` nyní ignoruje `vscode-extension/**` , `electron/**` , `docs/**` , `app/.next/**` a `clipr/**` — ESLint havaroval s chybou JS haldy OOM skenováním binárních blobů a kompilovaných chunků VS Code.
|
||||
- **Oprava jednotkového testu** : Z 2 testovacích souborů byl odstraněn zastaralý `ALTER TABLE provider_connections ADD COLUMN "group"` – sloupec je nyní součástí základního schématu (přidáno v #373), což způsobovalo `SQLITE_ERROR: duplicate column name` při každém spuštění CI.
|
||||
- **Pre-commit hook** : Do `.husky/pre-commit` přidán `npm run test:unit` — unit testy nyní blokují poškozené commity dříve, než se dostanou do CI.
|
||||
|
||||
## [2.5.3] - 14. 3. 2026
|
||||
|
||||
> Opravy kritických chyb: migrace schématu databáze, načítání spouštěcího prostředí, mazání chyb poskytovatele a oprava popisků i18n. Vylepšení kvality kódu nad každým PR.
|
||||
|
||||
### 🐛 Opravy chyb (PR #369, #371, #372, #373 od @kfiramar)
|
||||
|
||||
- **oprava(db) #373** : Přidání sloupce `provider_connections.group` do základního schématu + migrace zpětného doplnění pro existující databáze — sloupec byl použit ve všech dotazech, ale chyběl v definici schématu
|
||||
- **fix(i18n) #371** : Nahrazení neexistujícího klíče `t("deleteConnection")` existujícím `providers.delete` — oprava `MISSING_MESSAGE: providers.deleteConnection` na stránce s podrobnostmi o poskytovateli
|
||||
- **oprava(auth) #372** : Vymazat zastaralá chybová metadata ( `errorCode` , `lastErrorType` , `lastErrorSource` ) z účtů poskytovatelů po skutečném zotavení – dříve se obnovené účty zobrazovaly jako selhané
|
||||
- **oprava(startup) #369** : Sjednocení načítání env napříč `npm run start` , `run-standalone.mjs` a Electron s ohledem na prioritu `DATA_DIR/.env → ~/.omniroute/.env → ./.env` — zabránění generování nového `STORAGE_ENCRYPTION_KEY` přes existující šifrovanou databázi
|
||||
|
||||
### 🔧 Kvalita kódu
|
||||
|
||||
- Zdokumentované vzory `result.success` vs. `response?.ok` v `auth.ts` (oba úmyslné, nyní vysvětlené)
|
||||
- Normalizované `overridePath?.trim()` v `electron/main.js` pro shodu s `bootstrap-env.mjs`
|
||||
- Přidán komentář k objednávce sloučení `preferredEnv` při spuštění Electronu
|
||||
|
||||
> Oprava kvót pro účty Codex s automatickou rotací, rychlým přepínáním úrovní, modelem gpt-5.4 a označením analytických nástrojů.
|
||||
|
||||
### ✨ Nové funkce (PR #366, #367, #368)
|
||||
|
||||
- **Zásady kvót Codexu (PR #366)** : Okno kvóty 5 hodin/týden pro účet se přepíná v dashboardu poskytovatele. Účty jsou automaticky přeskočeny, když povolená okna dosáhnou prahové hodnoty 90 %, a znovu povoleny po `resetAt` . Zahrnuje `quotaCache.ts` s vedlejším efektem pro získávání statusu zdarma.
|
||||
- **Přepínání rychlé úrovně Codexu (PR #367)** : Dashboard → Nastavení → Úroveň služeb Codexu. Přepínání ve výchozím nastavení vkládá `service_tier: "flex"` pouze pro požadavky Codexu, což snižuje náklady o ~80 %. Celý stack: karta UI + koncový bod API + exekutor + překladač + obnovení po spuštění.
|
||||
- **Model gpt-5.4 (PR #368)** : Přidává `cx/gpt-5.4` a `codex/gpt-5.4` do registru modelů Codex. Regresní test je součástí.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **oprava č. 356** : Analytické grafy (Nejlepší poskytovatel, Podle účtu, Rozdělení poskytovatelů) nyní zobrazují lidsky čitelné názvy/štítky poskytovatelů namísto nezpracovaných interních ID u poskytovatelů kompatibilních s OpenAI.
|
||||
|
||||
> Hlavní vydání: strategie striktně náhodného směrování, řízení přístupu k klíčům API, skupiny připojení, synchronizace externích cen a opravy kritických chyb pro modely myšlení, kombinované testování a validaci názvů nástrojů.
|
||||
|
||||
### ✨ Nové funkce (PR #363 a #365)
|
||||
|
||||
- **Strategie striktně náhodného směrování** : Fisher-Yatesův náhodný balíček s garancí neopakování a serializací mutexů pro souběžné požadavky. Nezávislé balíčky pro každé kombo a providera.
|
||||
- **Řízení přístupu ke klíčům API** : `allowedConnections` (omezení připojení, která může klíč používat), `is_active` (povolení/zakázání klíče s kódem 403), `accessSchedule` (řízení přístupu na základě času), přepínání `autoResolve` , přejmenování klíčů pomocí PATCH.
|
||||
- **Skupiny připojení** : Seskupování připojení poskytovatelů podle prostředí. Harmonické zobrazení na stránce Limity s perzistencí localStorage a inteligentním automatickým přepínáním.
|
||||
- **Synchronizace externích cen (LiteLLM)** : 3stupňové rozlišení cen (uživatelské přepsání → synchronizace → výchozí hodnoty). Možnost přihlášení přes `PRICING_SYNC_ENABLED=true` . Nástroj MCP `omniroute_sync_pricing` . 23 nových testů.
|
||||
- **i18n** : 30 jazyků aktualizováno strategií striktní náhodnosti, řetězce pro správu klíčů API. pt-BR plně přeloženo.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **Oprava č. 355** : Časový limit nečinnosti streamu zvýšen z 60 s na 300 s – zabraňuje přerušení modelů s rozšířeným myšlením (claude-opus-4-6, o3 atd.) během dlouhých fází uvažování. Konfigurovatelné pomocí `STREAM_IDLE_TIMEOUT_MS` .
|
||||
- **Oprava č. 350** : Kombinovaný test nyní obchází `REQUIRE_API_KEY=true` pomocí interní hlavičky a univerzálně používá formát kompatibilní s OpenAI. Časový limit prodloužen z 15 s na 20 s.
|
||||
- **oprava #346** : Nástroje s prázdným `function.name` (přeposláno Claudem Code) jsou nyní filtrovány předtím, než je obdrží upstreamoví poskytovatelé, čímž se zabrání chybám „Neplatný vstup[N].name: prázdný řetězec“.
|
||||
|
||||
### 🗑️ Uzavřené problémy
|
||||
|
||||
- **#341** : Sekce ladění odstraněna – nahrazena je `/dashboard/logs` a `/dashboard/health` .
|
||||
|
||||
> Podpora API Key Round-Robin pro nastavení poskytovatelů s více klíči a potvrzení již zavedeného směrování zástupných znaků a rolování oken kvót.
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **Round-Robin klíčů API (T07)** : Připojení poskytovatelů nyní mohou obsahovat více klíčů API (Upravit připojení → Další klíče API). Požadavky rotují round-robin mezi primárními a dalšími klíči pomocí `providerSpecificData.extraApiKeys[]` . Klíče jsou uchovávány v paměti indexované pro každé připojení – nejsou nutné žádné změny schématu databáze.
|
||||
|
||||
### 📝 Již implementováno (potvrzeno auditem)
|
||||
|
||||
- **Směrování modelu s wildcard znaky (T13)** : soubor `wildcardRouter.ts` s porovnáváním zástupných znaků ve stylu glob ( `gpt*` , `claude-?-sonnet` atd.) je již integrován do `model.ts` s hodnocením specificity.
|
||||
- **Posunování okna kvót (T08)** : `accountFallback.ts:isModelLocked()` již automaticky posouvá okno vpřed – pokud `Date.now() > entry.until` , zámek se okamžitě smaže (žádné blokování zastaralých funkcí).
|
||||
|
||||
> Vylepšení uživatelského rozhraní, doplnění strategií směrování a elegantní zpracování chyb pro omezení využití.
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **Strategie směrování Fill-First a P2C** : Do výběru kombinované strategie přidány strategie `fill-first` (vyčerpání kvóty před přesunem) a `p2c` (výběr Power-of-Two-Choices s nízkou latencí) s kompletními panely s pokyny a barevně odlišenými odznaky.
|
||||
- **Přednastavené modely Free Stack** : Vytvoření kombinace pomocí šablony Free Stack nyní automaticky vyplní 7 nejlepších modelů bezplatných poskytovatelů ve své třídě (Gemini CLI, Kiro, iFlow×2, Qwen, NVIDIA NIM, Groq). Uživatelé stačí aktivovat poskytovatele a ihned získají kombinaci 0 $/měsíc.
|
||||
- **Širší kombo modální okno** : Modální okno pro vytvoření/úpravu komba nyní používá `max-w-4xl` pro pohodlnou úpravu velkých komb.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **Stránka s limity HTTP 500 pro Codex a GitHub** : `getCodexUsage()` a `getGitHubUsage()` nyní vracejí uživatelsky přívětivou zprávu, když poskytovatel vrátí 401/403 (vypršelý token), místo aby vyvolaly chybu 500 na stránce s limity.
|
||||
- **Falešně pozitivní MaintenanceBanner** : Banner již při načítání stránky falešně nezobrazuje „Server je nedostupný“. Opraveno okamžitým voláním `checkHealth()` při připojení a odstraněním zastaralého uzavření `show` -state.
|
||||
- **Popisky ikon poskytovatele** : Tlačítka s ikonami pro úpravu (tužka) a odstranění v řádku připojení poskytovatele nyní obsahují nativní HTML popisky – všech 6 ikon akcí je nyní samodokumentovaných.
|
||||
|
||||
> Několik vylepšení z analýzy problémů komunity, podpora nových poskytovatelů, opravy chyb pro sledování tokenů, směrování modelů a spolehlivost streamování.
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **Inteligentní směrování s ohledem na úlohy (T05)** : Automatický výběr modelu na základě typu obsahu požadavku — kódování → deepseek-chat, analýza → gemini-2.5-pro, vision → gpt-4o, sumarizace → gemini-2.5-flash. Konfigurovatelné v Nastavení. Nové API `GET/PUT/POST /api/settings/task-routing` .
|
||||
- **Poskytovatel HuggingFace** : Přidán HuggingFace Router jako poskytovatel kompatibilní s OpenAI s Llama 3.1 70B/8B, Qwen 2.5 72B, Mistral 7B, Phi-3.5 Mini.
|
||||
- **Poskytovatel Vertex AI** : Přidán poskytovatel Vertex AI (Google Cloud) s Gemini 2.5 Pro/Flash, Gemma 2 27B, Claude přes Vertex.
|
||||
- **Nahrávání souborů do Playgroundu** : Nahrávání zvuku pro přepis, nahrávání obrázků pro modely vidění (automatická detekce podle názvu modelu), inline vykreslování obrázků pro výsledky generování obrázků.
|
||||
- **Vizuální zpětná vazba při výběru modelu** : Již přidané modely v kombinovaném výběru nyní zobrazují zelený odznak ✓ – zabraňuje záměně duplicitních modelů.
|
||||
- **Kompatibilita s Qwen (PR #352)** : Aktualizováno nastavení otisků uživatelského agenta a rozhraní CLI pro kompatibilitu s poskytovateli Qwen.
|
||||
- **Správa stavu round-robin (PR #349)** : Vylepšená logika round-robin pro zpracování vyloučených účtů a správné udržování stavu rotace.
|
||||
- **Uživatelská zkušenost se schránkou (PR #360)** : Vylepšené operace se schránkou s možností zálohování pro nezabezpečené kontexty; vylepšení normalizace nástroje Claude.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **Oprava č. 302 – OpenAI SDK stream=False zanechává tool_calls** : T01 Accept header negotiation již nevynucuje streamování, pokud je `body.stream` explicitně `false` . Způsobovalo to tiché zanechávání tool_calls při použití OpenAI Python SDK v režimu bez streamování.
|
||||
- **Oprava č. 73 — Claude Haiku směrován do OpenAI bez prefixu poskytovatele** : modely `claude-*` odeslané bez prefixu poskytovatele nyní správně směrují k poskytovateli `antigravity` (antropickému). Přidána také heuristika `gemini-*` / `gemma-*` → `gemini` .
|
||||
- **Oprava č. 74 – Počet tokenů je pro streamování Antigravity/Claude vždy 0** : Událost SSE `message_start` , která obsahuje `input_tokens` nebyla analyzována funkcí `extractUsage()` , což způsobovalo pokles všech počtů vstupních tokenů. Sledování vstupních/výstupních tokenů nyní funguje správně pro streamované odpovědi.
|
||||
- **Oprava č. 180 – Duplikáty importovaných modelů bez zpětné vazby** : `ModelSelectModal` nyní zobrazuje ✓ zelené zvýraznění u modelů, které jsou již v kombinaci, takže je zřejmé, že jsou již přidány.
|
||||
- **Chyby generování mediálních stránek** : Výsledky obrázků se nyní vykreslují jako tagy `<img>` místo nezpracovaného JSON. Výsledky přepisu se zobrazují jako čitelný text. Chyby přihlašovacích údajů zobrazují oranžový banner místo tiché chyby.
|
||||
- **Tlačítko pro obnovení tokenu na stránce poskytovatele** : Pro poskytovatele OAuth bylo přidáno uživatelské rozhraní pro ruční obnovení tokenu.
|
||||
|
||||
### 🔧 Vylepšení
|
||||
|
||||
- **Registr poskytovatelů** : Do `providerRegistry.ts` a `providers.ts` (frontend) přidány prvky HuggingFace a Vertex AI.
|
||||
- **Čtení mezipaměti** : Nový `src/lib/db/readCache.ts` pro efektivní ukládání do mezipaměti čtení databáze.
|
||||
- **Mezipaměť kvót** : Vylepšená mezipaměť kvót s vyřazením na základě TTL.
|
||||
|
||||
### 📦 Závislosti
|
||||
|
||||
- `dompurify` → 3.3.3 (PR #347)
|
||||
- `undici` → 7.24.2 (PR #348, #361)
|
||||
- `docker/setup-qemu-action` → v4 (PR #342)
|
||||
- `docker/setup-buildx-action` → v4 (PR #343)
|
||||
|
||||
### 📁 Nové soubory
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`open-sse/services/taskAwareRouter.ts` | Logika směrování s ohledem na úlohy (7 typů úloh)
|
||||
`src/app/api/settings/task-routing/route.ts` | API pro konfiguraci směrování úloh
|
||||
`src/app/api/providers/[id]/refresh/route.ts` | Ruční aktualizace tokenu OAuth
|
||||
`src/lib/db/readCache.ts` | Efektivní mezipaměť pro čtení databáze
|
||||
`src/shared/utils/clipboard.ts` | Zpevněná schránka s funkcí
|
||||
|
||||
## [2.4.1] - 13. 3. 2026
|
||||
|
||||
### 🐛 Oprava
|
||||
|
||||
- **Modální okno s kombinacemi: Šablona Volný zásobník viditelná a výrazná** – Šablona Volný zásobník byla skrytá (4. v mřížce se 3 sloupci). Opraveno: přesunuto na pozici 1, přepnuto na mřížku 2x2, takže jsou viditelné všechny 4 šablony, zelený okraj + zvýraznění odznaku ZDARMA.
|
||||
|
||||
## [2.4.0] - 13. 3. 2026
|
||||
|
||||
> **Hlavní vydání** – ekosystém Free Stack, přepracované transkripční hřiště, více než 44 poskytovatelů, komplexní dokumentace k bezplatné úrovni a vylepšení uživatelského rozhraní napříč všemi oblastmi.
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **Kombinace: Šablona Free Stack** — Nová 4. šablona „Free Stack (0 $)“ využívající round-robin napříč Kiro + iFlow + Qwen + Gemini CLI. Při prvním použití doporučuje předpřipravenou kombinaci s nulovými náklady.
|
||||
- **Média/Přepis: Deepgram jako výchozí** – Deepgram (Nova 3, 200 dolarů zdarma) je nyní výchozím poskytovatelem přepisu. AssemblyAI (50 dolarů zdarma) a Groq Whisper (navždy zdarma) jsou zobrazeny s odznaky bezplatného kreditu.
|
||||
- **README: Sekce „Začít zdarma“** – Nová tabulka s 5 kroky v předběžném souboru README, která ukazuje, jak nastavit umělou inteligenci s nulovými náklady během několika minut.
|
||||
- **README: Kombinace bezplatného přepisu** – Nová sekce s návrhem kombinací Deepgram/AssemblyAI/Groq a informacemi o bezplatném kreditu pro každého poskytovatele.
|
||||
- **providers.ts: příznak hasFree** — NVIDIA NIM, Cerebras a Groq označené odznakem hasFree a freeNote pro uživatelské rozhraní poskytovatelů.
|
||||
- **i18n: klíče templateFreeStack** — kombinovaná šablona Free Stack přeložená a synchronizovaná do všech 30 jazyků.
|
||||
|
||||
## [2.3.16] - 13. 3. 2026
|
||||
|
||||
### 📖 Dokumentace
|
||||
|
||||
- **README: 44+ poskytovatelů** — Všechny 3 výskyty výrazu „36+ poskytovatelů“ byly aktualizovány na „44+“, což odráží skutečný počet kódové základny (44 poskytovatelů v souboru providers.ts).
|
||||
- **README: Nová sekce „🆓 Bezplatné modely – Co skutečně získáte“** – Přidána tabulka 7 poskytovatelů s limity rychlosti pro každý model pro: Kiro (Claude neomezeně přes AWS Builder ID), iFlow (5 modelů neomezeně), Qwen (4 modely neomezeně), Gemini CLI (180K/měsíc), NVIDIA NIM (~40 RPM dev-forever), Cerebras (1M tok/den / 60K TPM), Groq (30 RPM / 14.4K RPD). Zahrnuje doporučení pro kombinaci /usr/bin/bash Ultimate Free Stack.
|
||||
- **Soubor README: Aktualizace cenové tabulky** – přidán Cerebras do úrovně API KEY, opravena změna NVIDIA z „1000 kreditů“ na „navždy zdarma pro vývojáře“, aktualizovány počty a názvy modelů iFlow/Qwen
|
||||
- **README: Modely iFlow 8→5** (s názvy: kimi-k2-thinking, qwen3-coder-plus, deepseek-r1, minimax-m2, kimi-k2)
|
||||
- **README: Modely Qwen 3→4** (s názvy: qwen3-coder-plus, qwen3-coder-flash, qwen3-coder-next, vision-model)
|
||||
|
||||
## [2.3.15] - 13. 3. 2026
|
||||
|
||||
### ✨ Funkce
|
||||
|
||||
- **Panel automatických kombinací (priorita úrovně)** : Přidána `🏷️ Tier` jako 7. faktor bodování v zobrazení rozpisu faktorů `/dashboard/auto-combo` – nyní je viditelných všech 7 faktorů bodování automatických kombinací.
|
||||
- **i18n — sekce autoCombo** : Pro panel Auto-Combo bylo přidáno 20 nových překladových klíčů ( `title` , `status` , `modePack` , `providerScores` , `factorTierPriority` atd.) do všech 30 jazykových souborů.
|
||||
|
||||
## [2.3.14] - 13. 3. 2026
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **iFlow OAuth (#339)** : Obnoven platný výchozí `clientSecret` – dříve to byl prázdný řetězec, který při každém pokusu o připojení způsoboval chybu „Chybné přihlašovací údaje klienta“. Veřejné přihlašovací údaje jsou nyní výchozím záložním nastavením (lze je přepsat pomocí proměnné prostředí `IFLOW_OAUTH_CLIENT_SECRET` ).
|
||||
- **MITM server nenalezen (#335)** : `prepublish.mjs` nyní kompiluje `src/mitm/*.ts` do JavaScriptu pomocí `tsc` před zkopírováním do npm balíčku. Dříve se kopírovaly pouze nezpracované soubory `.ts` – což znamenalo, že `server.js` nikdy neexistoval v globálních instalacích npm/Volta.
|
||||
- **Chybí projectId v GeminiCLI (#338)** : Namísto vyvolání hardwarové chyby 500, když v uložených přihlašovacích údajích chybí `projectId` (např. po restartu Dockeru), OmniRoute nyní zaznamená varování a pokusí se o požadavek – vrátí smysluplnou chybu na straně poskytovatele místo pádu OmniRoute.
|
||||
- **Neshoda verzí balíčku Electron (#323)** : Synchronizována verze `electron/package.json` s verzí `2.3.13` (dříve `2.0.13` ), takže binární verze pro stolní počítače odpovídá balíčku npm.
|
||||
|
||||
### ✨ Nové modely (#334)
|
||||
|
||||
- **Kiro** : `claude-sonnet-4` , `claude-opus-4.6` , `deepseek-v3.2` , `minimax-m2.1` , `qwen3-coder-next` , `auto`
|
||||
- **Kodex** : `gpt5.4`
|
||||
|
||||
### 🔧 Vylepšení
|
||||
|
||||
- **Bodové hodnocení (API + validace)** : Do schématu Zod `ScoringWeights` a trasy API `combos/auto` přidána `tierPriority` (váha `0.05` ) – 7. faktor bodování je nyní plně akceptován rozhraním REST API a ověřován na vstupu. Váha `stability` upravena z `0.10` na `0.05` , aby celkový součet zůstal `1.0` .
|
||||
|
||||
### ✨ Nové funkce
|
||||
|
||||
- **Víceúrovňové bodování kvót (automatické kombinování)** : Přidána `tierPriority` jako 7. faktor bodování – účty s úrovněmi Ultra/Pro jsou nyní upřednostňovány před úrovněmi Free, pokud jsou ostatní faktory stejné. Nová volitelná pole `accountTier` a `quotaResetIntervalSecs` u `ProviderCandidate` . Všechny 4 balíčky režimů byly aktualizovány ( `ship-fast` , `cost-saver` , `quality-first` , `offline-friendly` ).
|
||||
- **Záložní model v rámci rodiny (T5)** : Pokud model není k dispozici (404/400/403), OmniRoute se nyní automaticky vrátí k sourozeneckým modelům ze stejné rodiny, než vrátí chybu ( `modelFamilyFallback.ts` ).
|
||||
- **Konfigurovatelný časový limit API Bridge** : Proměnná prostředí `API_BRIDGE_PROXY_TIMEOUT_MS` umožňuje operátorům ladit časový limit proxy (výchozí hodnota 30 s). Opravuje chyby 504 při pomalých odezvách upstreamu. (#332)
|
||||
- **Historie hvězd** : Widget star-history.com byl ve všech 30 souborech README nahrazen widgetem starchart.cc ( `?variant=adaptive` ) – přizpůsobuje se světlému/tmavému tématu a aktualizacím v reálném čase.
|
||||
|
||||
### 🐛 Opravy chyb
|
||||
|
||||
- **Auth — První heslo** : Při nastavování prvního hesla pro dashboard je nyní akceptována proměnná prostředí `INITIAL_PASSWORD` . Používá `timingSafeEqual` pro porovnávání v konstantním čase, čímž se zabraňuje útokům na časování. (#333)
|
||||
- **Zkrácení souboru README** : Opraven chybějící uzavírací tag `</details>` v sekci Řešení problémů, který způsoboval, že GitHub zastavil vykreslování všeho pod ním (Tech Stack, Dokumentace, Plán, Přispěvatelé).
|
||||
- **Instalace pnpm** : Z `package.json` byl odstraněn redundantní přepis `@swc/helpers` , který kolidoval s přímou závislostí a způsoboval chyby `EOVERRIDE` na pnpm. Přidána konfigurace `pnpm.onlyBuiltDependencies` .
|
||||
- **Vložení cesty do CLI (T12)** : V `cliRuntime.ts` byl přidán validátor `isSafePath()` pro blokování procházení cesty a metaznaků shellu v proměnných prostředí `CLI_*_BIN` .
|
||||
- **CI** : Po odstranění přepsání byl obnoven `package-lock.json` pro opravu chyb `npm ci` v akcích GitHubu.
|
||||
|
||||
### 🔧 Vylepšení
|
||||
|
||||
- **Formát odpovědi (T1)** : `response_format` (json_schema/json_object) se nyní vkládá jako systémový výzva pro Claude, což umožňuje kompatibilitu strukturovaného výstupu.
|
||||
- **429 Opakování (T2)** : Opakování odpovědí 429 v rámci URL (2× pokusy s 2s zpožděním) před návratem k další URL.
|
||||
- **Záhlaví rozhraní příkazového řádku Gemini (T3)** : Přidány záhlaví otisků prstů `User-Agent` a `X-Goog-Api-Client` pro kompatibilitu s rozhraním příkazového řádku Gemini.
|
||||
- **Cenový katalog (T9)** : Přidány ceníky pro `deepseek-3.1` , `deepseek-3.2` a `qwen3-coder-next` .
|
||||
|
||||
### 📁 Nové soubory
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`open-sse/services/modelFamilyFallback.ts` | Definice modelových rodin a logika záložních řešení v rámci rodiny
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **KiloCode** : časový limit kontroly stavu kilocode již byl opraven ve verzi 2.3.11.
|
||||
- **OpenCode** : Přidání opencode do registru cliRuntime s 15sekundovým časovým limitem pro kontrolu stavu
|
||||
- **OpenClaw / Cursor** : Prodloužení časového limitu kontroly stavu na 15 sekund pro varianty s pomalým startem.
|
||||
- **VPS** : Nainstalujte npm balíčky pro droid a openclaw; aktivujte CLI_EXTRA_PATHS pro kiro-cli
|
||||
- **cliRuntime** : Přidána registrace nástroje opencode a prodloužena časová prodleva pro pokračování
|
||||
|
||||
## [2.3.11] - 12. 3. 2026
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **KiloCode healthcheck** : Zvýšení `healthcheckTimeoutMs` z 4000 ms na 15000 ms — kilocode při spuštění vykreslí banner s logem ASCII, což v prostředích s pomalým/studeným startem způsobí chybu `healthcheck_failed`
|
||||
|
||||
## [2.3.10] - 12. 3. 2026
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **Lint** : Oprava chyby `check:any-budget:t11` — nahrazení `as any` za `as Record<string, unknown>` v OAuthModal.tsx (3 výskyty)
|
||||
|
||||
### Dokumenty
|
||||
|
||||
- **CLI-TOOLS.md** : Kompletní průvodce všemi 11 nástroji CLI (claude, codex, gemini, opencode, cline, kilocode, continue, kiro-cli, cursor, droid, openclaw)
|
||||
- **i18n** : CLI-TOOLS.md synchronizovaný do 30 jazyků s přeloženým názvem a úvodem
|
||||
|
||||
## [2.3.8] - 12. 3. 2026
|
||||
|
||||
## [2.3.9] - 12. 3. 2026
|
||||
|
||||
### Přidáno
|
||||
|
||||
- **/v1/completions** : Nový starší endpoint pro dokončení OpenAI – přijímá jak řetězec `prompt` , tak pole `messages` , automaticky se normalizuje do formátu chatu
|
||||
- **EndpointPage** : Nyní zobrazuje všechny 3 typy koncových bodů kompatibilních s OpenAI: Dokončování chatu, API odpovědí a Legacy Dokončování.
|
||||
- **i18n** : Přidán `completionsLegacy/completionsLegacyDesc` do 30 jazykových souborů.
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **OAuthModal** : Oprava zobrazení objektu `[object Object]` u všech chyb připojení OAuth – správně extrahovat `.message` z objektů odpovědí na chyby ve všech 3 `throw new Error(data.error)` (exchange, device-code, authorize)
|
||||
- Ovlivňuje Cline, Codex, GitHub, Qwen, Kiro a všechny ostatní poskytovatele OAuth.
|
||||
|
||||
## [2.3.7] - 12. 3. 2026
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **Cline OAuth** : Před dekódování base64 přidána `decodeURIComponent` , aby autorizační kódy kódované pomocí URL z URL zpětného volání byly správně analyzovány, opraveny chyby „neplatný nebo vypršený autorizační kód“ ve vzdálených instalacích (LAN IP).
|
||||
- **Cline OAuth** : `mapTokens` nyní vyplňuje `name = firstName + lastName || email` , takže účty Cline zobrazují skutečná uživatelská jména místo „Account #ID“.
|
||||
- **Názvy účtů OAuth** : Všechny toky výměny OAuth (exchange, poll, poll-callback) nyní normalizují `name = email` pokud název chybí, takže každý účet OAuth zobrazuje svůj e-mail jako zobrazovaný popisek v dashboardu Poskytovatelé.
|
||||
- **Názvy účtů OAuth** : V souboru `db/providers.ts` byla odstraněna sekvenční záložní možnost „Účet N“ – účty bez e-mailu/jména nyní používají stabilní popisek založený na ID pomocí `getAccountDisplayName()` namísto sekvenčního čísla, které se mění při smazání účtů.
|
||||
|
||||
## [2.3.6] - 12. 3. 2026
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **Dávkový test poskytovatele** : Opraveno schéma Zod pro akceptování `providerId: null` (frontend odesílá null pro režimy bez poskytovatele); nesprávně vracelo „Neplatný požadavek“ pro všechny dávkové testy.
|
||||
- **Modální okno testování poskytovatele** : Opraveno zobrazení `[object Object]` normalizací objektů chyb API na řetězce před vykreslením v `setTestResults` a `ProviderTestResultsView`
|
||||
- **i18n** : Do `en.json` přidány chybějící klíče `cliTools.toolDescriptions.opencode` , `cliTools.toolDescriptions.kiro` , `cliTools.guides.opencode` , `cliTools.guides.kiro`
|
||||
- **i18n** : Synchronizováno chybějící 1111 klíčů ve všech 29 souborech v neanglických jazycích s použitím anglických hodnot jako záložních hodnot.
|
||||
|
||||
## [2.3.5] - 11. 3. 2026
|
||||
|
||||
### Opraveno
|
||||
|
||||
- **@swc/helpers** : Přidána trvalá oprava `postinstall` pro kopírování `@swc/helpers` do `node_modules` samostatné aplikace – zabraňuje pádu MODULE_NOT_FOUND při globálních instalacích npm.
|
||||
|
||||
## [2.3.4] - 10. 3. 2026
|
||||
|
||||
### Přidáno
|
||||
|
||||
- Integrace více poskytovatelů a vylepšení dashboardu
|
||||
341
docs/i18n/cs/CLI-TOOLS.md
Normal file
341
docs/i18n/cs/CLI-TOOLS.md
Normal file
@@ -0,0 +1,341 @@
|
||||
# Průvodce nastavením nástrojů CLI — OmniRoute
|
||||
|
||||
Tato příručka vysvětluje, jak nainstalovat a nakonfigurovat všechny podporované nástroje CLI pro kódování umělé inteligence tak, aby **OmniRoute** fungoval jako jednotný backend, což vám umožní centralizovanou správu klíčů, sledování nákladů, přepínání modelů a protokolování požadavků napříč všemi nástroji.
|
||||
|
||||
---
|
||||
|
||||
## Jak to funguje
|
||||
|
||||
```
|
||||
Claude / Codex / Gemini CLI / OpenCode / Cline / KiloCode / Continue / Kiro CLI
|
||||
│
|
||||
▼ (all point to OmniRoute)
|
||||
http://YOUR_SERVER:20128/v1
|
||||
│
|
||||
▼ (OmniRoute routes to the right provider)
|
||||
Anthropic / OpenAI / Gemini / DeepSeek / Groq / Mistral / ...
|
||||
```
|
||||
|
||||
**Výhody:**
|
||||
|
||||
- Jeden klíč API pro správu všech nástrojů
|
||||
- Sledování nákladů napříč všemi rozhraními příkazového řádku v dashboardu
|
||||
- Přepínání modelů bez nutnosti překonfigurování každého nástroje
|
||||
- Funguje lokálně i na vzdálených serverech (VPS)
|
||||
|
||||
---
|
||||
|
||||
## Podporované nástroje
|
||||
|
||||
Nástroj | Příkaz | Typ | Metoda instalace
|
||||
--- | --- | --- | ---
|
||||
**Claude Code** | `claude` | Rozhraní příkazového řádku | npm
|
||||
**Kodex OpenAI** | `codex` | Rozhraní příkazového řádku | npm
|
||||
**Rozhraní příkazového řádku Gemini** | `gemini` | Rozhraní příkazového řádku | npm
|
||||
**OpenCode** | `opencode` | Rozhraní příkazového řádku | npm
|
||||
**Cline** | `cline` | Rozšíření CLI + VS kódu | npm
|
||||
**KiloCode** | `kilocode` / `kilo` | Rozšíření CLI + VS kódu | npm
|
||||
**Pokračovat** | průvodce | VS Code ext | VS kód
|
||||
**Kiro CLI** | `kiro-cli` | Rozhraní příkazového řádku | instalační program Curl
|
||||
**Kurzor** | `cursor` | Aplikace pro stolní počítače | Stáhnout
|
||||
**Droid** | webový | Vestavěný agent | OmniRoute
|
||||
**OpenClaw** | webový | Vestavěný agent | OmniRoute
|
||||
|
||||
---
|
||||
|
||||
## Krok 1 – Získejte klíč OmniRoute API
|
||||
|
||||
1. Otevřete dashboard OmniRoute → **Správce API** ( `/dashboard/api-manager` )
|
||||
2. Klikněte na **Vytvořit klíč API**
|
||||
3. Pojmenujte to (např. `cli-tools` ) a vyberte všechna oprávnění.
|
||||
4. Zkopírujte klíč – budete ho potřebovat pro každé níže uvedené rozhraní příkazového řádku.
|
||||
|
||||
> Váš klíč vypadá takto: `sk-xxxxxxxxxxxxxxxx-xxxxxxxxx`
|
||||
|
||||
---
|
||||
|
||||
## Krok 2 – Instalace nástrojů CLI
|
||||
|
||||
Všechny nástroje založené na npm vyžadují Node.js 18+:
|
||||
|
||||
```bash
|
||||
# Claude Code (Anthropic)
|
||||
npm install -g @anthropic-ai/claude-code
|
||||
|
||||
# OpenAI Codex
|
||||
npm install -g @openai/codex
|
||||
|
||||
# Gemini CLI (Google)
|
||||
npm install -g @google/gemini-cli
|
||||
|
||||
# OpenCode
|
||||
npm install -g opencode-ai
|
||||
|
||||
# Cline
|
||||
npm install -g cline
|
||||
|
||||
# KiloCode
|
||||
npm install -g kilecode
|
||||
|
||||
# Kiro CLI (Amazon — requires curl + unzip)
|
||||
apt-get install -y unzip # on Debian/Ubuntu
|
||||
curl -fsSL https://cli.kiro.dev/install | bash
|
||||
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc
|
||||
```
|
||||
|
||||
**Ověřit:**
|
||||
|
||||
```bash
|
||||
claude --version # 2.x.x
|
||||
codex --version # 0.x.x
|
||||
gemini --version # 0.x.x
|
||||
opencode --version # x.x.x
|
||||
cline --version # 2.x.x
|
||||
kilocode --version # x.x.x (or: kilo --version)
|
||||
kiro-cli --version # 1.x.x
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Krok 3 – Nastavení globálních proměnných prostředí
|
||||
|
||||
Přidejte do `~/.bashrc` (nebo `~/.zshrc` ) a poté spusťte `source ~/.bashrc` :
|
||||
|
||||
```bash
|
||||
# OmniRoute Universal Endpoint
|
||||
export OPENAI_BASE_URL="http://localhost:20128/v1"
|
||||
export OPENAI_API_KEY="sk-your-omniroute-key"
|
||||
export ANTHROPIC_BASE_URL="http://localhost:20128/v1"
|
||||
export ANTHROPIC_API_KEY="sk-your-omniroute-key"
|
||||
export GEMINI_BASE_URL="http://localhost:20128/v1"
|
||||
export GEMINI_API_KEY="sk-your-omniroute-key"
|
||||
```
|
||||
|
||||
> Pro **vzdálený server** nahraďte `localhost:20128` IP adresou nebo doménou serveru, např. `http://192.168.0.15:20128` .
|
||||
|
||||
---
|
||||
|
||||
## Krok 4 – Konfigurace jednotlivých nástrojů
|
||||
|
||||
### Claude Code
|
||||
|
||||
```bash
|
||||
# Via CLI:
|
||||
claude config set --global api-base-url http://localhost:20128/v1
|
||||
|
||||
# Or create ~/.claude/settings.json:
|
||||
mkdir -p ~/.claude && cat > ~/.claude/settings.json << EOF
|
||||
{
|
||||
"apiBaseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "sk-your-omniroute-key"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Test:** `claude "say hello"`
|
||||
|
||||
---
|
||||
|
||||
### Kodex OpenAI
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.codex && cat > ~/.codex/config.yaml << EOF
|
||||
model: auto
|
||||
apiKey: sk-your-omniroute-key
|
||||
apiBaseUrl: http://localhost:20128/v1
|
||||
EOF
|
||||
```
|
||||
|
||||
**Test:** `codex "what is 2+2?"`
|
||||
|
||||
---
|
||||
|
||||
### Rozhraní příkazového řádku Gemini
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini && cat > ~/.gemini/settings.json << EOF
|
||||
{
|
||||
"apiKey": "sk-your-omniroute-key",
|
||||
"baseUrl": "http://localhost:20128/v1"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Test:** `gemini "hello"`
|
||||
|
||||
---
|
||||
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/opencode && cat > ~/.config/opencode/config.toml << EOF
|
||||
[provider.openai]
|
||||
base_url = "http://localhost:20128/v1"
|
||||
api_key = "sk-your-omniroute-key"
|
||||
EOF
|
||||
```
|
||||
|
||||
**Test:** `opencode`
|
||||
|
||||
---
|
||||
|
||||
### Cline (CLI nebo VS kód)
|
||||
|
||||
**Režim CLI:**
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.cline/data && cat > ~/.cline/data/globalState.json << EOF
|
||||
{
|
||||
"apiProvider": "openai",
|
||||
"openAiBaseUrl": "http://localhost:20128/v1",
|
||||
"openAiApiKey": "sk-your-omniroute-key"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Režim VS Code:** Nastavení rozšíření Cline → Poskytovatel API: `OpenAI Compatible` → Základní URL: `http://localhost:20128/v1`
|
||||
|
||||
Nebo použijte dashboard OmniRoute → **CLI Tools → Cline → Apply Config** .
|
||||
|
||||
---
|
||||
|
||||
### KiloCode (CLI nebo VS kód)
|
||||
|
||||
**Režim CLI:**
|
||||
|
||||
```bash
|
||||
kilocode --api-base http://localhost:20128/v1 --api-key sk-your-omniroute-key
|
||||
```
|
||||
|
||||
**Nastavení VS kódu:**
|
||||
|
||||
```json
|
||||
{
|
||||
"kilo-code.openAiBaseUrl": "http://localhost:20128/v1",
|
||||
"kilo-code.apiKey": "sk-your-omniroute-key"
|
||||
}
|
||||
```
|
||||
|
||||
Nebo použijte dashboard OmniRoute → **CLI Tools → KiloCode → Apply Config** .
|
||||
|
||||
---
|
||||
|
||||
### Pokračovat (rozšíření kódu VS)
|
||||
|
||||
Upravit `~/.continue/config.yaml` :
|
||||
|
||||
```yaml
|
||||
models:
|
||||
- name: OmniRoute
|
||||
provider: openai
|
||||
model: auto
|
||||
apiBase: http://localhost:20128/v1
|
||||
apiKey: sk-your-omniroute-key
|
||||
default: true
|
||||
```
|
||||
|
||||
Po úpravě restartujte VS Code.
|
||||
|
||||
---
|
||||
|
||||
### Kiro CLI (Amazon)
|
||||
|
||||
```bash
|
||||
# Login to your AWS/Kiro account:
|
||||
kiro-cli login
|
||||
|
||||
# The CLI uses its own auth — OmniRoute is not needed as backend for Kiro CLI itself.
|
||||
# Use kiro-cli alongside OmniRoute for other tools.
|
||||
kiro-cli status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Kurzor (aplikace pro stolní počítače)
|
||||
|
||||
> **Poznámka:** Cursor směruje požadavky přes svůj cloud. Pro integraci OmniRoute povolte **cloudový koncový bod** v nastavení OmniRoute a použijte URL adresu vaší veřejné domény.
|
||||
|
||||
Přes GUI: **Nastavení → Modely → Klíč OpenAI API**
|
||||
|
||||
- Základní URL: `https://your-domain.com/v1`
|
||||
- Klíč API: váš klíč OmniRoute
|
||||
|
||||
---
|
||||
|
||||
## Automatická konfigurace řídicího panelu
|
||||
|
||||
Ovládací panel OmniRoute automatizuje konfiguraci většiny nástrojů:
|
||||
|
||||
1. Přejděte na `http://localhost:20128/dashboard/cli-tools`
|
||||
2. Rozbalit libovolnou kartu nástroje
|
||||
3. Vyberte klíč API z rozbalovací nabídky
|
||||
4. Klikněte **na Použít konfiguraci** (pokud je nástroj detekován jako nainstalovaný)
|
||||
5. Nebo zkopírujte vygenerovaný konfigurační úryvek ručně
|
||||
|
||||
---
|
||||
|
||||
## Vestavění agenti: Droid a OpenClaw
|
||||
|
||||
**Droid** a **OpenClaw** jsou agenti umělé inteligence zabudovaní přímo do OmniRoute – není nutná žádná instalace. Běží jako interní trasy a automaticky používají modelové směrování OmniRoute.
|
||||
|
||||
- Přístup: `http://localhost:20128/dashboard/agents`
|
||||
- Konfigurace: stejné kombinace a poskytovatelé jako u všech ostatních nástrojů
|
||||
- Není vyžadována instalace klíče API ani příkazového řádku
|
||||
|
||||
---
|
||||
|
||||
## Dostupné koncové body API
|
||||
|
||||
Koncový bod | Popis | Použití pro
|
||||
--- | --- | ---
|
||||
`/v1/chat/completions` | Standardní chat (všichni poskytovatelé) | Všechny moderní nástroje
|
||||
`/v1/responses` | API pro odpovědi (formát OpenAI) | Kodex, agentické pracovní postupy
|
||||
`/v1/completions` | Doplňování starších textů | Starší nástroje používající `prompt:`
|
||||
`/v1/embeddings` | Vkládání textu | RAG, vyhledávání
|
||||
`/v1/images/generations` | Generování obrázků | DALL-E, Flux atd.
|
||||
`/v1/audio/speech` | Převod textu na řeč | ElevenLabs, OpenAI TTS
|
||||
`/v1/audio/transcriptions` | Převod řeči na text | Deepgram, AssemblyAI
|
||||
|
||||
---
|
||||
|
||||
## Odstraňování problémů
|
||||
|
||||
Chyba | Příčina | Opravit
|
||||
--- | --- | ---
|
||||
`Connection refused` | OmniRoute neběží | `pm2 start omniroute`
|
||||
`401 Unauthorized` | Chybný klíč API | Zkontrolovat `/dashboard/api-manager`
|
||||
`No combo configured` | Žádná aktivní routingová kombinace | Nastavení v `/dashboard/combos`
|
||||
`invalid model` | Model není v katalogu | Použijte `auto` nebo zkontrolujte `/dashboard/providers`
|
||||
CLI zobrazuje „není nainstalováno“ | Binární soubor není v cestě PATH | Zkontrolujte, `which <command>`
|
||||
`kiro-cli: not found` | Není v PATH | `export PATH="$HOME/.local/bin:$PATH"`
|
||||
|
||||
---
|
||||
|
||||
## Skript pro rychlé nastavení (jeden příkaz)
|
||||
|
||||
```bash
|
||||
# Install all CLIs and configure for OmniRoute (replace with your key and server URL)
|
||||
OMNIROUTE_URL="http://localhost:20128/v1"
|
||||
OMNIROUTE_KEY="sk-your-omniroute-key"
|
||||
|
||||
npm install -g @anthropic-ai/claude-code @openai/codex @google/gemini-cli opencode-ai cline kilecode
|
||||
|
||||
# Kiro CLI
|
||||
apt-get install -y unzip 2>/dev/null; curl -fsSL https://cli.kiro.dev/install | bash
|
||||
|
||||
# Write configs
|
||||
mkdir -p ~/.claude ~/.codex ~/.gemini ~/.config/opencode ~/.continue
|
||||
|
||||
cat > ~/.claude/settings.json <<< "{\"apiBaseUrl\":\"$OMNIROUTE_URL\",\"apiKey\":\"$OMNIROUTE_KEY\"}"
|
||||
cat > ~/.codex/config.yaml <<< "model: auto\napiKey: $OMNIROUTE_KEY\napiBaseUrl: $OMNIROUTE_URL"
|
||||
cat > ~/.gemini/settings.json <<< "{\"apiKey\":\"$OMNIROUTE_KEY\",\"baseUrl\":\"$OMNIROUTE_URL\"}"
|
||||
cat >> ~/.bashrc << EOF
|
||||
export OPENAI_BASE_URL="$OMNIROUTE_URL"
|
||||
export OPENAI_API_KEY="$OMNIROUTE_KEY"
|
||||
export ANTHROPIC_BASE_URL="$OMNIROUTE_URL"
|
||||
export ANTHROPIC_API_KEY="$OMNIROUTE_KEY"
|
||||
EOF
|
||||
|
||||
source ~/.bashrc
|
||||
echo "✅ All CLIs installed and configured for OmniRoute"
|
||||
```
|
||||
589
docs/i18n/cs/CODEBASE_DOCUMENTATION.md
Normal file
589
docs/i18n/cs/CODEBASE_DOCUMENTATION.md
Normal file
@@ -0,0 +1,589 @@
|
||||
# omniroute — Dokumentace kódové základny
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](i18n/es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](i18n/fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](i18n/it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](i18n/ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](i18n/de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](i18n/in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](i18n/th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](i18n/uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](i18n/ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵[日本語](i18n/ja/CODEBASE_DOCUMENTATION.md)| 🇻🇳 [Tiếng Việt](i18n/vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](i18n/bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dánsko](i18n/da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](i18n/fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](i18n/he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [maďarština](i18n/hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](i18n/ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nizozemsko](i18n/nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](i18n/no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](i18n/ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](i18n/pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](i18n/sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](i18n/sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipínec](i18n/phi/CODEBASE_DOCUMENTATION.md) | 🇨🇿 [Čeština](i18n/cs/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
> Komplexní průvodce pro začátečníky s využitím multiproviderového proxy routeru s umělou inteligencí **od OmniRoute** .
|
||||
|
||||
---
|
||||
|
||||
## 1. Co je to omniroute?
|
||||
|
||||
Omniroute je **proxy router** , který se nachází mezi klienty umělé inteligence (Claude CLI, Codex, Cursor IDE atd.) a poskytovateli umělé inteligence (Anthropic, Google, OpenAI, AWS, GitHub atd.). Řeší jeden velký problém:
|
||||
|
||||
> **Různí klienti AI hovoří různými „jazyky“ (formáty API) a různí poskytovatelé AI také očekávají různé „jazyky“.** Omniroute mezi nimi automaticky překládá.
|
||||
|
||||
Představte si to jako univerzálního překladatele v Organizaci spojených národů – kterýkoli delegát může mluvit jakýmkoli jazykem a překladatel ho pro kteréhokoli jiného delegáta převede.
|
||||
|
||||
---
|
||||
|
||||
## 2. Přehled architektury
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Clients
|
||||
A[Claude CLI]
|
||||
B[Codex]
|
||||
C[Cursor IDE]
|
||||
D[OpenAI-compatible]
|
||||
end
|
||||
|
||||
subgraph omniroute
|
||||
E[Handler Layer]
|
||||
F[Translator Layer]
|
||||
G[Executor Layer]
|
||||
H[Services Layer]
|
||||
end
|
||||
|
||||
subgraph Providers
|
||||
I[Anthropic Claude]
|
||||
J[Google Gemini]
|
||||
K[OpenAI / Codex]
|
||||
L[GitHub Copilot]
|
||||
M[AWS Kiro]
|
||||
N[Antigravity]
|
||||
O[Cursor API]
|
||||
end
|
||||
|
||||
A --> E
|
||||
B --> E
|
||||
C --> E
|
||||
D --> E
|
||||
E --> F
|
||||
F --> G
|
||||
G --> I
|
||||
G --> J
|
||||
G --> K
|
||||
G --> L
|
||||
G --> M
|
||||
G --> N
|
||||
G --> O
|
||||
H -.-> E
|
||||
H -.-> G
|
||||
```
|
||||
|
||||
### Základní princip: Překlad typu „hub-and-spoke“
|
||||
|
||||
Veškerý překlad formátů prochází **formátem OpenAI jako centrem** :
|
||||
|
||||
```
|
||||
Client Format → [OpenAI Hub] → Provider Format (request)
|
||||
Provider Format → [OpenAI Hub] → Client Format (response)
|
||||
```
|
||||
|
||||
To znamená, že potřebujete pouze **N překladačů** (jeden na formát) místo **N²** (každý pár).
|
||||
|
||||
---
|
||||
|
||||
## 3. Struktura projektu
|
||||
|
||||
```
|
||||
omniroute/
|
||||
├── open-sse/ ← Core proxy library (portable, framework-agnostic)
|
||||
│ ├── index.js ← Main entry point, exports everything
|
||||
│ ├── config/ ← Configuration & constants
|
||||
│ ├── executors/ ← Provider-specific request execution
|
||||
│ ├── handlers/ ← Request handling orchestration
|
||||
│ ├── services/ ← Business logic (auth, models, fallback, usage)
|
||||
│ ├── translator/ ← Format translation engine
|
||||
│ │ ├── request/ ← Request translators (8 files)
|
||||
│ │ ├── response/ ← Response translators (7 files)
|
||||
│ │ └── helpers/ ← Shared translation utilities (6 files)
|
||||
│ └── utils/ ← Utility functions
|
||||
├── src/ ← Application layer (Express/Worker runtime)
|
||||
│ ├── app/ ← Web UI, API routes, middleware
|
||||
│ ├── lib/ ← Database, auth, and shared library code
|
||||
│ ├── mitm/ ← Man-in-the-middle proxy utilities
|
||||
│ ├── models/ ← Database models
|
||||
│ ├── shared/ ← Shared utilities (wrappers around open-sse)
|
||||
│ ├── sse/ ← SSE endpoint handlers
|
||||
│ └── store/ ← State management
|
||||
├── data/ ← Runtime data (credentials, logs)
|
||||
│ └── provider-credentials.json (external credentials override, gitignored)
|
||||
└── tester/ ← Test utilities
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Rozdělení podle modulů
|
||||
|
||||
### 4.1 Konfigurace ( `open-sse/config/` )
|
||||
|
||||
Jediný **zdroj pravdivých informací** pro všechny konfigurace poskytovatelů.
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`constants.ts` | Objekt `PROVIDERS` se základními URL adresami, přihlašovacími údaji OAuth (výchozí), záhlavími a výchozími systémovými výzvami pro každého poskytovatele. Definuje také `HTTP_STATUS` , `ERROR_TYPES` , `COOLDOWN_MS` , `BACKOFF_CONFIG` a `SKIP_PATTERNS` .
|
||||
`credentialLoader.ts` | Načte externí přihlašovací údaje z `data/provider-credentials.json` a sloučí je s pevně zakódovanými výchozími hodnotami v `PROVIDERS` . Uchovává tajné údaje mimo kontrolu zdrojového kódu a zároveň zachovává zpětnou kompatibilitu.
|
||||
`providerModels.ts` | Centrální registr modelů: mapuje aliasy poskytovatelů → ID modelů. Funkce jako `getModels()` , `getProviderByAlias()` .
|
||||
`codexInstructions.ts` | Systémové instrukce vložené do požadavků Codexu (omezení úprav, pravidla sandboxu, zásady schvalování).
|
||||
`defaultThinkingSignature.ts` | Výchozí „myšlenkové“ podpisy pro modely Claude a Gemini.
|
||||
`ollamaModels.ts` | Definice schématu pro lokální Ollama modely (název, velikost, rodina, kvantizace).
|
||||
|
||||
#### Postup načítání přihlašovacích údajů
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["App starts"] --> B["constants.ts defines PROVIDERS\nwith hardcoded defaults"]
|
||||
B --> C{"data/provider-credentials.json\nexists?"}
|
||||
C -->|Yes| D["credentialLoader reads JSON"]
|
||||
C -->|No| E["Use hardcoded defaults"]
|
||||
D --> F{"For each provider in JSON"}
|
||||
F --> G{"Provider exists\nin PROVIDERS?"}
|
||||
G -->|No| H["Log warning, skip"]
|
||||
G -->|Yes| I{"Value is object?"}
|
||||
I -->|No| J["Log warning, skip"]
|
||||
I -->|Yes| K["Merge clientId, clientSecret,\ntokenUrl, authUrl, refreshUrl"]
|
||||
K --> F
|
||||
H --> F
|
||||
J --> F
|
||||
F -->|Done| L["PROVIDERS ready with\nmerged credentials"]
|
||||
E --> L
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.2 Vykonavatelé ( `open-sse/executors/` )
|
||||
|
||||
Prováděcí metody zapouzdřují **logiku specifickou pro poskytovatele** pomocí **vzoru strategie** . Každý prováděcí metody podle potřeby přepisují základní metody.
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BaseExecutor {
|
||||
+buildUrl(model, stream, options)
|
||||
+buildHeaders(credentials, stream, body)
|
||||
+transformRequest(body, model, stream, credentials)
|
||||
+execute(url, options)
|
||||
+shouldRetry(status, error)
|
||||
+refreshCredentials(credentials, log)
|
||||
}
|
||||
|
||||
class DefaultExecutor {
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class AntigravityExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+shouldRetry()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
class CursorExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseResponse()
|
||||
+generateChecksum()
|
||||
}
|
||||
|
||||
class KiroExecutor {
|
||||
+buildUrl()
|
||||
+buildHeaders()
|
||||
+transformRequest()
|
||||
+parseEventStream()
|
||||
+refreshCredentials()
|
||||
}
|
||||
|
||||
BaseExecutor <|-- DefaultExecutor
|
||||
BaseExecutor <|-- AntigravityExecutor
|
||||
BaseExecutor <|-- CursorExecutor
|
||||
BaseExecutor <|-- KiroExecutor
|
||||
BaseExecutor <|-- CodexExecutor
|
||||
BaseExecutor <|-- GeminiCLIExecutor
|
||||
BaseExecutor <|-- GithubExecutor
|
||||
```
|
||||
|
||||
Vykonavatel | Poskytovatel | Klíčové specializace
|
||||
--- | --- | ---
|
||||
`base.ts` | — | Abstraktní základ: tvorba URL adres, hlavičky, logika opakování, aktualizace přihlašovacích údajů
|
||||
`default.ts` | Claude, Gemini, OpenAI, GLM, Kimi, MiniMax | Aktualizace generického tokenu OAuth pro standardní poskytovatele
|
||||
`antigravity.ts` | Kód Google Cloud | Generování ID projektu/relace, záložní více URL adres, vlastní analýza opakovaných pokusů z chybových zpráv („reset po 2h7m23s“)
|
||||
`cursor.ts` | IDE kurzoru | **Nejsložitější** : autorizace kontrolního součtu SHA-256, kódování požadavků Protobuf, analýza binárních EventStream → SSE odpovědí
|
||||
`codex.ts` | Kodex OpenAI | Vkládá systémové instrukce, spravuje úrovně myšlení, odstraňuje nepodporované parametry
|
||||
`gemini-cli.ts` | Rozhraní příkazového řádku Google Gemini | Vytvoření vlastní URL adresy ( `streamGenerateContent` ), aktualizace tokenu Google OAuth
|
||||
`github.ts` | GitHub Copilot | Systém duálních tokenů (GitHub OAuth + Copilot token), napodobování hlaviček VSCode
|
||||
`kiro.ts` | AWS CodeWhisperer | Binární parsování AWS EventStream, rámce událostí AMZN, odhad tokenů
|
||||
`index.ts` | — | Továrna: název poskytovatele map → třída exekutoru s výchozím záložním nastavením
|
||||
|
||||
---
|
||||
|
||||
### 4.3 Obslužné rutiny ( `open-sse/handlers/` )
|
||||
|
||||
**Orchestrační vrstva** – koordinuje překlad, provádění, streamování a zpracování chyb.
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`chatCore.ts` | **Centrální orchestrátor** (~600 řádků). Zvládá kompletní životní cyklus požadavku: detekce formátu → překlad → odeslání exekutoru → streamovaná/nestreamovaná odpověď → aktualizace tokenu → zpracování chyb → protokolování využití.
|
||||
`responsesHandler.ts` | Adaptér pro OpenAI Responses API: převádí formát odpovědí → Dokončení chatu → odesílá do `chatCore` → převádí SSE zpět do formátu odpovědí.
|
||||
`embeddings.ts` | Obslužná rutina generování embeddingu: řeší model embeddingu → poskytovatele, odesílá do API poskytovatele, vrací odpověď na embedding kompatibilní s OpenAI. Podporuje 6+ poskytovatelů.
|
||||
`imageGeneration.ts` | Obslužná rutina generování obrázků: řeší model obrázku → poskytovatele, podporuje režimy kompatibilní s OpenAI, Gemini-image (Antigravity) a fallback (Nebius). Vrací obrázky v base64 nebo URL.
|
||||
|
||||
#### Životní cyklus požadavku (chatCore.ts)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client
|
||||
participant chatCore
|
||||
participant Translator
|
||||
participant Executor
|
||||
participant Provider
|
||||
|
||||
Client->>chatCore: Request (any format)
|
||||
chatCore->>chatCore: Detect source format
|
||||
chatCore->>chatCore: Check bypass patterns
|
||||
chatCore->>chatCore: Resolve model & provider
|
||||
chatCore->>Translator: Translate request (source → OpenAI → target)
|
||||
chatCore->>Executor: Get executor for provider
|
||||
Executor->>Executor: Build URL, headers, transform request
|
||||
Executor->>Executor: Refresh credentials if needed
|
||||
Executor->>Provider: HTTP fetch (streaming or non-streaming)
|
||||
|
||||
alt Streaming
|
||||
Provider-->>chatCore: SSE stream
|
||||
chatCore->>chatCore: Pipe through SSE transform stream
|
||||
Note over chatCore: Transform stream translates<br/>each chunk: target → OpenAI → source
|
||||
chatCore-->>Client: Translated SSE stream
|
||||
else Non-streaming
|
||||
Provider-->>chatCore: JSON response
|
||||
chatCore->>Translator: Translate response
|
||||
chatCore-->>Client: Translated JSON
|
||||
end
|
||||
|
||||
alt Error (401, 429, 500...)
|
||||
chatCore->>Executor: Retry with credential refresh
|
||||
chatCore->>chatCore: Account fallback logic
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.4 Služby ( `open-sse/services/` )
|
||||
|
||||
Obchodní logika, která podporuje obslužné rutiny a vykonavatele.
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`provider.ts` | **Detekce formátu** ( `detectFormat` ): analyzuje strukturu těla požadavku a identifikuje formáty Claude/OpenAI/Gemini/Antigravity/Responses (včetně heuristiky `max_tokens` pro Claude). Dále: tvorba URL, tvorba hlaviček, normalizace konfigurace thinking. Podporuje dynamické poskytovatele kompatibilní `openai-compatible-*` a `anthropic-compatible-*` .
|
||||
`model.ts` | Analýza řetězců modelu ( `claude/model-name` → `{provider: "claude", model: "model-name"}` ), rozlišení aliasů s detekcí kolizí, sanitizace vstupu (odmítá průchod cestou/řídicí znaky) a rozlišení informací o modelu s podporou asynchronních metod pro získávání aliasů.
|
||||
`accountFallback.ts` | Ovládání limitů rychlosti: exponenciální upomínka (1 s → 2 s → 4 s → max. 2 min), správa doby zpoždění účtu, klasifikace chyb (které chyby spouštějí fallback a které ne).
|
||||
`tokenRefresh.ts` | Aktualizace tokenu OAuth pro **všechny poskytovatele** : Google (Gemini, Antigravity), Claude, Codex, Qwen, iFlow, GitHub (duální token OAuth + Copilot), Kiro (AWS SSO OIDC + sociální ověřování). Zahrnuje mezipaměť deduplikace promise za provozu a opakování s exponenciálním zpožděním.
|
||||
`combo.ts` | **Kombinované modely** : řetězce záložních modelů. Pokud model A selže s chybou způsobilou pro záložní model, zkuste model B, poté C atd. Vrací skutečné stavové kódy upstreamu.
|
||||
`usage.ts` | Načítá data o kvótách/využití z API poskytovatelů (kvóty GitHub Copilot, kvóty modelu Antigravity, limity rychlosti Codexu, rozpisy využití Kiro, nastavení Claude).
|
||||
`accountSelector.ts` | Inteligentní výběr účtu s algoritmem bodování: pro výběr optimálního účtu pro každý požadavek se zohledňuje priorita, zdravotní stav, pozice v systému round robin a stav ochlazování.
|
||||
`contextManager.ts` | Správa životního cyklu kontextu požadavku: vytváří a sleduje objekty kontextu pro každý požadavek s metadaty (ID požadavku, časová razítka, informace o poskytovateli) pro ladění a protokolování.
|
||||
`ipFilter.ts` | Řízení přístupu založené na IP adrese: podporuje režimy povolených seznamů a blokovaných seznamů. Před zpracováním požadavků API ověřuje IP adresu klienta podle nakonfigurovaných pravidel.
|
||||
`sessionManager.ts` | Sledování relací s otisky prstů klientů: sleduje aktivní relace pomocí hašovaných identifikátorů klientů, monitoruje počty požadavků a poskytuje metriky relací.
|
||||
`signatureCache.ts` | Mezipaměť deduplikace na základě signatur požadavků: zabraňuje duplicitním požadavkům ukládáním nedávných signatur požadavků do mezipaměti a vrácením odpovědí z mezipaměti pro identické požadavky v rámci časového okna.
|
||||
`systemPrompt.ts` | Globální vložení systémového výzvy: přidá konfigurovatelnou systémovou výzvu ke všem požadavkům s možností kompatibility pro jednotlivé poskytovatele.
|
||||
`thinkingBudget.ts` | Správa rozpočtu tokenů uvažování: podporuje režimy průchodu, automatický (konfigurace strip thinking), vlastní (pevný rozpočet) a adaptivní (měřítko složitosti) pro řízení tokenů myšlení/uvažování.
|
||||
`wildcardRouter.ts` | Směrování podle vzorů zástupných znaků: rozpoznává vzory zástupných znaků (např. `*/claude-*` ) na konkrétní páry poskytovatel/model na základě dostupnosti a priority.
|
||||
|
||||
#### Deduplikace obnovení tokenů
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant R1 as Request 1
|
||||
participant R2 as Request 2
|
||||
participant Cache as refreshPromiseCache
|
||||
participant OAuth as OAuth Provider
|
||||
|
||||
R1->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: No in-flight promise
|
||||
Cache->>OAuth: Start refresh
|
||||
R2->>Cache: getAccessToken("gemini", token)
|
||||
Cache->>Cache: Found in-flight promise
|
||||
Cache-->>R2: Return existing promise
|
||||
OAuth-->>Cache: New access token
|
||||
Cache-->>R1: New access token
|
||||
Cache-->>R2: Same access token (shared)
|
||||
Cache->>Cache: Delete cache entry
|
||||
```
|
||||
|
||||
#### Záložní stavový automat účtu
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Active
|
||||
Active --> Error: Request fails (401/429/500)
|
||||
Error --> Cooldown: Apply backoff
|
||||
Cooldown --> Active: Cooldown expires
|
||||
Active --> Active: Request succeeds (reset backoff)
|
||||
|
||||
state Error {
|
||||
[*] --> ClassifyError
|
||||
ClassifyError --> ShouldFallback: Rate limit / Auth / Transient
|
||||
ClassifyError --> NoFallback: 400 Bad Request
|
||||
}
|
||||
|
||||
state Cooldown {
|
||||
[*] --> ExponentialBackoff
|
||||
ExponentialBackoff: Level 0 = 1s
|
||||
ExponentialBackoff: Level 1 = 2s
|
||||
ExponentialBackoff: Level 2 = 4s
|
||||
ExponentialBackoff: Max = 2min
|
||||
}
|
||||
```
|
||||
|
||||
#### Řetězec kombinovaných modelů
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Request with\ncombo model"] --> B["Model A"]
|
||||
B -->|"2xx Success"| C["Return response"]
|
||||
B -->|"429/401/500"| D{"Fallback\neligible?"}
|
||||
D -->|Yes| E["Model B"]
|
||||
D -->|No| F["Return error"]
|
||||
E -->|"2xx Success"| C
|
||||
E -->|"429/401/500"| G{"Fallback\neligible?"}
|
||||
G -->|Yes| H["Model C"]
|
||||
G -->|No| F
|
||||
H -->|"2xx Success"| C
|
||||
H -->|"Fail"| I["All failed →\nReturn last status"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.5 Překladač ( `open-sse/translator/` )
|
||||
|
||||
**Modul pro překlad formátů** využívající systém samoregistrujících se pluginů.
|
||||
|
||||
#### Architektura
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph "Request Translation"
|
||||
A["Claude → OpenAI"]
|
||||
B["Gemini → OpenAI"]
|
||||
C["Antigravity → OpenAI"]
|
||||
D["OpenAI Responses → OpenAI"]
|
||||
E["OpenAI → Claude"]
|
||||
F["OpenAI → Gemini"]
|
||||
G["OpenAI → Kiro"]
|
||||
H["OpenAI → Cursor"]
|
||||
end
|
||||
|
||||
subgraph "Response Translation"
|
||||
I["Claude → OpenAI"]
|
||||
J["Gemini → OpenAI"]
|
||||
K["Kiro → OpenAI"]
|
||||
L["Cursor → OpenAI"]
|
||||
M["OpenAI → Claude"]
|
||||
N["OpenAI → Antigravity"]
|
||||
O["OpenAI → Responses"]
|
||||
end
|
||||
```
|
||||
|
||||
Adresář | Soubory | Popis
|
||||
--- | --- | ---
|
||||
`request/` | 8 překladatelů | Převod těl požadavků mezi formáty. Každý soubor se při importu sám zaregistruje pomocí `register(from, to, fn)` .
|
||||
`response/` | 7 překladatelů | Převádí bloky odpovědí streamovaných dat mezi formáty. Zpracovává typy událostí SSE, myšlenkové bloky a volání nástrojů.
|
||||
`helpers/` | 6 pomocníků | Sdílené utility: `claudeHelper` (extrakce systémových prompts, thinking config), `geminiHelper` (mapování částí/obsahu), `openaiHelper` (filtrování formátů), `toolCallHelper` (generování ID, vkládání chybějících odpovědí), `maxTokensHelper` , `responsesApiHelper` .
|
||||
`index.ts` | — | Překladový engine: `translateRequest()` , `translateResponse()` , správa stavu, registr.
|
||||
`formats.ts` | — | Formátovací konstanty: `OPENAI` , `CLAUDE` , `GEMINI` , `ANTIGRAVITY` , `KIRO` , `CURSOR` , `OPENAI_RESPONSES` .
|
||||
|
||||
#### Klíčový design: Samoregistrující se pluginy
|
||||
|
||||
```javascript
|
||||
// Each translator file calls register() on import:
|
||||
import { register } from "../index.js";
|
||||
register("claude", "openai", translateClaudeToOpenAI);
|
||||
|
||||
// The index.js imports all translator files, triggering registration:
|
||||
import "./request/claude-to-openai.js"; // ← self-registers
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.6 Nástroje ( `open-sse/utils/` )
|
||||
|
||||
Soubor | Účel
|
||||
--- | ---
|
||||
`error.ts` | Vytváření chybové odezvy (formát kompatibilní s OpenAI), parsování chyb v upstreamu, extrakce doby opakování Antigravity z chybových zpráv, streamování chyb SSE.
|
||||
`stream.ts` | **SSE Transform Stream** — základní streamovací kanál. Dva režimy: `TRANSLATE` (plný překlad formátu) a `PASSTHROUGH` (normalizace + extrakce využití). Zpracovává ukládání bloků do vyrovnávací paměti, odhad využití a sledování délky obsahu. Instance kodéru/dekodéru pro každý stream se vyhýbají sdílenému stavu.
|
||||
`streamHelpers.ts` | Nízkoúrovňové utility SSE: `parseSSELine` (tolerantní k bílým znakům), `hasValuableContent` (filtruje prázdné segmenty pro OpenAI/Claude/Gemini), `fixInvalidId` , `formatSSE` (serializace SSE s ohledem na formát s čištěním `perf_metrics` ).
|
||||
`usageTracking.ts` | Extrakce využití tokenů z libovolného formátu (Claude/OpenAI/Gemini/Responses), odhad s oddělenými poměry znaků na token pro jednotlivé nástroje/zprávy, přidání vyrovnávací paměti (bezpečnostní rezerva 2000 tokenů), filtrování polí specifických pro formát, protokolování konzole s barvami ANSI.
|
||||
`requestLogger.ts` | Protokolování požadavků na základě souborů (přihlášení pomocí `ENABLE_REQUEST_LOGS=true` ). Vytváří složky relací s očíslovanými soubory: `1_req_client.json` → `7_res_client.txt` . Veškeré I/O operace jsou asynchronní (aktivní a zapomenutý). Maskuje citlivé hlavičky.
|
||||
`bypassHandler.ts` | Zachycuje specifické vzory z Claude CLI (extrakce názvu, zahřívání, počet) a vrací falešné odpovědi bez volání jakéhokoli poskytovatele. Podporuje streamování i nestreamování. Záměrně omezeno na rozsah Claude CLI.
|
||||
`networkProxy.ts` | Rozpozná URL odchozí proxy pro daného poskytovatele s prioritou: konfigurace specifická pro poskytovatele → globální konfigurace → proměnné prostředí ( `HTTPS_PROXY` / `HTTP_PROXY` / `ALL_PROXY` ). Podporuje výjimky `NO_PROXY` . Ukládá konfiguraci do mezipaměti po dobu 30 sekund.
|
||||
|
||||
#### Streamovací kanál SSE
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Provider SSE stream"] --> B["TextDecoder\n(per-stream instance)"]
|
||||
B --> C["Buffer lines\n(split on newline)"]
|
||||
C --> D["parseSSELine()\n(trim whitespace, parse JSON)"]
|
||||
D --> E{"Mode?"}
|
||||
E -->|TRANSLATE| F["translateResponse()\ntarget → OpenAI → source"]
|
||||
E -->|PASSTHROUGH| G["fixInvalidId()\nnormalize chunk"]
|
||||
F --> H["hasValuableContent()\nfilter empty chunks"]
|
||||
G --> H
|
||||
H -->|"Has content"| I["extractUsage()\ntrack token counts"]
|
||||
H -->|"Empty"| J["Skip chunk"]
|
||||
I --> K["formatSSE()\nserialize + clean perf_metrics"]
|
||||
K --> L["TextEncoder\n(per-stream instance)"]
|
||||
L --> M["Enqueue to\nclient stream"]
|
||||
|
||||
style A fill:#f9f,stroke:#333
|
||||
style M fill:#9f9,stroke:#333
|
||||
```
|
||||
|
||||
#### Struktura relace protokolování požadavků
|
||||
|
||||
```
|
||||
logs/
|
||||
└── claude_gemini_claude-sonnet_20260208_143045/
|
||||
├── 1_req_client.json ← Raw client request
|
||||
├── 2_req_source.json ← After initial conversion
|
||||
├── 3_req_openai.json ← OpenAI intermediate format
|
||||
├── 4_req_target.json ← Final target format
|
||||
├── 5_res_provider.txt ← Provider SSE chunks (streaming)
|
||||
├── 5_res_provider.json ← Provider response (non-streaming)
|
||||
├── 6_res_openai.txt ← OpenAI intermediate chunks
|
||||
├── 7_res_client.txt ← Client-facing SSE chunks
|
||||
└── 6_error.json ← Error details (if any)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4.7 Aplikační vrstva ( `src/` )
|
||||
|
||||
Adresář | Účel
|
||||
--- | ---
|
||||
`src/app/` | Webové uživatelské rozhraní, trasy API, middleware Express, obslužné rutiny zpětných volání OAuth
|
||||
`src/lib/` | Přístup k databázi ( `localDb.ts` , `usageDb.ts` ), ověřování, sdílení
|
||||
`src/mitm/` | Nástroje proxy typu „man-in-the-middle“ pro zachycení provozu poskytovatelů
|
||||
`src/models/` | Definice modelů databáze
|
||||
`src/shared/` | Obálky kolem funkcí open-sse (provider, stream, error atd.)
|
||||
`src/sse/` | Obslužné rutiny koncových bodů SSE, které propojují knihovnu open-sse s trasami Express
|
||||
`src/store/` | Správa stavu aplikací
|
||||
|
||||
#### Významné trasy API
|
||||
|
||||
Trasa | Metody | Účel
|
||||
--- | --- | ---
|
||||
`/api/provider-models` | ZÍSKAT/ODESLAT/SMAZAT | CRUD pro vlastní modely na poskytovatele
|
||||
`/api/models/catalog` | ZÍSKAT | Agregovaný katalog všech modelů (chat, embedding, image, custom) seskupených podle poskytovatele
|
||||
`/api/settings/proxy` | ZÍSKAT/VLOŽIT/ODSTRANIT | Konfigurace hierarchické odchozí proxy ( `global/providers/combos/keys` )
|
||||
`/api/settings/proxy/test` | ZVEŘEJNIT | Ověřuje připojení proxy a vrací veřejnou IP adresu/latenci
|
||||
`/v1/providers/[provider]/chat/completions` | ZVEŘEJNIT | Vyhrazené dokončování chatu pro jednotlivé poskytovatele s ověřováním modelu
|
||||
`/v1/providers/[provider]/embeddings` | ZVEŘEJNIT | Vyhrazené vkládání pro jednotlivé poskytovatele s ověřováním modelu
|
||||
`/v1/providers/[provider]/images/generations` | ZVEŘEJNIT | Vyhrazené generování obrázků pro každého poskytovatele s ověřováním modelu
|
||||
`/api/settings/ip-filter` | ZÍSKAT/VLOŽIT | Správa povolených/blokovaných IP adres
|
||||
`/api/settings/thinking-budget` | ZÍSKAT/VLOŽIT | Konfigurace rozpočtu tokenů zdůvodnění (průchozí/automatická/vlastní/adaptivní)
|
||||
`/api/settings/system-prompt` | ZÍSKAT/VLOŽIT | Globální vložení systémového promptu pro všechny požadavky
|
||||
`/api/sessions` | ZÍSKAT | Sledování a metriky aktivních relací
|
||||
`/api/rate-limits` | ZÍSKAT | Stav limitu sazby na účet
|
||||
|
||||
---
|
||||
|
||||
## 5. Klíčové návrhové vzory
|
||||
|
||||
### 5.1 Překlad typu Hub-and-Spoke
|
||||
|
||||
Všechny formáty se překládají prostřednictvím **formátu OpenAI jako ústředny** . Přidání nového poskytovatele vyžaduje napsání pouze **jednoho páru** překladačů (do/z OpenAI), nikoli N párů.
|
||||
|
||||
### 5.2 Vzor strategie exekutora
|
||||
|
||||
Každý poskytovatel má vyhrazenou třídu exekutoru, která dědí z `BaseExecutor` . Továrna v `executors/index.ts` vybere ten správný za běhu.
|
||||
|
||||
### 5.3 Systém samoregistračních pluginů
|
||||
|
||||
Moduly překladače se při importu registrují pomocí `register()` . Přidání nového překladače znamená pouze vytvoření souboru a jeho import.
|
||||
|
||||
### 5.4 Záložní účet s exponenciálním oddlužením
|
||||
|
||||
Když poskytovatel vrátí 429/401/500, systém může přepnout na další účet s exponenciálním zpožděním (1s → 2s → 4s → max. 2min).
|
||||
|
||||
### 5.5 Kombinované modelové řetězy
|
||||
|
||||
„Kombinace“ seskupuje více řetězců `provider/model` . Pokud první selže, automaticky se vrátí k dalšímu.
|
||||
|
||||
### 5.6 Stavový streamovací překlad
|
||||
|
||||
Překlad odpovědí udržuje stav napříč bloky SSE (sledování myšlenkových bloků, akumulace volání nástrojů, indexování bloků obsahu) prostřednictvím mechanismu `initState()` .
|
||||
|
||||
### 5.7 Bezpečnostní vyrovnávací paměť pro použití
|
||||
|
||||
K hlášenému využití je přidána vyrovnávací paměť o kapacitě 2000 tokenů, aby se zabránilo tomu, že klienti dosáhnou limitů kontextového okna v důsledku režijních nákladů systémových výzev a překladu formátu.
|
||||
|
||||
---
|
||||
|
||||
## 6. Podporované formáty
|
||||
|
||||
Formát | Směr | Identifikátor
|
||||
--- | --- | ---
|
||||
Dokončení chatu OpenAI | zdroj + cíl | `openai`
|
||||
API pro odpovědi OpenAI | zdroj + cíl | `openai-responses`
|
||||
Antropický Claude | zdroj + cíl | `claude`
|
||||
Google Gemini | zdroj + cíl | `gemini`
|
||||
Rozhraní příkazového řádku Google Gemini | pouze cíl | `gemini-cli`
|
||||
Antigravitace | zdroj + cíl | `antigravity`
|
||||
AWS Kiro | pouze cíl | `kiro`
|
||||
Kurzor | pouze cíl | `cursor`
|
||||
|
||||
---
|
||||
|
||||
## 7. Podporovaní poskytovatelé
|
||||
|
||||
Poskytovatel | Metoda ověřování | Vykonavatel | Klíčové poznámky
|
||||
--- | --- | --- | ---
|
||||
Antropický Claude | Klíč API nebo OAuth | Výchozí | Používá hlavičku `x-api-key`
|
||||
Google Gemini | Klíč API nebo OAuth | Výchozí | Používá hlavičku `x-goog-api-key`
|
||||
Rozhraní příkazového řádku Google Gemini | OAuth | GeminiCLI | Používá koncový bod `streamGenerateContent`
|
||||
Antigravitace | OAuth | Antigravitace | Záložní více URL adres, vlastní analýza opakovaných pokusů
|
||||
OpenAI | Klíč API | Výchozí | Autorizace standardního nosiče
|
||||
Kodex | OAuth | Kodex | Vkládá systémové instrukce, řídí myšlení
|
||||
GitHub Copilot | OAuth + token Copilot | Github | Duální token, napodobování záhlaví VSCode
|
||||
Kiro (AWS) | AWS SSO OIDC nebo sociální sítě | Kiro | Analýza binárního EventStreamu
|
||||
IDE kurzoru | Autorizace kontrolního součtu | Kurzor | Kódování Protobuf, kontrolní součty SHA-256
|
||||
Qwen | OAuth | Výchozí | Standardní ověřování
|
||||
iFlow | OAuth (základní + nosič) | Výchozí | Duální hlavička pro autorizaci
|
||||
OpenRouter | Klíč API | Výchozí | Autorizace standardního nosiče
|
||||
GLM, Kimi, MiniMax | Klíč API | Výchozí | Kompatibilní s Claude, použijte `x-api-key`
|
||||
`openai-compatible-*` | Klíč API | Výchozí | Dynamické: jakýkoli koncový bod kompatibilní s OpenAI
|
||||
`anthropic-compatible-*` | Klíč API | Výchozí | Dynamický: jakýkoli koncový bod kompatibilní s Claude
|
||||
|
||||
---
|
||||
|
||||
## 8. Souhrn datového toku
|
||||
|
||||
### Žádost o streamování
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor\nbuildUrl + buildHeaders"]
|
||||
D --> E["fetch(providerURL)"]
|
||||
E --> F["createSSEStream()\nTRANSLATE mode"]
|
||||
F --> G["parseSSELine()"]
|
||||
G --> H["translateResponse()\ntarget → OpenAI → source"]
|
||||
H --> I["extractUsage()\n+ addBuffer"]
|
||||
I --> J["formatSSE()"]
|
||||
J --> K["Client receives\ntranslated SSE"]
|
||||
K --> L["logUsage()\nsaveRequestUsage()"]
|
||||
```
|
||||
|
||||
### Žádost o nestreamování
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Client"] --> B["detectFormat()"]
|
||||
B --> C["translateRequest()\nsource → OpenAI → target"]
|
||||
C --> D["Executor.execute()"]
|
||||
D --> E["translateResponse()\ntarget → OpenAI → source"]
|
||||
E --> F["Return JSON\nresponse"]
|
||||
```
|
||||
|
||||
### Obtokový tok (Claude CLI)
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["Claude CLI request"] --> B{"Match bypass\npattern?"}
|
||||
B -->|"Title/Warmup/Count"| C["Generate fake\nOpenAI response"]
|
||||
B -->|"No match"| D["Normal flow"]
|
||||
C --> E["Translate to\nsource format"]
|
||||
E --> F["Return without\ncalling provider"]
|
||||
```
|
||||
273
docs/i18n/cs/CONTRIBUTING.md
Normal file
273
docs/i18n/cs/CONTRIBUTING.md
Normal file
@@ -0,0 +1,273 @@
|
||||
# Přispívání k OmniRoute
|
||||
|
||||
Děkujeme za váš zájem o přispění! Tato příručka obsahuje vše, co potřebujete k zahájení.
|
||||
|
||||
---
|
||||
|
||||
## Nastavení vývoje
|
||||
|
||||
### Předpoklady
|
||||
|
||||
- **Node.js** 20+ (doporučeno: 22 LTS)
|
||||
- **npm** 10+
|
||||
- **Git**
|
||||
|
||||
### Klonovat a instalovat
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute
|
||||
npm install
|
||||
```
|
||||
|
||||
### Proměnné prostředí
|
||||
|
||||
```bash
|
||||
# Create your .env from the template
|
||||
cp .env.example .env
|
||||
|
||||
# Generate required secrets
|
||||
echo "JWT_SECRET=$(openssl rand -base64 48)" >> .env
|
||||
echo "API_KEY_SECRET=$(openssl rand -hex 32)" >> .env
|
||||
```
|
||||
|
||||
Klíčové proměnné pro vývoj:
|
||||
|
||||
Proměnná | Výchozí nastavení pro vývoj | Popis
|
||||
--- | --- | ---
|
||||
`PORT` | `3000` | Port serveru
|
||||
`NEXT_PUBLIC_BASE_URL` | `http://localhost:3000` | Základní URL pro frontend
|
||||
`JWT_SECRET` | (vygenerovat výše) | Tajemství podpisu JWT
|
||||
`INITIAL_PASSWORD` | `123456` | První přihlašovací heslo
|
||||
`ENABLE_REQUEST_LOGS` | `false` | Povolit protokoly požadavků na ladění
|
||||
|
||||
### Spuštěno lokálně
|
||||
|
||||
```bash
|
||||
# Development mode (hot reload)
|
||||
npm run dev
|
||||
|
||||
# Production build
|
||||
npm run build
|
||||
npm run start
|
||||
|
||||
# Common port configuration
|
||||
PORT=20128 NEXT_PUBLIC_BASE_URL=http://localhost:20128 npm run dev
|
||||
```
|
||||
|
||||
Výchozí adresy URL:
|
||||
|
||||
- **Dashboard** : `http://localhost:3000/dashboard`
|
||||
- **API** : `http://localhost:3000/v1`
|
||||
|
||||
---
|
||||
|
||||
## Pracovní postup Gitu
|
||||
|
||||
> ⚠️ **NIKDY se necommitujte přímo do `main` .** Vždy používejte větve feature.
|
||||
|
||||
```bash
|
||||
git checkout -b feat/your-feature-name
|
||||
# ... make changes ...
|
||||
git commit -m "feat: describe your change"
|
||||
git push -u origin feat/your-feature-name
|
||||
# Open a Pull Request on GitHub
|
||||
```
|
||||
|
||||
### Pojmenování poboček
|
||||
|
||||
Předpona | Účel
|
||||
--- | ---
|
||||
`feat/` | Nové funkce
|
||||
`fix/` | Opravy chyb
|
||||
`refactor/` | Restrukturalizace kódu
|
||||
`docs/` | Změny dokumentace
|
||||
`test/` | Doplnění/opravy testů
|
||||
`chore/` | Nástroje, CI, závislosti
|
||||
|
||||
### Zprávy o potvrzení
|
||||
|
||||
Postupujte podle [konvenčních commitů](https://www.conventionalcommits.org/) :
|
||||
|
||||
```
|
||||
feat: add circuit breaker for provider calls
|
||||
fix: resolve JWT secret validation edge case
|
||||
docs: update SECURITY.md with PII protection
|
||||
test: add observability unit tests
|
||||
refactor(db): consolidate rate limit tables
|
||||
```
|
||||
|
||||
Rozsahy: `db` , `sse` , `oauth` , `dashboard` , `api` , `cli` , `docker` , `ci` .
|
||||
|
||||
---
|
||||
|
||||
## Spouštění testů
|
||||
|
||||
```bash
|
||||
# All unit tests
|
||||
npm test
|
||||
npm run test:unit
|
||||
|
||||
# Specific test suites
|
||||
npm run test:security # Security tests
|
||||
npm run test:fixes # Fix verification tests
|
||||
|
||||
# With coverage
|
||||
npm run test:coverage
|
||||
|
||||
# E2E tests (requires Playwright)
|
||||
npm run test:e2e
|
||||
|
||||
# Lint + format check
|
||||
npm run lint
|
||||
npm run check
|
||||
```
|
||||
|
||||
Aktuální stav testování: **368+ jednotkových testů** zahrnujících:
|
||||
|
||||
- Poskytovatelé překladů a konverze formátů
|
||||
- Omezení rychlosti, jistič a odolnost
|
||||
- Sémantická mezipaměť, idempotence, sledování průběhu
|
||||
- Databázové operace a schéma
|
||||
- Toky a ověřování OAuth
|
||||
- Ověření koncového bodu API
|
||||
|
||||
---
|
||||
|
||||
## Styl kódu
|
||||
|
||||
- **ESLint** — Spustí `npm run lint` před commitem
|
||||
- **Hezčí** – Automaticky naformátováno pomocí `lint-staged` při commitu
|
||||
- **TypeScript** — Veškerý kód `src/` používá `.ts` / `.tsx` ; dokument s TSDoc ( `@param` , `@returns` , `@throws` )
|
||||
- **No `eval()`** — ESLint vynucuje `no-eval` , `no-implied-eval` , `no-new-func`
|
||||
- **Ověření Zod** — Použití schémat Zod pro ověřování vstupu API
|
||||
|
||||
---
|
||||
|
||||
## Struktura projektu
|
||||
|
||||
```
|
||||
src/ # TypeScript (.ts / .tsx)
|
||||
├── app/ # Next.js App Router
|
||||
│ ├── (dashboard)/ # Dashboard pages (.tsx)
|
||||
│ ├── api/ # API routes (.ts)
|
||||
│ └── login/ # Auth pages (.tsx)
|
||||
├── domain/ # Domain types and response helpers (.ts)
|
||||
├── lib/ # Core business logic (.ts)
|
||||
│ ├── db/ # SQLite database layer
|
||||
│ ├── oauth/ # OAuth services per provider
|
||||
│ ├── cacheLayer.ts # LRU cache
|
||||
│ ├── semanticCache.ts # Semantic response cache
|
||||
│ ├── idempotencyLayer.ts # Request deduplication
|
||||
│ └── localDb.ts # Settings facade (LowDB for config, SQLite for domain data)
|
||||
├── shared/
|
||||
│ ├── components/ # React components (.tsx)
|
||||
│ ├── middleware/ # Correlation IDs, etc.
|
||||
│ ├── utils/ # Circuit breaker, sanitizer, etc.
|
||||
│ └── validation/ # Zod schemas
|
||||
└── sse/ # SSE chat handlers (.ts)
|
||||
|
||||
open-sse/ # @omniroute/open-sse workspace (JavaScript)
|
||||
├── handlers/ # chatCore.js — main request handler
|
||||
├── services/ # Rate limit, fallback
|
||||
├── translators/ # Format converters (OpenAI ↔ Claude ↔ Gemini)
|
||||
└── utils/ # Progress tracker, stream helpers
|
||||
|
||||
tests/
|
||||
├── unit/ # Node.js test runner (.test.mjs)
|
||||
└── e2e/ # Playwright tests
|
||||
|
||||
docs/ # Documentation
|
||||
├── USER_GUIDE.md # Provider setup, CLI integration
|
||||
├── API_REFERENCE.md # All endpoints
|
||||
├── TROUBLESHOOTING.md # Common issues
|
||||
├── ARCHITECTURE.md # System architecture
|
||||
└── adr/ # Architecture Decision Records
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Přidání nového poskytovatele
|
||||
|
||||
### Krok 1: Služba OAuth (pokud používáte OAuth)
|
||||
|
||||
Vytvořte `src/lib/oauth/services/your-provider.ts` rozšiřující `OAuthService` :
|
||||
|
||||
```typescript
|
||||
import { OAuthService } from "../OAuthService";
|
||||
|
||||
export class YourProviderService extends OAuthService {
|
||||
constructor() {
|
||||
super({
|
||||
name: "your-provider",
|
||||
authUrl: "https://provider.com/oauth/authorize",
|
||||
tokenUrl: "https://provider.com/oauth/token",
|
||||
clientId: "...",
|
||||
scopes: ["..."],
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Krok 2: Registrace poskytovatele
|
||||
|
||||
Přidat do `src/lib/oauth/providers.ts` :
|
||||
|
||||
```typescript
|
||||
import { YourProviderService } from "./services/your-provider";
|
||||
// Add to the providers map
|
||||
```
|
||||
|
||||
### Krok 3: Přidání konstant
|
||||
|
||||
Přidejte konstanty poskytovatele do `src/lib/providerConstants.ts` :
|
||||
|
||||
- Předpona poskytovatele (např. `yp/` )
|
||||
- Výchozí modely
|
||||
- Informace o cenách
|
||||
|
||||
### Krok 4: Přidání překladače (pokud se nejedná o formát OpenAI)
|
||||
|
||||
Pokud poskytovatel používá vlastní formát API, vytvořte překladač v `open-sse/translators/` .
|
||||
|
||||
### Krok 5: Přidání časového limitu
|
||||
|
||||
Přidejte konfiguraci časového limitu požadavku do `src/shared/utils/requestTimeout.ts` .
|
||||
|
||||
### Krok 6: Přidání testů
|
||||
|
||||
Pište jednotkové testy v `tests/unit/` které pokrývají minimálně:
|
||||
|
||||
- Registrace poskytovatele
|
||||
- Překlad žádostí/odpovědí
|
||||
- Ošetření chyb
|
||||
|
||||
---
|
||||
|
||||
## Kontrolní seznam žádostí o natažení
|
||||
|
||||
- [ ] Testy prošly ( `npm test` )
|
||||
- [ ] Průchody pro linting ( `npm run lint` )
|
||||
- [ ] Sestavení proběhlo úspěšně ( `npm run build` )
|
||||
- [ ] Pro nové veřejné funkce a rozhraní přidány typy TypeScript
|
||||
- [ ] Žádné pevně zakódované tajné kódy ani záložní hodnoty
|
||||
- [ ] Aktualizován CHANGELOG (pokud se změna týká uživatele)
|
||||
- [ ] Aktualizovaná dokumentace (pokud je to relevantní)
|
||||
|
||||
---
|
||||
|
||||
## Uvolnění
|
||||
|
||||
Když je vytvořena nová verze GitHubu (např. `v0.4.0` ), balíček je **automaticky publikován do npm** prostřednictvím akcí GitHubu:
|
||||
|
||||
```bash
|
||||
gh release create v0.4.0 --title "v0.4.0" --generate-notes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Získání pomoci
|
||||
|
||||
- **Architektura** : Viz [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md)
|
||||
- **Problémy** : [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **ADR** : Viz `docs/adr/`
|
||||
143
docs/i18n/cs/FEATURES.md
Normal file
143
docs/i18n/cs/FEATURES.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# OmniRoute — Galerie funkcí řídicího panelu
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](FEATURES.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/FEATURES.md) | 🇪🇸 [Español](i18n/es/FEATURES.md) | 🇫🇷 [Français](i18n/fr/FEATURES.md) | 🇮🇹 [Italiano](i18n/it/FEATURES.md) | 🇷🇺 [Русский](i18n/ru/FEATURES.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](i18n/de/FEATURES.md) | 🇮🇳 [हिन्दी](i18n/in/FEATURES.md) | 🇹🇭 [ไทย](i18n/th/FEATURES.md) | 🇺🇦 [Українська](i18n/uk-UA/FEATURES.md) | 🇸🇦 [العربية](i18n/ar/FEATURES.md) | 🇯🇵[日本語](i18n/ja/FEATURES.md)| 🇻🇳 [Tiếng Việt](i18n/vi/FEATURES.md) | 🇧🇬 [Български](i18n/bg/FEATURES.md) | 🇩🇰 [Dánsko](i18n/da/FEATURES.md) | 🇫🇮 [Suomi](i18n/fi/FEATURES.md) | 🇮🇱 [עברית](i18n/he/FEATURES.md) | 🇭🇺 [maďarština](i18n/hu/FEATURES.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/FEATURES.md) | 🇰🇷 [한국어](i18n/ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/FEATURES.md) | 🇳🇱 [Nizozemsko](i18n/nl/FEATURES.md) | 🇳🇴 [Norsk](i18n/no/FEATURES.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/FEATURES.md) | 🇷🇴 [Română](i18n/ro/FEATURES.md) | 🇵🇱 [Polski](i18n/pl/FEATURES.md) | 🇸🇰 [Slovenčina](i18n/sk/FEATURES.md) | 🇸🇪 [Svenska](i18n/sv/FEATURES.md) | 🇵🇭 [Filipínec](i18n/phi/FEATURES.md) | 🇨🇿 [Čeština](i18n/cs/FEATURES.md)
|
||||
|
||||
Vizuální průvodce všemi částmi ovládacího panelu OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Poskytovatelé
|
||||
|
||||
Spravujte připojení poskytovatelů AI: poskytovatelé OAuth (Claude Code, Codex, Gemini CLI), poskytovatelé klíčů API (Groq, DeepSeek, OpenRouter) a bezplatní poskytovatelé (iFlow, Qwen, Kiro). Účty Kiro zahrnují sledování zůstatku kreditů – zbývající kredity, celkový limit a datum obnovení jsou viditelné v Dashboard → Usage.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎨 Kombinace
|
||||
|
||||
Vytvářejte kombinace směrování modelů pomocí 6 strategií: prioritní, vážená, kruhová, náhodná, nejméně používaná a nákladově optimalizovaná. Každá kombinace řetězí více modelů s automatickým přepínáním mezi nimi a zahrnuje rychlé šablony a kontroly připravenosti.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 📊 Analytika
|
||||
|
||||
Komplexní analýzy využití se spotřebou tokenů, odhady nákladů, mapami aktivit, týdenními distribučními grafy a rozpisy podle jednotlivých poskytovatelů.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🏥 Stav systému
|
||||
|
||||
Monitorování v reálném čase: dostupnost, paměť, verze, percentily latence (p50/p95/p99), statistiky mezipaměti a stavy jističů poskytovatelů.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Překladatelské hřiště
|
||||
|
||||
Čtyři režimy pro ladění překladů API: **Playground** (převodník formátů), **Chat Tester** (živé požadavky), **Test Bench** (dávkové testy) a **Live Monitor** (stream v reálném čase).
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🎮 Modelové hřiště *(v2.0.9+)*
|
||||
|
||||
Otestujte libovolný model přímo z řídicího panelu. Vyberte poskytovatele, model a koncový bod, pište výzvy pomocí editoru Monaco, streamujte odpovědi v reálném čase, přerušte stream a zobrazte metriky časování.
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Témata *(v2.0.5+)*
|
||||
|
||||
Přizpůsobitelná barevná témata pro celý dashboard. Vyberte si ze 7 přednastavených barev (korálová, modrá, červená, zelená, fialová, oranžová, azurová) nebo si vytvořte vlastní téma výběrem libovolné hexadecimální barvy. Podporuje světlý, tmavý a systémový režim.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Nastavení
|
||||
|
||||
Komplexní panel nastavení s kartami:
|
||||
|
||||
- **Obecné** – Systémové úložiště, správa záloh (export/import databáze)
|
||||
- **Vzhled** – Výběr motivu (tmavý/světlý/systémový), přednastavené barevné motivy a vlastní barvy, viditelnost protokolu stavu
|
||||
- **Zabezpečení** — ochrana koncových bodů API, blokování vlastních poskytovatelů, filtrování IP adres, informace o relaci
|
||||
- **Směrování** — Aliasy modelů, degradace úloh na pozadí
|
||||
- **Odolnost** — Perzistence omezení rychlosti, ladění jističe
|
||||
- **Pokročilé** – Přepsání konfigurace
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔧 Nástroje CLI
|
||||
|
||||
Konfigurace nástrojů pro kódování s umělou inteligencí jedním kliknutím: Claude Code, Codex CLI, Gemini CLI, OpenClaw, Kilo Code, Antigravity, Cline, Continue, Cursor a Factory Droid. Nabízí automatické použití/resetování konfigurace, profily připojení a mapování modelů.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🤖 Agenti CLI *(v2.0.11+)*
|
||||
|
||||
Ovládací panel pro vyhledávání a správu agentů CLI. Zobrazuje mřížku 14 vestavěných agentů (Codex, Claude, Goose, Gemini CLI, OpenClaw, Aider, OpenCode, Cline, Qwen Code, ForgeCode, Amazon Q, Open Interpreter, Cursor CLI, Warp) s:
|
||||
|
||||
- **Stav instalace** — Nainstalováno / Nenalezeno s detekcí verze
|
||||
- **Odznaky protokolů** – stdio, HTTP atd.
|
||||
- **Vlastní agenti** — Registrace libovolného nástroje CLI pomocí formuláře (název, binární soubor, verze příkazu, argumenty spawn)
|
||||
- **Porovnávání otisků prstů v příkazovém řádku** – Přepínání pro jednotlivé poskytovatele pro porovnávání nativních podpisů požadavků v příkazovém řádku, čímž se snižuje riziko zablokování a zároveň se zachovává IP adresa proxy.
|
||||
|
||||
---
|
||||
|
||||
## 🖼️ Média *(v2.0.3+)*
|
||||
|
||||
Generujte obrázky, videa a hudbu z řídicího panelu. Podporuje OpenAI, xAI, Together, Hyperbolic, SD WebUI, ComfyUI, AnimateDiff, Stable Audio Open a MusicGen.
|
||||
|
||||
---
|
||||
|
||||
## 📝 Vyžádat si protokoly
|
||||
|
||||
Protokolování požadavků v reálném čase s filtrováním podle poskytovatele, modelu, účtu a klíče API. Zobrazuje stavové kódy, využití tokenů, latenci a podrobnosti o odpovědi.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🌐 Koncový bod API
|
||||
|
||||
Váš jednotný koncový bod API s rozpisem funkcí: Dokončování chatu, API odpovědí, vkládání, generování obrázků, změna pořadí, přepis zvuku, převod textu na řeč, moderování a registrované klíče API. Podpora cloudového proxy pro vzdálený přístup.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 🔑 Správa klíčů API
|
||||
|
||||
Vytvářejte, upravujte rozsah a rušte klíče API. Každý klíč lze omezit na konkrétní modely/poskytovatele s plným přístupem nebo oprávněním pouze pro čtení. Vizuální správa klíčů se sledováním využití.
|
||||
|
||||
---
|
||||
|
||||
## 📋 Záznam auditu
|
||||
|
||||
Sledování administrativních akcí s filtrováním podle typu akce, aktéra, cíle, IP adresy a časového razítka. Úplná historie bezpečnostních událostí.
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Desktopová aplikace
|
||||
|
||||
Desktopová aplikace Native Electron pro Windows, macOS a Linux. Spouštějte OmniRoute jako samostatnou aplikaci s integrací do systémové lišty, podporou offline, automatickými aktualizacemi a instalací jedním kliknutím.
|
||||
|
||||
Klíčové vlastnosti:
|
||||
|
||||
- Dotazování připravenosti serveru (žádná prázdná obrazovka při studeném startu)
|
||||
- Systémový panel se správou portů
|
||||
- Zásady zabezpečení obsahu
|
||||
- Jednoinstanční zámek
|
||||
- Automatická aktualizace při restartu
|
||||
- Podmíněné uživatelské rozhraní pro platformu (semafory pro macOS, výchozí záhlaví okna pro Windows/Linux)
|
||||
- Zpevněné balení buildů Electron — symbolicky odkazované `node_modules` v samostatném balíčku jsou detekovány a odmítnuty před balením, čímž se zabrání závislosti na buildovacím stroji za běhu (v2.5.5+)
|
||||
|
||||
📖 Úplnou dokumentaci naleznete v [`electron/README.md`](../electron/README.md) .
|
||||
83
docs/i18n/cs/MCP-SERVER.md
Normal file
83
docs/i18n/cs/MCP-SERVER.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Dokumentace k serveru OmniRoute MCP
|
||||
|
||||
> Server protokolu kontextu modelu s 16 inteligentními nástroji
|
||||
|
||||
## Instalace
|
||||
|
||||
OmniRoute MCP je integrovaný. Spusťte ho pomocí:
|
||||
|
||||
```bash
|
||||
omniroute --mcp
|
||||
```
|
||||
|
||||
Nebo prostřednictvím open-sse transportu:
|
||||
|
||||
```bash
|
||||
# HTTP streamable transport (port 20130)
|
||||
omniroute --dev # MCP auto-starts on /mcp endpoint
|
||||
```
|
||||
|
||||
## Konfigurace IDE
|
||||
|
||||
Viz [konfigurace IDE](integrations/ide-configs.md) pro nastavení Antigravity, Cursoru, Copilota a Claude Desktopu.
|
||||
|
||||
---
|
||||
|
||||
## Základní nástroje (8)
|
||||
|
||||
Nástroj | Popis
|
||||
:-- | :--
|
||||
`omniroute_get_health` | Stav brány, jističe, provozuschopnost
|
||||
`omniroute_list_combos` | Všechny nakonfigurované kombinace s modely
|
||||
`omniroute_get_combo_metrics` | Metriky výkonu pro konkrétní kombinaci
|
||||
`omniroute_switch_combo` | Přepnout aktivní kombinaci podle ID/jména
|
||||
`omniroute_check_quota` | Stav kvóty pro jednotlivé poskytovatele nebo všechny
|
||||
`omniroute_route_request` | Odeslání dokončení chatu přes OmniRoute
|
||||
`omniroute_cost_report` | Analýza nákladů za určité časové období
|
||||
`omniroute_list_models_catalog` | Kompletní katalog modelů s funkcemi
|
||||
|
||||
## Pokročilé nástroje (8)
|
||||
|
||||
Nástroj | Popis
|
||||
:-- | :--
|
||||
`omniroute_simulate_route` | Simulace trasování na dryru s fallback stromem
|
||||
`omniroute_set_budget_guard` | Rozpočet relace s akcemi degradace/blokování/upozornění
|
||||
`omniroute_set_resilience_profile` | Použít konzervativní/vyvážený/agresivní předvolbu
|
||||
`omniroute_test_combo` | Živé testování všech modelů v kombinaci
|
||||
`omniroute_get_provider_metrics` | Podrobné metriky pro jednoho poskytovatele
|
||||
`omniroute_best_combo_for_task` | Doporučení pro splnění úkolu a jeho vhodnosti s alternativami
|
||||
`omniroute_explain_route` | Vysvětlete minulé rozhodnutí o trase
|
||||
`omniroute_get_session_snapshot` | Stav celé relace: náklady, tokeny, chyby
|
||||
|
||||
## Ověřování
|
||||
|
||||
Nástroje MCP jsou ověřovány pomocí rozsahů klíčů API. Každý nástroj vyžaduje specifické rozsahy:
|
||||
|
||||
Rozsah | Nástroje
|
||||
:-- | :--
|
||||
`read:health` | get_health, get_provider_metrics
|
||||
`read:combos` | seznam_kombinací, získání_kombinovaných_metrik
|
||||
`write:combos` | přepínač_kombinace
|
||||
`read:quota` | check_quote
|
||||
`write:route` | požadavek_trasy, simulace_trasy, testovací_kombinace
|
||||
`read:usage` | zpráva_o_nákladech, získání_snímku_relace, vysvětlení_trasy
|
||||
`write:config` | set_budget_guard, set_resilience_profile
|
||||
`read:models` | seznam_modelů_katalog, nejlepší_kombinace_pro_úkol
|
||||
|
||||
## Protokolování auditu
|
||||
|
||||
Každé volání nástroje je zaznamenáno do `mcp_tool_audit` s touto funkcí:
|
||||
|
||||
- Název nástroje, argumenty, výsledek
|
||||
- Trvání (ms), úspěch/neúspěch
|
||||
- Haš klíče API, časové razítko
|
||||
|
||||
## Soubory
|
||||
|
||||
Soubor | Účel
|
||||
:-- | :--
|
||||
`open-sse/mcp-server/server.ts` | Vytvoření MCP serveru + 16 registrací nástrojů
|
||||
`open-sse/mcp-server/transport.ts` | Stdio + HTTP transport
|
||||
`open-sse/mcp-server/auth.ts` | Ověření klíče API + rozsahu
|
||||
`open-sse/mcp-server/audit.ts` | Protokolování auditu volání nástrojů
|
||||
`open-sse/mcp-server/tools/advancedTools.ts` | 8 pokročilých manipulátorů s nástroji
|
||||
2010
docs/i18n/cs/README.md
Normal file
2010
docs/i18n/cs/README.md
Normal file
File diff suppressed because it is too large
Load Diff
33
docs/i18n/cs/RELEASE_CHECKLIST.md
Normal file
33
docs/i18n/cs/RELEASE_CHECKLIST.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Kontrolní seznam vydání
|
||||
|
||||
Tento kontrolní seznam použijte před označením nebo publikováním nové verze OmniRoute.
|
||||
|
||||
## Verze a seznam změn
|
||||
|
||||
1. Navýšit verzi `package.json` ( `xyz` ) ve větvi release.
|
||||
2. Přesunout poznámky k vydání z `## [Unreleased]` v `CHANGELOG.md` do sekce s datem vydání:
|
||||
- `## [x.y.z] — YYYY-MM-DD`
|
||||
3. Ponechte `## [Unreleased]` jako první sekci changelogu pro nadcházející práci.
|
||||
4. Ujistěte se, že nejnovější sekce semver v `CHANGELOG.md` je rovna verzi `package.json` .
|
||||
|
||||
## Dokumentace API
|
||||
|
||||
1. Aktualizace `docs/openapi.yaml` :
|
||||
- Soubor `info.version` se musí rovnat verzi `package.json` .
|
||||
2. Ověřte příklady koncových bodů, pokud se změnily smlouvy API.
|
||||
|
||||
## Dokumentace k běhovému prostředí
|
||||
|
||||
1. Projděte si `docs/ARCHITECTURE.md` , zda nedochází k posunu v úložišti/běhovém prostředí.
|
||||
2. Projděte si soubor `docs/TROUBLESHOOTING.md` , kde naleznete informace o proměnné prostředí a provozním posunu.
|
||||
3. Aktualizujte lokalizovanou dokumentaci, pokud se zdrojová dokumentace výrazně změnila.
|
||||
|
||||
## Automatická kontrola
|
||||
|
||||
Před otevřením PR spusťte lokálně ochranu synchronizace:
|
||||
|
||||
```bash
|
||||
npm run check:docs-sync
|
||||
```
|
||||
|
||||
CI také spouští tuto kontrolu v `.github/workflows/ci.yml` (úloha lint).
|
||||
169
docs/i18n/cs/SECURITY.md
Normal file
169
docs/i18n/cs/SECURITY.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Bezpečnostní zásady
|
||||
|
||||
## Hlášení zranitelností
|
||||
|
||||
Pokud v OmniRoute objevíte bezpečnostní zranitelnost, nahlaste ji prosím zodpovědně:
|
||||
|
||||
1. **NEOTVÍREJTE** veřejný problém na GitHubu
|
||||
2. Používejte [bezpečnostní doporučení GitHubu](https://github.com/diegosouzapw/OmniRoute/security/advisories/new)
|
||||
3. Zahrňte: popis, kroky reprodukce a potenciální dopad
|
||||
|
||||
## Časová osa odezvy
|
||||
|
||||
Fáze | Cíl
|
||||
--- | ---
|
||||
Potvrzení | 48 hodin
|
||||
Triáž a posouzení | 5 pracovních dnů
|
||||
Vydání záplaty | 14 pracovních dnů (kritické)
|
||||
|
||||
## Podporované verze
|
||||
|
||||
Verze | Stav podpory
|
||||
--- | ---
|
||||
1.0.x | ✅ Aktivní
|
||||
0.8.x | ✅ Bezpečnost
|
||||
< 0,8,0 | ❌ Nepodporováno
|
||||
|
||||
---
|
||||
|
||||
## Bezpečnostní architektura
|
||||
|
||||
OmniRoute implementuje vícevrstvý bezpečnostní model:
|
||||
|
||||
```
|
||||
Request → CORS → API Key Auth → Prompt Injection Guard → Input Sanitizer → Rate Limiter → Circuit Breaker → Provider
|
||||
```
|
||||
|
||||
### 🔐 Ověřování a autorizace
|
||||
|
||||
Funkce | Implementace
|
||||
--- | ---
|
||||
**Přihlášení do ovládacího panelu** | Ověřování na základě hesla s tokeny JWT (soubory cookie HttpOnly)
|
||||
**Autorizace klíče API** | Klíče podepsané HMAC s ověřením CRC
|
||||
**OAuth 2.0 + PKCE** | Bezpečné ověřování poskytovatelů (Claude, Codex, Gemini, Cursor atd.)
|
||||
**Obnovení tokenu** | Automatická aktualizace tokenu OAuth před vypršením platnosti
|
||||
**Bezpečné soubory cookie** | `AUTH_COOKIE_SECURE=true` pro prostředí HTTPS
|
||||
|
||||
### 🛡️ Šifrování v klidovém stavu
|
||||
|
||||
Všechna citlivá data uložená v SQLite jsou šifrována pomocí **AES-256-GCM** s odvozením klíče scrypt:
|
||||
|
||||
- Klíče API, přístupové tokeny, obnovovací tokeny a ID tokeny
|
||||
- Verzovaný formát: `enc:v1:<iv>:<ciphertext>:<authTag>`
|
||||
- Režim průchodu (prostý text), pokud není nastaven `STORAGE_ENCRYPTION_KEY`
|
||||
|
||||
```bash
|
||||
# Generate encryption key:
|
||||
STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
### 🧠 Ochrana před okamžitou injekcí
|
||||
|
||||
Middleware, který detekuje a blokuje útoky prompt injection v požadavcích LLM:
|
||||
|
||||
Typ vzoru | Závažnost | Příklad
|
||||
--- | --- | ---
|
||||
Přepsání systému | Vysoký | "ignorovat všechny předchozí pokyny"
|
||||
Únos role | Vysoký | "Teď jsi DAN, dokážeš cokoli."
|
||||
Vložení oddělovače | Střední | Kódované oddělovače pro přerušení hranic kontextu
|
||||
DAN/Útěk z vězení | Vysoký | Známé vzory výzev k jailbreaku
|
||||
Únik instrukcí | Střední | „Ukaž mi systémový výzvu“
|
||||
|
||||
Konfigurace přes ovládací panel (Nastavení → Zabezpečení) nebo `.env` :
|
||||
|
||||
```env
|
||||
INPUT_SANITIZER_ENABLED=true
|
||||
INPUT_SANITIZER_MODE=block # warn | block | redact
|
||||
```
|
||||
|
||||
### 🔒 Redakční úprava osobních údajů
|
||||
|
||||
Automatická detekce a volitelná redakce osobních údajů:
|
||||
|
||||
Typ osobních údajů | Vzor | Nahrazení
|
||||
--- | --- | ---
|
||||
E-mail | `user@domain.com` | `[EMAIL_REDACTED]`
|
||||
CPF (Brazílie) | `123.456.789-00` | `[CPF_REDACTED]`
|
||||
CNPJ (Brazílie) | `12.345.678/0001-00` | `[CNPJ_REDACTED]`
|
||||
Kreditní karta | `4111-1111-1111-1111` | `[CC_REDACTED]`
|
||||
Telefon | `+55 11 99999-9999` | `[PHONE_REDACTED]`
|
||||
Číslo sociálního zabezpečení (USA) | `123-45-6789` | `[SSN_REDACTED]`
|
||||
|
||||
```env
|
||||
PII_REDACTION_ENABLED=true
|
||||
```
|
||||
|
||||
### 🌐 Zabezpečení sítě
|
||||
|
||||
Funkce | Popis
|
||||
--- | ---
|
||||
**CORS** | Konfigurovatelná kontrola původu (proměnná prostředí `CORS_ORIGIN` , výchozí nastavení `*` )
|
||||
**Filtrování IP adres** | Rozsahy IP adres na bílou/černou listinu v dashboardu
|
||||
**Omezení rychlosti** | Limity sazeb na poskytovatele s automatickým ukončením
|
||||
**Protihromové stádo** | Mutex + uzamčení pro každé připojení zabraňuje kaskádování 502.
|
||||
|
||||
### 🔌 Odolnost a dostupnost
|
||||
|
||||
Funkce | Popis
|
||||
--- | ---
|
||||
**Jistič** | 3 stavy (Zavřeno → Otevřeno → Polootevřeno) na poskytovatele, trvalé uložení v SQLite
|
||||
**Žádost o idempotenci** | 5sekundové okno pro odstranění duplicitních požadavků
|
||||
**Exponenciální odklon** | Automatické opakování s rostoucím zpožděním
|
||||
**Dashboard zdraví** | Monitorování stavu poskytovatele v reálném čase
|
||||
|
||||
### 📋 Dodržování předpisů
|
||||
|
||||
Funkce | Popis
|
||||
--- | ---
|
||||
**Uchovávání protokolů** | Automatické čištění po `LOG_RETENTION_DAYS`
|
||||
**Odhlášení bez ukládání protokolů** | Příznak `noLog` pro každý klíč API zakazuje protokolování požadavků.
|
||||
**Protokol auditu** | Administrativní akce sledované v tabulce `audit_log`
|
||||
|
||||
---
|
||||
|
||||
## Požadované proměnné prostředí
|
||||
|
||||
Všechny tajné kódy musí být nastaveny před spuštěním serveru. Server **rychle selže** , pokud chybí nebo jsou slabé.
|
||||
|
||||
```bash
|
||||
# REQUIRED — server will not start without these:
|
||||
JWT_SECRET=$(openssl rand -base64 48) # min 32 chars
|
||||
API_KEY_SECRET=$(openssl rand -hex 32) # min 16 chars
|
||||
|
||||
# RECOMMENDED — enables encryption at rest:
|
||||
STORAGE_ENCRYPTION_KEY=$(openssl rand -hex 32)
|
||||
```
|
||||
|
||||
Server aktivně odmítá známé slabé hodnoty, jako například `changeme` , `secret` nebo `password` .
|
||||
|
||||
---
|
||||
|
||||
## Zabezpečení Dockeru
|
||||
|
||||
- Použití uživatele bez oprávnění root v produkčním prostředí
|
||||
- Připojte tajné kódy jako svazky jen pro čtení
|
||||
- Nikdy nekopírujte soubory `.env` do imagí Dockeru
|
||||
- Použití `.dockerignore` k vyloučení citlivých souborů
|
||||
- Nastavit `AUTH_COOKIE_SECURE=true` při připojení za HTTPS
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name omniroute \
|
||||
--restart unless-stopped \
|
||||
--read-only \
|
||||
-p 20128:20128 \
|
||||
-v omniroute-data:/app/data \
|
||||
-e JWT_SECRET="$(openssl rand -base64 48)" \
|
||||
-e API_KEY_SECRET="$(openssl rand -hex 32)" \
|
||||
-e STORAGE_ENCRYPTION_KEY="$(openssl rand -hex 32)" \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Závislosti
|
||||
|
||||
- Pravidelně spouštějte `npm audit`
|
||||
- Udržujte závislosti aktualizované
|
||||
- Projekt používá pro kontroly před commitem `husky` + `lint-staged`
|
||||
- CI pipeline spouští bezpečnostní pravidla ESLint při každém odeslání.
|
||||
254
docs/i18n/cs/TROUBLESHOOTING.md
Normal file
254
docs/i18n/cs/TROUBLESHOOTING.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# Odstraňování problémů
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](TROUBLESHOOTING.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](i18n/es/TROUBLESHOOTING.md) | 🇫🇷 [Français](i18n/fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](i18n/it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](i18n/ru/TROUBLESHOOTING.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](i18n/de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](i18n/in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](i18n/th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](i18n/uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](i18n/ar/TROUBLESHOOTING.md) | 🇯🇵[日本語](i18n/ja/TROUBLESHOOTING.md)| 🇻🇳 [Tiếng Việt](i18n/vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](i18n/bg/TROUBLESHOOTING.md) | 🇩🇰 [Dánsko](i18n/da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](i18n/fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](i18n/he/TROUBLESHOOTING.md) | 🇭🇺 [maďarština](i18n/hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](i18n/ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/TROUBLESHOOTING.md) | 🇳🇱 [Nizozemsko](i18n/nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](i18n/no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](i18n/ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](i18n/pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](i18n/sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](i18n/sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipínec](i18n/phi/TROUBLESHOOTING.md) | 🇨🇿 [Čeština](i18n/cs/TROUBLESHOOTING.md)
|
||||
|
||||
Běžné problémy a řešení pro OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Rychlé opravy
|
||||
|
||||
Problém | Řešení
|
||||
--- | ---
|
||||
První přihlášení nefunguje | Nastavit `INITIAL_PASSWORD` v `.env` (bez pevně zakódovaného výchozího nastavení)
|
||||
Dashboard se otevírá na nesprávném portu | Nastavte `PORT=20128` a `NEXT_PUBLIC_BASE_URL=http://localhost:20128`
|
||||
Žádné protokoly požadavků v sekci `logs/` | Nastavte `ENABLE_REQUEST_LOGS=true`
|
||||
PŘÍSTUP: povolení zamítnuto | Nastavením `DATA_DIR=/path/to/writable/dir` přepíšete `~/.omniroute`
|
||||
Strategie směrování se neukládá | Aktualizace na v1.4.11+ (oprava schématu Zod pro perzistenci nastavení)
|
||||
|
||||
---
|
||||
|
||||
## Problémy s poskytovateli
|
||||
|
||||
### "Jazykový model neposkytoval zprávy"
|
||||
|
||||
**Příčina:** Vyčerpání kvóty poskytovatele.
|
||||
|
||||
**Opravit:**
|
||||
|
||||
1. Zkontrolujte sledovač kvót na řídicím panelu
|
||||
2. Použijte kombinaci se záložními úrovněmi
|
||||
3. Přepnout na levnější/bezplatnou úroveň
|
||||
|
||||
### Omezení rychlosti
|
||||
|
||||
**Příčina:** Vyčerpání kvóty předplatného.
|
||||
|
||||
**Opravit:**
|
||||
|
||||
- Přidat záložní variantu: `cc/claude-opus-4-6 → glm/glm-4.7 → if/kimi-k2-thinking`
|
||||
- Použijte GLM/MiniMax jako levnou zálohu
|
||||
|
||||
### Platnost tokenu OAuth vypršela
|
||||
|
||||
OmniRoute automaticky obnovuje tokeny. Pokud problémy přetrvávají:
|
||||
|
||||
1. Ovládací panel → Poskytovatel → Znovu připojit
|
||||
2. Odstranění a opětovné přidání připojení poskytovatele
|
||||
|
||||
---
|
||||
|
||||
## Problémy s cloudem
|
||||
|
||||
### Chyby synchronizace s cloudem
|
||||
|
||||
1. Ověřte, zda `BASE_URL` odkazuje na vaši spuštěnou instanci (např. `http://localhost:20128` )
|
||||
2. Ověřte, zda `CLOUD_URL` odkazuje na váš cloudový koncový bod (např. `https://omniroute.dev` ).
|
||||
3. Udržujte hodnoty `NEXT_PUBLIC_*` zarovnané s hodnotami na straně serveru.
|
||||
|
||||
### Cloud `stream=false` Vrací 500
|
||||
|
||||
**Příznak:** `Unexpected token 'd'...` na cloudovém koncovém bodu pro nestreamovaná volání.
|
||||
|
||||
**Příčina:** Upstream vrací datovou část SSE, zatímco klient očekává JSON.
|
||||
|
||||
**Řešení:** Pro přímá volání z cloudu použijte `stream=true` . Lokální běhové prostředí zahrnuje záložní SSE→JSON.
|
||||
|
||||
### Cloud hlásí připojení, ale „neplatný klíč API“.
|
||||
|
||||
1. Vytvořte nový klíč z lokálního dashboardu ( `/api/keys` )
|
||||
2. Spuštění synchronizace s cloudem: Povolit cloud → Synchronizovat nyní
|
||||
3. Staré/nesynchronizované klíče mohou v cloudu stále vracet `401`
|
||||
|
||||
---
|
||||
|
||||
## Problémy s Dockerem
|
||||
|
||||
### Nástroj CLI se zobrazuje jako nenainstalovaný
|
||||
|
||||
1. Zkontrolujte běhová pole: `curl http://localhost:20128/api/cli-tools/runtime/codex | jq`
|
||||
2. Pro přenosný režim: použijte cílový soubor image `runner-cli` (dodávané CLI)
|
||||
3. Pro režim připojení hostitele: nastavte `CLI_EXTRA_PATHS` a připojte adresář hostitele bin jako pouze pro čtení.
|
||||
4. Pokud `installed=true` a `runnable=false` : binární soubor byl nalezen, ale kontrola stavu selhala.
|
||||
|
||||
### Rychlé ověření za běhu
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:20128/api/cli-tools/codex-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/claude-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
curl -s http://localhost:20128/api/cli-tools/openclaw-settings | jq '{installed,runnable,commandPath,runtimeMode,reason}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Problémy s náklady
|
||||
|
||||
### Vysoké náklady
|
||||
|
||||
1. Zkontrolujte statistiky využití v sekci Nástěnka → Využití
|
||||
2. Přepnout primární model na GLM/MiniMax
|
||||
3. Pro nekritické úlohy použijte bezplatnou úroveň (Gemini CLI, iFlow).
|
||||
4. Nastavení rozpočtů nákladů pro každý klíč API: Dashboard → API klíče → Rozpočet
|
||||
|
||||
---
|
||||
|
||||
## Ladění
|
||||
|
||||
### Povolit protokoly požadavků
|
||||
|
||||
V souboru `.env` nastavte `ENABLE_REQUEST_LOGS=true` . Protokoly se zobrazují v adresáři `logs/` .
|
||||
|
||||
### Zkontrolujte stav poskytovatele
|
||||
|
||||
```bash
|
||||
# Health dashboard
|
||||
http://localhost:20128/dashboard/health
|
||||
|
||||
# API health check
|
||||
curl http://localhost:20128/api/monitoring/health
|
||||
```
|
||||
|
||||
### Runtimové úložiště
|
||||
|
||||
- Hlavní stav: `${DATA_DIR}/storage.sqlite` (poskytovatelé, kombinace, aliasy, klíče, nastavení)
|
||||
- Použití: SQLite tabulky v `storage.sqlite` ( `usage_history` , `call_logs` , `proxy_logs` ) + volitelné `${DATA_DIR}/log.txt` a `${DATA_DIR}/call_logs/`
|
||||
- Záznamy požadavků: `<repo>/logs/...` (pokud `ENABLE_REQUEST_LOGS=true` )
|
||||
|
||||
---
|
||||
|
||||
## Problémy s jističi
|
||||
|
||||
### Poskytovatel uvízl ve stavu OPEN (OTEVŘENO)
|
||||
|
||||
Pokud je jistič poskytovatele VYPNUTÝ, požadavky jsou blokovány, dokud neuplyne doba ochlazování.
|
||||
|
||||
**Opravit:**
|
||||
|
||||
1. Přejděte do **nabídky Ovládací panel → Nastavení → Odolnost**
|
||||
2. Zkontrolujte kartu jističe u dotčeného poskytovatele
|
||||
3. Kliknutím na **Obnovit vše** vynulujete všechny jističe nebo počkejte, až vyprší doba zpoždění.
|
||||
4. Před resetováním ověřte, zda je poskytovatel skutečně dostupný.
|
||||
|
||||
### Poskytovatel neustále vypíná jistič
|
||||
|
||||
Pokud poskytovatel opakovaně přechází do stavu OTEVŘENO:
|
||||
|
||||
1. Zkontrolujte **v části Dashboard → Stav → Stav poskytovatele** vzorec selhání.
|
||||
2. Přejděte do **Nastavení → Odolnost → Profily poskytovatelů** a zvyšte prahovou hodnotu selhání.
|
||||
3. Zkontrolujte, zda poskytovatel změnil limity API nebo vyžaduje opětovné ověření.
|
||||
4. Zkontrolujte telemetrii latence – vysoká latence může způsobit selhání z důvodu časového limitu.
|
||||
|
||||
---
|
||||
|
||||
## Problémy s přepisem zvuku
|
||||
|
||||
### Chyba „Nepodporovaný model“
|
||||
|
||||
- Ujistěte se, že používáte správný prefix: `deepgram/nova-3` nebo `assemblyai/best`
|
||||
- Ověřte, zda je poskytovatel připojen v **nabídce Dashboard → Poskytovatelé.**
|
||||
|
||||
### Přepis vrací prázdný výsledek nebo selže
|
||||
|
||||
- Zkontrolujte podporované zvukové formáty: `mp3` , `wav` , `m4a` , `flac` , `ogg` , `webm`
|
||||
- Ověřte, zda je velikost souboru v rámci limitů poskytovatele (obvykle < 25 MB)
|
||||
- Zkontrolujte platnost klíče API poskytovatele v kartě poskytovatele
|
||||
|
||||
---
|
||||
|
||||
## Ladění překladače
|
||||
|
||||
Pro ladění problémů s překladem formátu použijte **Dashboard → Translator** :
|
||||
|
||||
Režim | Kdy použít
|
||||
--- | ---
|
||||
**Dětské hřiště** | Porovnejte vstupní/výstupní formáty vedle sebe – vložte neúspěšný požadavek a podívejte se, jak se přeloží
|
||||
**Tester chatu** | Odesílejte živé zprávy a kontrolujte kompletní datovou část požadavků/odpovědí včetně záhlaví
|
||||
**Zkušební stolice** | Spusťte dávkové testy napříč kombinacemi formátů a zjistěte, které překlady jsou poškozené.
|
||||
**Živý monitor** | Sledujte tok požadavků v reálném čase a zachyťte občasné problémy s překladem
|
||||
|
||||
### Běžné problémy s formátováním
|
||||
|
||||
- **Štítky myšlení se nezobrazují** – Zkontrolujte, zda cílový poskytovatel podporuje myšlení a nastavení rozpočtu myšlení.
|
||||
- **Volání nástrojů se vynechávají** – Některé překlady formátů mohou odstranit nepodporovaná pole; ověřte v režimu Playground.
|
||||
- **Chybí systémová výzva** – Claude a Gemini zpracovávají systémové výzvy odlišně; zkontrolujte překlad výstupu
|
||||
- **SDK vrací nezpracovaný řetězec místo objektu** – Opraveno ve verzi 1.1.0: sanitizér odpovědí nyní odstraňuje nestandardní pole ( `x_groq` , `usage_breakdown` atd.), která způsobují selhání validace OpenAI SDK v Pydantic.
|
||||
- **GLM/ERNIE odmítá `system` roli** — Opraveno ve verzi 1.1.0: normalizátor rolí automaticky slučoval systémové zprávy s uživatelskými zprávami pro nekompatibilní modely.
|
||||
- **role `developer` nebyla rozpoznána** – Opraveno ve verzi 1.1.0: automaticky převedeno na `system` pro poskytovatele, kteří nepoužívají OpenAI
|
||||
- **`json_schema` nefunguje s Gemini** — Opraveno ve verzi 1.1.0: `response_format` se nyní převádí na `responseMimeType` + `responseSchema` z Gemini.
|
||||
|
||||
---
|
||||
|
||||
## Nastavení odolnosti
|
||||
|
||||
### Automatické omezení rychlosti se nespouští
|
||||
|
||||
- Automatické omezení rychlosti se vztahuje pouze na poskytovatele klíčů API (ne na OAuth/předplatné)
|
||||
- Ověřte **Nastavení → Odolnost → Profily poskytovatelů** mají povoleno automatické omezení rychlosti
|
||||
- Zkontrolujte, zda poskytovatel vrací stavové kódy `429` nebo hlavičky `Retry-After`
|
||||
|
||||
### Ladění exponenciálního poklesu
|
||||
|
||||
Profily poskytovatelů podporují tato nastavení:
|
||||
|
||||
- **Základní zpoždění** — Počáteční doba čekání po prvním selhání (výchozí: 1 s)
|
||||
- **Max. zpoždění** — Maximální doba čekání (výchozí: 30 s)
|
||||
- **Násobitel** — O kolik se má zvýšit zpoždění za každou po sobě jdoucí chybu (výchozí: 2x)
|
||||
|
||||
### Stádo proti hromům
|
||||
|
||||
Když se na poskytovatele s omezenou rychlostí odesílá mnoho souběžných požadavků, OmniRoute použije mutex + automatické omezení rychlosti k serializaci požadavků a zabránění kaskádovým selháním. Toto je automatické pro poskytovatele klíčů API.
|
||||
|
||||
---
|
||||
|
||||
## Volitelná taxonomie selhání RAG / LLM (16 problémů)
|
||||
|
||||
Někteří uživatelé OmniRoute umisťují bránu před RAG nebo agent stacky. V těchto nastaveních je běžné vidět zvláštní vzorec: OmniRoute vypadá v pořádku (poskytovatelé aktivní, profily směrování v pořádku, žádná upozornění na limity rychlosti), ale konečná odpověď je stále nesprávná.
|
||||
|
||||
V praxi tyto incidenty obvykle pocházejí z následného RAG kanálu, nikoli ze samotné brány.
|
||||
|
||||
Pokud chcete sdílenou slovní zásobu pro popis těchto selhání, můžete použít WFGY ProblemMap, externí textový zdroj s licencí MIT, který definuje šestnáct opakujících se vzorců selhání RAG / LLM. Na obecné úrovni zahrnuje:
|
||||
|
||||
- drift vyhledávání a narušené hranice kontextu
|
||||
- prázdné nebo zastaralé indexy a vektorové úložiště
|
||||
- vkládání versus sémantický nesoulad
|
||||
- problémy s assembly promptu a kontextovým oknem
|
||||
- logický kolaps a přehnaně sebevědomé odpovědi
|
||||
- selhání dlouhého řetězce a koordinace agentů
|
||||
- paměť více agentů a posun rolí
|
||||
- problémy s nasazením a objednáváním bootstrapů
|
||||
|
||||
Myšlenka je jednoduchá:
|
||||
|
||||
1. Při vyšetřování špatné odpovědi zaznamenejte:
|
||||
- úkol a požadavek uživatele
|
||||
- Kombinace trasy nebo poskytovatele v OmniRoute
|
||||
- jakýkoli kontext RAG použitý v následných fázích (načtené dokumenty, volání nástrojů atd.)
|
||||
2. Namapujte incident na jedno nebo dvě čísla z WFGY ProblemMap ( `No.1` … `No.16` ).
|
||||
3. Uložte číslo do vlastního řídicího panelu, runbooku nebo sledovače incidentů vedle protokolů OmniRoute.
|
||||
4. Pro rozhodnutí, zda je potřeba změnit RAG stack, retriever nebo směrovací strategii, použijte odpovídající stránku WFGY.
|
||||
|
||||
Plný text a konkrétní recepty naleznete zde (licence MIT, pouze text):
|
||||
|
||||
[Soubor README pro mapu problémů WFGY](https://github.com/onestardao/WFGY/blob/main/ProblemMap/README.md)
|
||||
|
||||
Tuto část můžete ignorovat, pokud za OmniRoute nespouštěte RAG ani agenty.
|
||||
|
||||
---
|
||||
|
||||
## Stále v koncích?
|
||||
|
||||
- **Problémy s GitHubem** : [github.com/diegosouzapw/OmniRoute/issues](https://github.com/diegosouzapw/OmniRoute/issues)
|
||||
- **Architektura** : Viz [`docs/ARCHITECTURE.md`](ARCHITECTURE.md) pro interní podrobnosti
|
||||
- **Referenční informace k API** : Všechny koncové body naleznete v [`docs/API_REFERENCE.md`](API_REFERENCE.md)
|
||||
- **Panel stavu** : Zkontrolujte **Panel stavu, kde** najdete stav systému v reálném čase.
|
||||
- **Překladač** : Použijte **Dashboard → Překladač** k ladění problémů s formátem
|
||||
814
docs/i18n/cs/USER_GUIDE.md
Normal file
814
docs/i18n/cs/USER_GUIDE.md
Normal file
@@ -0,0 +1,814 @@
|
||||
# Uživatelská příručka
|
||||
|
||||
🌐 **Jazyky:** 🇺🇸 [angličtina](USER_GUIDE.md) | 🇧🇷 [Português (Brazílie)](i18n/pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](i18n/es/USER_GUIDE.md) | 🇫🇷 [Français](i18n/fr/USER_GUIDE.md) | 🇮🇹 [Italiano](i18n/it/USER_GUIDE.md) | 🇷🇺 [Русский](i18n/ru/USER_GUIDE.md) | 🇨🇳[中文 (简体)](i18n/zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](i18n/de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](i18n/in/USER_GUIDE.md) | 🇹🇭 [ไทย](i18n/th/USER_GUIDE.md) | 🇺🇦 [Українська](i18n/uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](i18n/ar/USER_GUIDE.md) | 🇯🇵[日本語](i18n/ja/USER_GUIDE.md)| 🇻🇳 [Tiếng Việt](i18n/vi/USER_GUIDE.md) | 🇧🇬 [Български](i18n/bg/USER_GUIDE.md) | 🇩🇰 [Dánsko](i18n/da/USER_GUIDE.md) | 🇫🇮 [Suomi](i18n/fi/USER_GUIDE.md) | 🇮🇱 [עברית](i18n/he/USER_GUIDE.md) | 🇭🇺 [maďarština](i18n/hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonésie](i18n/id/USER_GUIDE.md) | 🇰🇷 [한국어](i18n/ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](i18n/ms/USER_GUIDE.md) | 🇳🇱 [Nizozemsko](i18n/nl/USER_GUIDE.md) | 🇳🇴 [Norsk](i18n/no/USER_GUIDE.md) | 🇵🇹 [Português (Portugalsko)](i18n/pt/USER_GUIDE.md) | 🇷🇴 [Română](i18n/ro/USER_GUIDE.md) | 🇵🇱 [Polski](i18n/pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](i18n/sk/USER_GUIDE.md) | 🇸🇪 [Svenska](i18n/sv/USER_GUIDE.md) | 🇵🇭 [Filipínec](i18n/phi/USER_GUIDE.md) | 🇨🇿 [Čeština](i18n/cs/USER_GUIDE.md)
|
||||
|
||||
Kompletní průvodce konfigurací poskytovatelů, vytvářením kombinací, integrací nástrojů CLI a nasazením OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Obsah
|
||||
|
||||
- [Ceny v kostce](#-pricing-at-a-glance)
|
||||
- [Případy použití](#-use-cases)
|
||||
- [Nastavení poskytovatele](#-provider-setup)
|
||||
- [Integrace s rozhraním CLI](#-cli-integration)
|
||||
- [Nasazení](#-deployment)
|
||||
- [Dostupné modely](#-available-models)
|
||||
- [Pokročilé funkce](#-advanced-features)
|
||||
|
||||
---
|
||||
|
||||
## 💰 Přehled cen
|
||||
|
||||
Úroveň | Poskytovatel | Náklady | Obnovení kvóty | Nejlepší pro
|
||||
--- | --- | --- | --- | ---
|
||||
**💳 PŘEDPLATNÉ** | Claude Code (profesionál) | 20 dolarů měsíčně | 5 hodin + týdně | Již přihlášen/a k odběru
|
||||
| Kodex (Plus/Pro) | 20–200 USD/měsíc | 5 hodin + týdně | Uživatelé OpenAI
|
||||
| Rozhraní příkazového řádku Gemini | **UVOLNIT** | 180 tisíc měsíčně + 1 tisíc denně | Každý!
|
||||
| GitHub Copilot | 10–19 USD/měsíc | Měsíční | Uživatelé GitHubu
|
||||
**🔑 KLÍČ API** | Hluboké vyhledávání | Platba za použití | Žádný | Laciné uvažování
|
||||
| Groq | Platba za použití | Žádný | Ultrarychlá inference
|
||||
| xAI (Grok) | Platba za použití | Žádný | Grok 4 uvažování
|
||||
| Mistral | Platba za použití | Žádný | Modely hostované v EU
|
||||
| Zmatek | Platba za použití | Žádný | Rozšířené vyhledávání
|
||||
| Společně s umělou inteligencí | Platba za použití | Žádný | Modely s otevřeným zdrojovým kódem
|
||||
| Ohňostroj s umělou inteligencí | Platba za použití | Žádný | Rychlé snímky FLUX
|
||||
| Mozky | Platba za použití | Žádný | Rychlost v měřítku destičky
|
||||
| Soudržný | Platba za použití | Žádný | Příkaz R+ RAG
|
||||
| NVIDIA NIM | Platba za použití | Žádný | Podnikové modely
|
||||
**💰 LEVNÉ** | GLM-4.7 | 0,6 USD/1 milion | Denně v 10:00 | Záloha rozpočtu
|
||||
| MiniMax M2.1 | 0,2 USD/1 milion | 5hodinové válcování | Nejlevnější varianta
|
||||
| Kimi K2 | 9 dolarů měsíčně bez závazků | 10 milionů tokenů/měsíc | Předvídatelné náklady
|
||||
**🆓 ZDARMA** | iFlow | 0 dolarů | Neomezený | 8 modelů zdarma
|
||||
| Qwen | 0 dolarů | Neomezený | 3 modely zdarma
|
||||
| Kiro | 0 dolarů | Neomezený | Claude zdarma
|
||||
|
||||
**💡 Tip pro profesionály:** Začněte s kombinací Gemini CLI (180 tisíc zdarma/měsíc) + iFlow (neomezeně zdarma) = 0 dolarů!
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Případy použití
|
||||
|
||||
### Případ 1: „Mám předplatné Claude Pro“
|
||||
|
||||
**Problém:** Kvóta vyprší, nevyužitá, limity rychlosti během náročného kódování
|
||||
|
||||
```
|
||||
Combo: "maximize-claude"
|
||||
1. cc/claude-opus-4-6 (use subscription fully)
|
||||
2. glm/glm-4.7 (cheap backup when quota out)
|
||||
3. if/kimi-k2-thinking (free emergency fallback)
|
||||
|
||||
Monthly cost: $20 (subscription) + ~$5 (backup) = $25 total
|
||||
vs. $20 + hitting limits = frustration
|
||||
```
|
||||
|
||||
### Případ 2: „Chci nulové náklady“
|
||||
|
||||
**Problém:** Nemůžu si dovolit předplatné, potřebuji spolehlivé kódování s využitím umělé inteligence
|
||||
|
||||
```
|
||||
Combo: "free-forever"
|
||||
1. gc/gemini-3-flash (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited free)
|
||||
3. qw/qwen3-coder-plus (unlimited free)
|
||||
|
||||
Monthly cost: $0
|
||||
Quality: Production-ready models
|
||||
```
|
||||
|
||||
### Případ 3: „Potřebuji kódování 24 hodin denně, 7 dní v týdnu, bez přerušení“
|
||||
|
||||
**Problém:** Termíny, nemůžeme si dovolit prostoje
|
||||
|
||||
```
|
||||
Combo: "always-on"
|
||||
1. cc/claude-opus-4-6 (best quality)
|
||||
2. cx/gpt-5.2-codex (second subscription)
|
||||
3. glm/glm-4.7 (cheap, resets daily)
|
||||
4. minimax/MiniMax-M2.1 (cheapest, 5h reset)
|
||||
5. if/kimi-k2-thinking (free unlimited)
|
||||
|
||||
Result: 5 layers of fallback = zero downtime
|
||||
Monthly cost: $20-200 (subscriptions) + $10-20 (backup)
|
||||
```
|
||||
|
||||
### Případ 4: „Chci BEZPLATNOU AI v OpenClaw“
|
||||
|
||||
**Problém:** Potřebujete asistenta s umělou inteligencí v aplikacích pro zasílání zpráv, zcela zdarma
|
||||
|
||||
```
|
||||
Combo: "openclaw-free"
|
||||
1. if/glm-4.7 (unlimited free)
|
||||
2. if/minimax-m2.1 (unlimited free)
|
||||
3. if/kimi-k2-thinking (unlimited free)
|
||||
|
||||
Monthly cost: $0
|
||||
Access via: WhatsApp, Telegram, Slack, Discord, iMessage, Signal...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📖 Nastavení poskytovatele
|
||||
|
||||
### 🔐 Poskytovatelé předplatného
|
||||
|
||||
#### Claude Code (Pro/Max)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Claude Code
|
||||
→ OAuth login → Auto token refresh
|
||||
→ 5-hour + weekly quota tracking
|
||||
|
||||
Models:
|
||||
cc/claude-opus-4-6
|
||||
cc/claude-sonnet-4-5-20250929
|
||||
cc/claude-haiku-4-5-20251001
|
||||
```
|
||||
|
||||
**Tip pro profesionály:** Pro složité úkoly používejte Opus, pro rychlost Sonnet. OmniRoute sleduje kvótu pro každý model!
|
||||
|
||||
#### OpenAI Codex (Plus/Pro)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Codex
|
||||
→ OAuth login (port 1455)
|
||||
→ 5-hour + weekly reset
|
||||
|
||||
Models:
|
||||
cx/gpt-5.2-codex
|
||||
cx/gpt-5.1-codex-max
|
||||
```
|
||||
|
||||
#### Gemini CLI (ZDARMA 180 000/měsíc!)
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect Gemini CLI
|
||||
→ Google OAuth
|
||||
→ 180K completions/month + 1K/day
|
||||
|
||||
Models:
|
||||
gc/gemini-3-flash-preview
|
||||
gc/gemini-2.5-pro
|
||||
```
|
||||
|
||||
**Nejlepší hodnota:** Obrovská bezplatná úroveň! Použijte ji před placenými úrovněmi.
|
||||
|
||||
#### GitHub Copilot
|
||||
|
||||
```bash
|
||||
Dashboard → Providers → Connect GitHub
|
||||
→ OAuth via GitHub
|
||||
→ Monthly reset (1st of month)
|
||||
|
||||
Models:
|
||||
gh/gpt-5
|
||||
gh/claude-4.5-sonnet
|
||||
gh/gemini-3-pro
|
||||
```
|
||||
|
||||
### 💰 Levní poskytovatelé
|
||||
|
||||
#### GLM-4.7 (Denní reset, 0,6 USD/1 milion)
|
||||
|
||||
1. Registrace: [Zhipu AI](https://open.bigmodel.cn/)
|
||||
2. Získejte klíč API z kódovacího plánu
|
||||
3. Nástěnka → Přidat klíč API: Poskytovatel: `glm` , klíč API: `your-key`
|
||||
|
||||
**Použití:** `glm/glm-4.7` — **Tip pro profesionály:** Coding Plan nabízí 3× kvótu za cenu 1/7! Resetovat denně v 10:00.
|
||||
|
||||
#### MiniMax M2.1 (5h reset, 0,20 $/1 milion)
|
||||
|
||||
1. Registrace: [MiniMax](https://www.minimax.io/)
|
||||
2. Získat API klíč → Dashboard → Přidat API klíč
|
||||
|
||||
**Použití:** `minimax/MiniMax-M2.1` — **Tip pro profesionály:** Nejlevnější varianta pro dlouhý kontext (1 milion tokenů)!
|
||||
|
||||
#### Kimi K2 (paušální poplatek 9 dolarů měsíčně)
|
||||
|
||||
1. Odebírat: [Moonshot AI](https://platform.moonshot.ai/)
|
||||
2. Získat API klíč → Dashboard → Přidat API klíč
|
||||
|
||||
**Použití:** `kimi/kimi-latest` — **Tip pro profesionály:** Fixní cena 9 $/měsíc za 10 milionů tokenů = efektivní náklady 0,90 $/1 milion!
|
||||
|
||||
### 🆓 Poskytovatelé ZDARMA
|
||||
|
||||
#### iFlow (8 modelů ZDARMA)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect iFlow → OAuth login → Unlimited usage
|
||||
|
||||
Models: if/kimi-k2-thinking, if/qwen3-coder-plus, if/glm-4.7, if/minimax-m2, if/deepseek-r1
|
||||
```
|
||||
|
||||
#### Qwen (3 modely ZDARMA)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Qwen → Device code auth → Unlimited usage
|
||||
|
||||
Models: qw/qwen3-coder-plus, qw/qwen3-coder-flash
|
||||
```
|
||||
|
||||
#### Kiro (Claude ZDARMA)
|
||||
|
||||
```bash
|
||||
Dashboard → Connect Kiro → AWS Builder ID or Google/GitHub → Unlimited
|
||||
|
||||
Models: kr/claude-sonnet-4.5, kr/claude-haiku-4.5
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Kombinace
|
||||
|
||||
### Příklad 1: Maximalizace předplatného → Levné zálohování
|
||||
|
||||
```
|
||||
Dashboard → Combos → Create New
|
||||
|
||||
Name: premium-coding
|
||||
Models:
|
||||
1. cc/claude-opus-4-6 (Subscription primary)
|
||||
2. glm/glm-4.7 (Cheap backup, $0.6/1M)
|
||||
3. minimax/MiniMax-M2.1 (Cheapest fallback, $0.20/1M)
|
||||
|
||||
Use in CLI: premium-coding
|
||||
```
|
||||
|
||||
### Příklad 2: Pouze zdarma (nulové náklady)
|
||||
|
||||
```
|
||||
Name: free-combo
|
||||
Models:
|
||||
1. gc/gemini-3-flash-preview (180K free/month)
|
||||
2. if/kimi-k2-thinking (unlimited)
|
||||
3. qw/qwen3-coder-plus (unlimited)
|
||||
|
||||
Cost: $0 forever!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Integrace s rozhraním příkazového řádku
|
||||
|
||||
### IDE kurzoru
|
||||
|
||||
```
|
||||
Settings → Models → Advanced:
|
||||
OpenAI API Base URL: http://localhost:20128/v1
|
||||
OpenAI API Key: [from omniroute dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
### Claude Code
|
||||
|
||||
Upravit `~/.claude/config.json` :
|
||||
|
||||
```json
|
||||
{
|
||||
"anthropic_api_base": "http://localhost:20128/v1",
|
||||
"anthropic_api_key": "your-omniroute-api-key"
|
||||
}
|
||||
```
|
||||
|
||||
### CLI Codexu
|
||||
|
||||
```bash
|
||||
export OPENAI_BASE_URL="http://localhost:20128"
|
||||
export OPENAI_API_KEY="your-omniroute-api-key"
|
||||
codex "your prompt"
|
||||
```
|
||||
|
||||
### OpenClaw
|
||||
|
||||
Upravit `~/.openclaw/openclaw.json` :
|
||||
|
||||
```json
|
||||
{
|
||||
"agents": {
|
||||
"defaults": {
|
||||
"model": { "primary": "omniroute/if/glm-4.7" }
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"providers": {
|
||||
"omniroute": {
|
||||
"baseUrl": "http://localhost:20128/v1",
|
||||
"apiKey": "your-omniroute-api-key",
|
||||
"api": "openai-completions",
|
||||
"models": [{ "id": "if/glm-4.7", "name": "glm-4.7" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Nebo použijte Dashboard:** CLI Tools → OpenClaw → Auto-config
|
||||
|
||||
### Cline / Pokračovat / RooCode
|
||||
|
||||
```
|
||||
Provider: OpenAI Compatible
|
||||
Base URL: http://localhost:20128/v1
|
||||
API Key: [from dashboard]
|
||||
Model: cc/claude-opus-4-6
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Nasazení
|
||||
|
||||
### Globální instalace npm (doporučeno)
|
||||
|
||||
```bash
|
||||
npm install -g omniroute
|
||||
|
||||
# Create config directory
|
||||
mkdir -p ~/.omniroute
|
||||
|
||||
# Create .env file (see .env.example)
|
||||
cp .env.example ~/.omniroute/.env
|
||||
|
||||
# Start server
|
||||
omniroute
|
||||
# Or with custom port:
|
||||
omniroute --port 3000
|
||||
```
|
||||
|
||||
Rozhraní příkazového řádku automaticky načte `.env` z adresáře `~/.omniroute/.env` nebo `./.env` .
|
||||
|
||||
### Nasazení VPS
|
||||
|
||||
```bash
|
||||
git clone https://github.com/diegosouzapw/OmniRoute.git
|
||||
cd OmniRoute && npm install && npm run build
|
||||
|
||||
export JWT_SECRET="your-secure-secret-change-this"
|
||||
export INITIAL_PASSWORD="your-password"
|
||||
export DATA_DIR="/var/lib/omniroute"
|
||||
export PORT="20128"
|
||||
export HOSTNAME="0.0.0.0"
|
||||
export NODE_ENV="production"
|
||||
export NEXT_PUBLIC_BASE_URL="http://localhost:20128"
|
||||
export API_KEY_SECRET="endpoint-proxy-api-key-secret"
|
||||
|
||||
npm run start
|
||||
# Or: pm2 start npm --name omniroute -- start
|
||||
```
|
||||
|
||||
### Nasazení PM2 (málo paměti)
|
||||
|
||||
Pro servery s omezenou pamětí RAM použijte možnost omezení paměti:
|
||||
|
||||
```bash
|
||||
# With 512MB limit (default)
|
||||
pm2 start npm --name omniroute -- start
|
||||
|
||||
# Or with custom memory limit
|
||||
OMNIROUTE_MEMORY_MB=512 pm2 start npm --name omniroute -- start
|
||||
|
||||
# Or using ecosystem.config.js
|
||||
pm2 start ecosystem.config.js
|
||||
```
|
||||
|
||||
Vytvořte soubor `ecosystem.config.js` :
|
||||
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "omniroute",
|
||||
script: "npm",
|
||||
args: "start",
|
||||
env: {
|
||||
NODE_ENV: "production",
|
||||
OMNIROUTE_MEMORY_MB: "512",
|
||||
JWT_SECRET: "your-secret",
|
||||
INITIAL_PASSWORD: "your-password",
|
||||
},
|
||||
node_args: "--max-old-space-size=512",
|
||||
max_memory_restart: "300M",
|
||||
},
|
||||
],
|
||||
};
|
||||
```
|
||||
|
||||
### Přístavní dělník
|
||||
|
||||
```bash
|
||||
# Build image (default = runner-cli with codex/claude/droid preinstalled)
|
||||
docker build -t omniroute:cli .
|
||||
|
||||
# Portable mode (recommended)
|
||||
docker run -d --name omniroute -p 20128:20128 --env-file ./.env -v omniroute-data:/app/data omniroute:cli
|
||||
```
|
||||
|
||||
Informace o režimu integrovaném s hostitelem s binárními soubory CLI naleznete v části Docker v hlavní dokumentaci.
|
||||
|
||||
### Proměnné prostředí
|
||||
|
||||
Proměnná | Výchozí | Popis
|
||||
--- | --- | ---
|
||||
`JWT_SECRET` | `omniroute-default-secret-change-me` | Tajný klíč podpisu JWT ( **změna v produkčním prostředí** )
|
||||
`INITIAL_PASSWORD` | `123456` | První přihlašovací heslo
|
||||
`DATA_DIR` | `~/.omniroute` | Datový adresář (db, využití, protokoly)
|
||||
`PORT` | výchozí nastavení rámce | Servisní port ( `20128` v příkladech)
|
||||
`HOSTNAME` | výchozí nastavení rámce | Vázat hostitele (Docker má výchozí hodnotu `0.0.0.0` )
|
||||
`NODE_ENV` | výchozí nastavení za běhu | Nastavení `production` pro nasazení
|
||||
`BASE_URL` | `http://localhost:20128` | Interní základní URL na straně serveru
|
||||
`CLOUD_URL` | `https://omniroute.dev` | Základní adresa URL koncového bodu synchronizace s cloudem
|
||||
`API_KEY_SECRET` | `endpoint-proxy-api-key-secret` | Tajný klíč HMAC pro generované klíče API
|
||||
`REQUIRE_API_KEY` | `false` | Vynutit klíč rozhraní Bearer API na `/v1/*`
|
||||
`ENABLE_REQUEST_LOGS` | `false` | Povoluje protokolování požadavků/odpovědí
|
||||
`AUTH_COOKIE_SECURE` | `false` | Vynutit soubor cookie `Secure` ověřování (za reverzní proxy HTTPS)
|
||||
`OMNIROUTE_MEMORY_MB` | `512` | Limit haldy Node.js v MB
|
||||
`PROMPT_CACHE_MAX_SIZE` | `50` | Maximální počet položek mezipaměti výzev
|
||||
`SEMANTIC_CACHE_MAX_SIZE` | `100` | Maximální počet položek sémantické mezipaměti
|
||||
|
||||
Úplný přehled proměnných prostředí naleznete v souboru [README](../README.md) .
|
||||
|
||||
---
|
||||
|
||||
## 📊 Dostupné modely
|
||||
|
||||
<details>
|
||||
<summary><b>Zobrazit všechny dostupné modely</b></summary>
|
||||
</details>
|
||||
|
||||
**Claude Code ( `cc/` )** — Pro/Max: `cc/claude-opus-4-6` , `cc/claude-sonnet-4-5-20250929` , `cc/claude-haiku-4-5-20251001`
|
||||
|
||||
**Codex ( `cx/` )** — Plus/Pro: `cx/gpt-5.2-codex` , `cx/gpt-5.1-codex-max`
|
||||
|
||||
**Rozhraní příkazového řádku Gemini ( `gc/` )** — ZDARMA: `gc/gemini-3-flash-preview` , `gc/gemini-2.5-pro`
|
||||
|
||||
**GitHub Copilot ( `gh/` )** : `gh/gpt-5` , `gh/claude-4.5-sonnet`
|
||||
|
||||
**GLM ( `glm/` )** — 0,6 USD/1 milion: `glm/glm-4.7`
|
||||
|
||||
**MiniMax ( `minimax/` )** — 0,2 USD/1 milion: `minimax/MiniMax-M2.1`
|
||||
|
||||
**iFlow ( `if/` )** — ZDARMA: `if/kimi-k2-thinking` , `if/qwen3-coder-plus` , `if/deepseek-r1`
|
||||
|
||||
**Qwen ( `qw/` )** — ZDARMA: `qw/qwen3-coder-plus` , `qw/qwen3-coder-flash`
|
||||
|
||||
**Kiro ( `kr/` )** — ZDARMA: `kr/claude-sonnet-4.5` , `kr/claude-haiku-4.5`
|
||||
|
||||
**DeepSeek ( `ds/` )** : `ds/deepseek-chat` , `ds/deepseek-reasoner`
|
||||
|
||||
**Groq ( `groq/` )** : `groq/llama-3.3-70b-versatile` , `groq/llama-4-maverick-17b-128e-instruct`
|
||||
|
||||
**xAI ( `xai/` )** : `xai/grok-4` , `xai/grok-4-0709-fast-reasoning` , `xai/grok-code-mini`
|
||||
|
||||
**Mistral ( `mistral/` )** : `mistral/mistral-large-2501` , `mistral/codestral-2501`
|
||||
|
||||
**Zmatek ( `pplx/` )** : `pplx/sonar-pro` , `pplx/sonar`
|
||||
|
||||
**Společně AI ( `together/` )** : `together/meta-llama/Llama-3.3-70B-Instruct-Turbo`
|
||||
|
||||
**Umělá inteligence pro ohňostroje ( `fireworks/` )** : `fireworks/accounts/fireworks/models/deepseek-v3p1`
|
||||
|
||||
**Cerebras ( `cerebras/` )** : `cerebras/llama-3.3-70b`
|
||||
|
||||
**Soudržnost ( `cohere/` )** : `cohere/command-r-plus-08-2024`
|
||||
|
||||
**NVIDIA NIM ( `nvidia/` )** : `nvidia/nvidia/llama-3.3-70b-instruct`
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 🧩 Pokročilé funkce
|
||||
|
||||
### Vlastní modely
|
||||
|
||||
Přidejte libovolné ID modelu k libovolnému poskytovateli bez čekání na aktualizaci aplikace:
|
||||
|
||||
```bash
|
||||
# Via API
|
||||
curl -X POST http://localhost:20128/api/provider-models \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"provider": "openai", "modelId": "gpt-4.5-preview", "modelName": "GPT-4.5 Preview"}'
|
||||
|
||||
# List: curl http://localhost:20128/api/provider-models?provider=openai
|
||||
# Remove: curl -X DELETE "http://localhost:20128/api/provider-models?provider=openai&model=gpt-4.5-preview"
|
||||
```
|
||||
|
||||
Nebo použijte Dashboard: **Poskytovatelé → [Poskytovatel] → Vlastní modely** .
|
||||
|
||||
### Vyhrazené trasy poskytovatelů
|
||||
|
||||
Směrování požadavků přímo ke konkrétnímu poskytovateli s validací modelu:
|
||||
|
||||
```bash
|
||||
POST http://localhost:20128/v1/providers/openai/chat/completions
|
||||
POST http://localhost:20128/v1/providers/openai/embeddings
|
||||
POST http://localhost:20128/v1/providers/fireworks/images/generations
|
||||
```
|
||||
|
||||
Pokud chybí prefix poskytovatele, automaticky se přidá. Neshodné modely vrátí chybu `400` .
|
||||
|
||||
### Konfigurace síťového proxy serveru
|
||||
|
||||
```bash
|
||||
# Set global proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"global": {"type":"http","host":"proxy.example.com","port":"8080"}}'
|
||||
|
||||
# Per-provider proxy
|
||||
curl -X PUT http://localhost:20128/api/settings/proxy \
|
||||
-d '{"providers": {"openai": {"type":"socks5","host":"proxy.example.com","port":"1080"}}}'
|
||||
|
||||
# Test proxy
|
||||
curl -X POST http://localhost:20128/api/settings/proxy/test \
|
||||
-d '{"proxy":{"type":"socks5","host":"proxy.example.com","port":"1080"}}'
|
||||
```
|
||||
|
||||
**Priorita:** Specifická pro klíč → Specifická pro kombinaci → Specifická pro poskytovatele → Globální → Prostředí.
|
||||
|
||||
### API katalogu modelů
|
||||
|
||||
```bash
|
||||
curl http://localhost:20128/api/models/catalog
|
||||
```
|
||||
|
||||
Vrátí modely seskupené podle poskytovatele s typy ( `chat` , `embedding` , `image` ).
|
||||
|
||||
### Synchronizace s cloudem
|
||||
|
||||
- Synchronizace poskytovatelů, kombinací a nastavení napříč zařízeními
|
||||
- Automatická synchronizace na pozadí s časovým limitem + rychlá ochrana proti selhání
|
||||
- V produkčním prostředí preferovat `BASE_URL` / `CLOUD_URL` na straně serveru
|
||||
|
||||
### LLM Gateway Intelligence (fáze 9)
|
||||
|
||||
- **Sémantická mezipaměť** — Automaticky ukládá do mezipaměti nestreamované odpovědi s teplotou 0 (obejde se pomocí `X-OmniRoute-No-Cache: true` )
|
||||
- **Request Idempotency** — Deduplikuje požadavky do 5 sekund pomocí hlavičky `Idempotency-Key` nebo `X-Request-Id`
|
||||
- **Sledování průběhu** — `event: progress` prostřednictvím záhlaví `X-OmniRoute-Progress: true`
|
||||
|
||||
---
|
||||
|
||||
### Hřiště překladatelů
|
||||
|
||||
Přístup přes **Dashboard → Translator** . Ladění a vizualizace toho, jak OmniRoute překládá požadavky API mezi poskytovateli.
|
||||
|
||||
Režim | Účel
|
||||
--- | ---
|
||||
**Dětské hřiště** | Vyberte zdrojový/cílový formát, vložte požadavek a okamžitě si prohlédněte přeložený výstup
|
||||
**Tester chatu** | Odesílejte zprávy živého chatu přes proxy a kontrolujte celý cyklus požadavku/odpovědi
|
||||
**Zkušební stolice** | Spusťte dávkové testy napříč různými kombinacemi formátů pro ověření správnosti překladu
|
||||
**Živý monitor** | Sledujte překlady v reálném čase, jak požadavky procházejí proxy serverem
|
||||
|
||||
**Případy použití:**
|
||||
|
||||
- Ladění, proč selhává určitá kombinace klienta/poskytovatele
|
||||
- Ověřte, zda se tagy myšlení, volání nástrojů a systémové výzvy správně překládají.
|
||||
- Porovnejte rozdíly ve formátech OpenAI, Claude, Gemini a Responses API
|
||||
|
||||
---
|
||||
|
||||
### Strategie směrování
|
||||
|
||||
Konfigurace přes **Dashboard → Nastavení → Routing** .
|
||||
|
||||
Strategie | Popis
|
||||
--- | ---
|
||||
**Nejprve vyplňte** | Používá účty podle priority – primární účet zpracovává všechny požadavky, dokud není k dispozici.
|
||||
**Round Robin** | Cykluje mezi všemi účty s nastavitelným trvalým limitem (výchozí: 3 volání na účet)
|
||||
**P2C (Síla dvou možností)** | Vybere 2 náhodné účty a nasměruje je k tomu zdravějšímu – vyvažuje zátěž s povědomím o zdraví
|
||||
**Náhodný** | Náhodně vybere účet pro každý požadavek pomocí Fisher-Yatesova náhodného výběru.
|
||||
**Nejméně používané** | Směruje k účtu s nejstarším časovým razítkem `lastUsedAt` a rovnoměrně rozděluje provoz.
|
||||
**Optimalizované náklady** | Směruje k účtu s nejnižší prioritou a optimalizuje pro poskytovatele s nejnižšími náklady.
|
||||
|
||||
#### Aliasy zástupných znaků modelů
|
||||
|
||||
Vytvořte zástupné znaky pro přemapování názvů modelů:
|
||||
|
||||
```
|
||||
Pattern: claude-sonnet-* → Target: cc/claude-sonnet-4-5-20250929
|
||||
Pattern: gpt-* → Target: gh/gpt-5.1-codex
|
||||
```
|
||||
|
||||
Zástupné znaky podporují `*` (libovolný znak) a `?` (jeden znak).
|
||||
|
||||
#### Záložní řetězce
|
||||
|
||||
Definujte globální záložní řetězce, které platí pro všechny požadavky:
|
||||
|
||||
```
|
||||
Chain: production-fallback
|
||||
1. cc/claude-opus-4-6
|
||||
2. gh/gpt-5.1-codex
|
||||
3. glm/glm-4.7
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Odolnost a jističe
|
||||
|
||||
Konfigurace přes **Dashboard → Settings → Resilience** .
|
||||
|
||||
OmniRoute implementuje odolnost na úrovni poskytovatele se čtyřmi komponentami:
|
||||
|
||||
1. **Profily poskytovatelů** – Konfigurace pro jednotlivé poskytovatele pro:
|
||||
|
||||
- Práh selhání (počet selhání před otevřením)
|
||||
- Doba zchlazení
|
||||
- Citlivost detekce limitu frekvence
|
||||
- Exponenciální backoff parametry
|
||||
|
||||
2. **Upravitelné limity rychlosti** – Výchozí nastavení na úrovni systému konfigurovatelná na řídicím panelu:
|
||||
|
||||
- **Požadavky za minutu (RPM)** — Maximální počet požadavků za minutu na účet
|
||||
- **Minimální doba mezi požadavky** — Minimální mezera v milisekundách mezi požadavky
|
||||
- **Max. počet souběžných požadavků** — Maximální počet souběžných požadavků na účet
|
||||
- Klikněte na **Upravit** pro úpravu a poté **na Uložit** nebo **Zrušit** . Hodnoty se ukládají prostřednictvím rozhraní API pro odolnost.
|
||||
|
||||
3. **Jistič** – Sleduje poruchy u jednotlivých poskytovatelů a automaticky rozpojuje obvod, když je dosaženo prahové hodnoty:
|
||||
|
||||
- **ZAVŘENO** (v pořádku) – Požadavky probíhají normálně.
|
||||
- **OTEVŘENO** — Poskytovatel je dočasně zablokován po opakovaných selháních
|
||||
- **HALF_OPEN** — Testování, zda se poskytovatel zotavil
|
||||
|
||||
4. **Zásady a uzamčené identifikátory** – Zobrazuje stav jističe a uzamčené identifikátory s možností vynuceného odemčení.
|
||||
|
||||
5. **Automatická detekce limitu rychlosti** – Monitoruje záhlaví `429` a `Retry-After` , aby se proaktivně zabránilo dosažení limitů rychlosti poskytovatele.
|
||||
|
||||
**Tip pro profesionály:** Pomocí tlačítka **Obnovit vše** vymažete všechny jističe a doby ochlazování, když se poskytovatel zotaví z výpadku.
|
||||
|
||||
---
|
||||
|
||||
### Export / import databáze
|
||||
|
||||
Správa záloh databáze se provádí v **nabídce Ovládací panel → Nastavení → Systém a úložiště** .
|
||||
|
||||
Akce | Popis
|
||||
--- | ---
|
||||
**Exportovat databázi** | Stáhne aktuální databázi SQLite jako soubor `.sqlite`
|
||||
**Exportovat vše (.tar.gz)** | Stáhne kompletní zálohu včetně: databáze, nastavení, kombinací, připojení k poskytovatelům (bez přihlašovacích údajů) a metadat klíče API.
|
||||
**Importovat databázi** | Nahrajte soubor `.sqlite` , který nahradí aktuální databázi. Záloha před importem se vytvoří automaticky.
|
||||
|
||||
```bash
|
||||
# API: Export database
|
||||
curl -o backup.sqlite http://localhost:20128/api/db-backups/export
|
||||
|
||||
# API: Export all (full archive)
|
||||
curl -o backup.tar.gz http://localhost:20128/api/db-backups/exportAll
|
||||
|
||||
# API: Import database
|
||||
curl -X POST http://localhost:20128/api/db-backups/import \
|
||||
-F "file=@backup.sqlite"
|
||||
```
|
||||
|
||||
**Ověření importu:** Importovaný soubor je ověřen z hlediska integrity (kontrola pragma SQLite), požadovaných tabulek ( `provider_connections` , `provider_nodes` , `combos` , `api_keys` ) a velikosti (max. 100 MB).
|
||||
|
||||
**Případy použití:**
|
||||
|
||||
- Migrace OmniRoute mezi počítači
|
||||
- Vytvořte externí zálohy pro zotavení po havárii
|
||||
- Sdílení konfigurací mezi členy týmu (exportovat vše → sdílet archiv)
|
||||
|
||||
---
|
||||
|
||||
### Ovládací panel nastavení
|
||||
|
||||
Stránka nastavení je pro snadnou navigaci uspořádána do 5 záložek:
|
||||
|
||||
Záložka | Obsah
|
||||
--- | ---
|
||||
**Zabezpečení** | Nastavení přihlášení/hesla, řízení přístupu k IP adrese, autorizace API pro `/models` a blokování poskytovatelů
|
||||
**Směrování** | Globální strategie směrování (6 možností), aliasy zástupných znaků, záložní řetězce, kombinované výchozí hodnoty
|
||||
**Odolnost** | Profily poskytovatelů, upravitelné limity sazeb, stav jističů, zásady a uzamčené identifikátory
|
||||
**Umělá inteligence** | Konfigurace rozpočtu promyšleného projektu, globální vkládání promptu do systému, statistiky mezipaměti promptu
|
||||
**Moderní** | Globální konfigurace proxy (HTTP/SOCKS5)
|
||||
|
||||
---
|
||||
|
||||
### Správa nákladů a rozpočtu
|
||||
|
||||
Přístup přes **Dashboard → Náklady** .
|
||||
|
||||
Záložka | Účel
|
||||
--- | ---
|
||||
**Rozpočet** | Nastavte limity útrat pro každý klíč API s denními/týdenními/měsíčními rozpočty a sledováním v reálném čase
|
||||
**Ceny** | Zobrazení a úprava cenových položek modelu – cena za 1000 vstupních/výstupních tokenů na poskytovatele
|
||||
|
||||
```bash
|
||||
# API: Set a budget
|
||||
curl -X POST http://localhost:20128/api/usage/budget \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyId": "key-123", "limit": 50.00, "period": "monthly"}'
|
||||
|
||||
# API: Get current budget status
|
||||
curl http://localhost:20128/api/usage/budget
|
||||
```
|
||||
|
||||
**Sledování nákladů:** Každý požadavek zaznamenává využití tokenů a vypočítává náklady pomocí ceníkové tabulky. Rozdělení si můžete prohlédnout v **sekci Dashboard → Využití** podle poskytovatele, modelu a klíče API.
|
||||
|
||||
---
|
||||
|
||||
### Přepis zvuku
|
||||
|
||||
OmniRoute podporuje přepis zvuku prostřednictvím koncového bodu kompatibilního s OpenAI:
|
||||
|
||||
```bash
|
||||
POST /v1/audio/transcriptions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
# Example with curl
|
||||
curl -X POST http://localhost:20128/v1/audio/transcriptions \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-F "file=@audio.mp3" \
|
||||
-F "model=deepgram/nova-3"
|
||||
```
|
||||
|
||||
Dostupní poskytovatelé: **Deepgram** ( `deepgram/` ), **AssemblyAI** ( `assemblyai/` ).
|
||||
|
||||
Podporované zvukové formáty: `mp3` , `wav` , `m4a` , `flac` , `ogg` , `webm` .
|
||||
|
||||
---
|
||||
|
||||
### Strategie kombinovaného vyvažování
|
||||
|
||||
Nastavte vyvažování jednotlivých kombinací v **nabídce Dashboard → Kombinace → Vytvořit/Upravit → Strategie** .
|
||||
|
||||
Strategie | Popis
|
||||
--- | ---
|
||||
**Round-Robin** | Postupně prochází modely
|
||||
**Přednost** | Vždy se pokusí o první model; vrací se pouze v případě chyby.
|
||||
**Náhodný** | Pro každý požadavek vybere náhodný model z komba
|
||||
**Vážené** | Trasy proporcionálně na základě přiřazených vah pro každý model
|
||||
**Nejméně používané** | Směruje k modelu s nejmenším počtem nedávných požadavků (používá kombinované metriky)
|
||||
**Optimalizované z hlediska nákladů** | Trasy k nejlevnějšímu dostupnému modelu (používá ceník)
|
||||
|
||||
Globální výchozí hodnoty kombinací lze nastavit v **nabídce Dashboard → Settings → Routing → Combo Defaults** .
|
||||
|
||||
---
|
||||
|
||||
### Dashboard zdraví
|
||||
|
||||
Přístup přes **Dashboard → Stav** . Přehled stavu systému v reálném čase se 6 kartami:
|
||||
|
||||
Karta | Co to ukazuje
|
||||
--- | ---
|
||||
**Stav systému** | Doba provozuschopnosti, verze, využití paměti, datový adresář
|
||||
**Zdraví poskytovatelů** | Stav jističe podle dodavatele (Zapnuto/Vypnuto/Napůl vypnuto)
|
||||
**Limity sazeb** | Aktivní limit rychlosti cooldownů na účet se zbývajícím časem
|
||||
**Aktivní výluky** | Poskytovatelé dočasně blokovaní politikou uzamčení
|
||||
**Mezipaměť podpisů** | Statistiky mezipaměti pro deduplikaci (aktivní klíče, míra zásahů)
|
||||
**Telemetrie latence** | Agregace latence p50/p95/p99 na poskytovatele
|
||||
|
||||
**Tip pro profesionály:** Stránka Zdraví se automaticky obnovuje každých 10 sekund. Pomocí karty jističe můžete zjistit, kteří poskytovatelé mají problémy.
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Desktopová aplikace (Electron)
|
||||
|
||||
OmniRoute je k dispozici jako nativní desktopová aplikace pro Windows, macOS a Linux.
|
||||
|
||||
### Instalace
|
||||
|
||||
```bash
|
||||
# From the electron directory:
|
||||
cd electron
|
||||
npm install
|
||||
|
||||
# Development mode (connect to running Next.js dev server):
|
||||
npm run dev
|
||||
|
||||
# Production mode (uses standalone build):
|
||||
npm start
|
||||
```
|
||||
|
||||
### Instalatéři budov
|
||||
|
||||
```bash
|
||||
cd electron
|
||||
npm run build # Current platform
|
||||
npm run build:win # Windows (.exe NSIS)
|
||||
npm run build:mac # macOS (.dmg universal)
|
||||
npm run build:linux # Linux (.AppImage)
|
||||
```
|
||||
|
||||
Výstup → `electron/dist-electron/`
|
||||
|
||||
### Klíčové vlastnosti
|
||||
|
||||
Funkce | Popis
|
||||
--- | ---
|
||||
**Připravenost serveru** | Před zobrazením okna se dotazuje server (žádná prázdná obrazovka)
|
||||
**Systémový zásobník** | Minimalizovat do zásobníku, změnit port, ukončit menu v zásobníku
|
||||
**Správa přístavů** | Změna portu serveru z panelu úloh (automatické restartování serveru)
|
||||
**Zásady zabezpečení obsahu** | Omezující CSP prostřednictvím záhlaví relace
|
||||
**Jedna instance** | V daném okamžiku může běžet pouze jedna instance aplikace
|
||||
**Offline režim** | Dodávaný server Next.js funguje bez internetu
|
||||
|
||||
### Proměnné prostředí
|
||||
|
||||
Proměnná | Výchozí | Popis
|
||||
--- | --- | ---
|
||||
`OMNIROUTE_PORT` | `20128` | Port serveru
|
||||
`OMNIROUTE_MEMORY_MB` | `512` | Limit haldy Node.js (64–16384 MB)
|
||||
|
||||
📖 Úplná dokumentace: [`electron/README.md`](../electron/README.md)
|
||||
399
docs/i18n/cs/VM_DEPLOYMENT_GUIDE.md
Normal file
399
docs/i18n/cs/VM_DEPLOYMENT_GUIDE.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
|
||||
Kompletní instalace a konfigurace OmniRoute u VM (VPS) s gerenciou přes Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
## Předpoklady
|
||||
|
||||
Položka | Mínimo | Doporučeno
|
||||
--- | --- | ---
|
||||
**Procesor** | 1 virtuální procesor | 2 vCPU
|
||||
**BERAN** | 1 GB | 2 GB
|
||||
**Disko** | 10GB SSD | 25GB SSD
|
||||
**TAK** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS
|
||||
**Domínio** | Registrován v Cloudflare | —
|
||||
**Přístavní dělník** | Docker Engine 24+ | Docker 27+
|
||||
|
||||
**Testados poskytovatelů** : Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## 1. Konfigurace virtuálního počítače
|
||||
|
||||
### 1.1 Vytvořit ihned
|
||||
|
||||
Žádný preferovaný poskytovatel seu VPS:
|
||||
|
||||
- Vyberte si Ubuntu 24.04 LTS
|
||||
- Výběr nebo plano minimo (1 vCPU / 1 GB RAM)
|
||||
- Definujte sílu pro root nebo konfiguraci klíče SSH
|
||||
- Anote o **IP público** (např.: `203.0.113.10` )
|
||||
|
||||
### 1.2 Připojení přes SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Aktualizace systému
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalace Dockeru
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalace nginxu
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Konfigurace firewallu (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
ufw default allow outgoing
|
||||
ufw allow 22/tcp # SSH
|
||||
ufw allow 80/tcp # HTTP (redirect)
|
||||
ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica** : Maximální zabezpečení, omezení jako porty 80 a 443 přístupů pro IP Cloudflare. Veja a seção [Segurança Avançada](#seguran%C3%A7a-avan%C3%A7ada) .
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalace OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
NODE_ENV=production
|
||||
HOSTNAME=0.0.0.0
|
||||
DATA_DIR=/app/data
|
||||
STORAGE_DRIVER=sqlite
|
||||
ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **DŮLEŽITÉ** : Gere chaves secretas únicas! Použijte `openssl rand -hex 32` para cada chave.
|
||||
|
||||
### 2.3 Spuštění kontejneru
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
|
||||
docker run -d \
|
||||
--name omniroute \
|
||||
--restart unless-stopped \
|
||||
--env-file /opt/omniroute/.env \
|
||||
-p 20128:20128 \
|
||||
-v omniroute-data:/app/data \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Vývojový příklad: `[DB] SQLite database ready` a `listening on port 20128` .
|
||||
|
||||
---
|
||||
|
||||
## 3. Konfigurace nginx (reverzní proxy)
|
||||
|
||||
### 3.1 Gerar Certificado SSL (Cloudflare Origin)
|
||||
|
||||
Cloudflare nic neřeší:
|
||||
|
||||
1. Používá **SSL/TLS → Origin Server**
|
||||
2. **Certifikát Clique Create**
|
||||
3. Deixe os padrões (15 ano, *.seudominio.com)
|
||||
4. Zkopírujte nebo zkopírujte **certifikát původu** a **soukromý klíč**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Konfigurace nginxu
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
listen 443 ssl default_server;
|
||||
listen [::]:443 ssl default_server;
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
server_name _;
|
||||
return 444;
|
||||
}
|
||||
|
||||
# OmniRoute — HTTPS
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
ssl_protocols TLSv1.2 TLSv1.3;
|
||||
|
||||
client_max_body_size 100M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:20128;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
proxy_cache off;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTP → HTTPS redirect
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar a testování
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Konfigurace DNS v Cloudflare
|
||||
|
||||
### 4.1 Další DNS registr
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
|
||||
Typ | Jméno | Obsah | Proxy
|
||||
--- | --- | --- | ---
|
||||
A | `llms` | `203.0.113.10` (IP adresa virtuálního počítače) | ✅ Proxy
|
||||
|
||||
### 4.2 Konfigurace SSL
|
||||
|
||||
Em **SSL/TLS → Přehled** :
|
||||
|
||||
- Režim: **Plný (Přísný)**
|
||||
|
||||
Em **SSL/TLS → Edge certifikáty** :
|
||||
|
||||
- Vždy používat HTTPS: ✅ Zapnuto
|
||||
- Minimální verze TLS: TLS 1.2
|
||||
- Automatické přepisování HTTPS: ✅ Zapnuto
|
||||
|
||||
### 4.3 Testar
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operace a údržba
|
||||
|
||||
### Aktualizovat na novou verzi
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
docker stop omniroute && docker rm omniroute
|
||||
docker run -d --name omniroute --restart unless-stopped \
|
||||
--env-file /opt/omniroute/.env \
|
||||
-p 20128:20128 \
|
||||
-v omniroute-data:/app/data \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Verzovní protokoly
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
```
|
||||
|
||||
### Ruční zálohování banky
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Obnovení zálohy
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Pokročilá bezpečnost
|
||||
|
||||
### Omezte přístup k IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Přidat `nginx.conf` dentro do bloco `http {}` :
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Nainstalujte fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear accesso direto na port do Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Nasazení cloudového pracovníka (volitelné)
|
||||
|
||||
Vzdálený přístup přes Cloudflare Workers (zde exponovat diretament VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Dokumenty jsou kompletní pro [omnirouteCloud/README.md](../omnirouteCloud/README.md) .
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
|
||||
Porta | Služba | Přístup
|
||||
--- | --- | ---
|
||||
22 | SSH | Veřejné (s fail2ban)
|
||||
80 | nginx HTTP | Přesměrování → HTTPS
|
||||
443 | nginx HTTPS | Prostřednictvím proxy serveru Cloudflare
|
||||
20128 | OmniRoute | Někdy na localhostu (přes nginx)
|
||||
45
docs/i18n/cs/adr/0001-proxy-registry-limit-generalization.md
Normal file
45
docs/i18n/cs/adr/0001-proxy-registry-limit-generalization.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# ADR-0001: Zobecnění registru proxy serverů + kontroly využití
|
||||
|
||||
Datum: 17. 3. 2026 Stav: Přijato
|
||||
|
||||
## Kontext
|
||||
|
||||
OmniRoute je užitečný:
|
||||
|
||||
- Přiřazení proxy na základě konfigurační mapy ( `global` , `providers` , `combos` , `keys` ).
|
||||
- Výběr s ohledem na kvóty poskytovatele khusus tertentu (zejména `codex` ).
|
||||
|
||||
Mezera utama:
|
||||
|
||||
- Proxy belum menjadi asset opakovaně použitelný jang bisa di-manage sebagai entitas (metadata, kde se používají, bezpečné smazání).
|
||||
- Zásady použití belum konsisten lintas provider.
|
||||
- Chybová smlouva API belum seragam untuk manajemen endpoint manajemen.
|
||||
|
||||
## Rozhodnutí
|
||||
|
||||
1. Tambah **Proxy Registry** sebegai domény baru di DB ( `proxy_registry` , `proxy_assignments` ).
|
||||
2. Stálá kompatibilita přiřazení lama (záložní lama `proxyConfig` ).
|
||||
3. Priority pakai runtime modulu Resolver:
|
||||
- účet -> poskytovatel -> globální (registr)
|
||||
- záložní ke legacy resolver jika registry belum ada přiřazení
|
||||
4. Výchozí registr výstupního seznamu Wajib redaction kredensial di.
|
||||
5. Standarkan error JSON unuk endpoint manajemen proxy agar konsisten dan punya `requestId` .
|
||||
|
||||
## Důsledky
|
||||
|
||||
Pozitivní:
|
||||
|
||||
- Opakovaně použitelný proxy server.
|
||||
- Bezpečné odstranění bisa ditegakkan (409 saat masih dipakai).
|
||||
- Migrasi bertahap tanpa prolomení runtime změn.
|
||||
|
||||
Negativní:
|
||||
|
||||
- Ada dual-source sementara (registr + starší konfigurace) sampai migrasi selesai.
|
||||
- Ale přiřazení koncových bodů tambahan a pemetaan rozsah a rozsah.
|
||||
|
||||
## Následná opatření
|
||||
|
||||
- Poskytovatel uživatelského rozhraní Migrasi/účet umožňuje zadat nezpracovaný registr selektoru proxy serveru.
|
||||
- Telemetrie zdraví Tambah na proxy a upozornění.
|
||||
- Všeobecná kontrola používání ke poskytovateli lain melalui interface policy yang sama.
|
||||
@@ -0,0 +1,31 @@
|
||||
# ADR-0002: Chybová smlouva pro koncové body správy
|
||||
|
||||
Datum: 17. 3. 2026 Stav: Přijato
|
||||
|
||||
## Rozhodnutí
|
||||
|
||||
Koncové body správy (konfigurace proxy, registr proxy a přiřazení proxy) vracejí jednotné tělo chyby:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"message": "Human-readable summary",
|
||||
"type": "invalid_request | not_found | conflict | server_error",
|
||||
"details": {}
|
||||
},
|
||||
"requestId": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
## Mapování stavu
|
||||
|
||||
- 400: neplatný požadavek / selhání ověření
|
||||
- 404: zdroj nenalezen
|
||||
- 409: konflikt zdrojů (například proxy stále přiřazen)
|
||||
- 500: neočekávaná chyba serveru
|
||||
|
||||
## Poznámky
|
||||
|
||||
- `requestId` je povinný pro korelaci protokolů.
|
||||
- `details` je volitelné a používá se pouze pro bezpečné ověření detailů.
|
||||
- Citlivé tajné informace (přihlašovací údaje proxy, tokeny) se nikdy nesmí objevit ve `message` ani v `details` .
|
||||
15
docs/i18n/cs/adr/0003-security-checklist-proxy-limits.md
Normal file
15
docs/i18n/cs/adr/0003-security-checklist-proxy-limits.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# ADR-0003: Kontrolní seznam zabezpečení pro registr proxy a kontroly používání
|
||||
|
||||
Datum: 17. 3. 2026 Stav: Přijato
|
||||
|
||||
## Kontrolní seznam
|
||||
|
||||
- Ověřte všechny datové části správy pomocí Zodu.
|
||||
- Odmítnout aktualizace chybně formátovaného přiřazení rozsahu se stavem 400.
|
||||
- Odmítnout smazání používané proxy se stavem 409, pokud to není vynuceno.
|
||||
- Ve výchozím nastavení nikdy nezobrazovat uživatelské jméno/heslo proxy v odpovědích seznamu.
|
||||
- Nikdy nezaznamenávejte nezpracované přihlašovací údaje ani hodnoty tokenů.
|
||||
- Udržujte chybové odpovědi bez interních trasování zásobníku.
|
||||
- Chraňte koncové body správy pomocí stávajících zásad middlewaru pro ověřování.
|
||||
- Auditovat mutující operace: vytvořit/aktualizovat/smazat/přiřadit/migraci.
|
||||
- Zajistěte, aby se resolver během přechodu vrátil k původní konfiguraci.
|
||||
254
docs/i18n/cs/electron/README.md
Normal file
254
docs/i18n/cs/electron/README.md
Normal file
@@ -0,0 +1,254 @@
|
||||
# Aplikace OmniRoute Electron pro stolní počítače
|
||||
|
||||
Tento adresář obsahuje obalovou aplikaci Electron pro desktopovou aplikaci OmniRoute.
|
||||
|
||||
## Architektura (v1.6.4)
|
||||
|
||||
```
|
||||
electron/
|
||||
├── main.js # Main process — window, tray, server lifecycle, CSP, IPC
|
||||
├── preload.js # Preload script — secure IPC bridge with disposer pattern
|
||||
├── package.json # Electron-specific dependencies & electron-builder config
|
||||
├── types.d.ts # TypeScript definitions (AppInfo, ServerStatus, ElectronAPI)
|
||||
└── assets/ # Application icons and resources
|
||||
|
||||
src/shared/hooks/
|
||||
└── useElectron.ts # React hooks — useSyncExternalStore, zero re-renders
|
||||
```
|
||||
|
||||
## Klíčová rozhodnutí o designu
|
||||
|
||||
Rozhodnutí | Odůvodnění
|
||||
--- | ---
|
||||
dotazování `waitForServer()` | Zabraňuje zobrazení prázdné obrazovky při studeném startu — před načtením se ozve `http://localhost:PORT`
|
||||
`stdio: 'pipe'` | Zachycuje stdout/stderr serveru pro logování + detekci připravenosti ( `inherit` )
|
||||
Vzor drtiče odpadu | `onServerStatus()` vrací `() => void` pro přesné vyčištění listeneru (ne `removeAllListeners` )
|
||||
`useSyncExternalStore` | Nulové renderování pro `useIsElectron()` — žádný cyklus `useState` + `useEffect`
|
||||
CSP prostřednictvím záhlaví relace | `Content-Security-Policy` omezuje `script-src` , `connect-src` atd. dle osvědčených postupů Electron.
|
||||
Podmíněný titulek pro platformu | `titleBarStyle: 'hiddenInset'` pouze v systému macOS; `default` ve Windows/Linuxu
|
||||
|
||||
## Rozvoj
|
||||
|
||||
### Předpoklady
|
||||
|
||||
1. Nejprve sestavte aplikaci Next.js:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
1. Instalace závislostí Electronu:
|
||||
|
||||
```bash
|
||||
cd electron
|
||||
npm install
|
||||
```
|
||||
|
||||
### Spuštěno ve vývoji
|
||||
|
||||
1. Spusťte vývojový server Next.js:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
1. V jiném terminálu spusťte Electron:
|
||||
|
||||
```bash
|
||||
cd electron
|
||||
npm run dev
|
||||
```
|
||||
|
||||
### Spuštění v produkčním režimu
|
||||
|
||||
1. Sestavení Next.js v samostatném režimu:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
1. Spuštění elektronu:
|
||||
|
||||
```bash
|
||||
cd electron
|
||||
npm start
|
||||
```
|
||||
|
||||
## Budova
|
||||
|
||||
### Sestavení pro aktuální platformu
|
||||
|
||||
```bash
|
||||
cd electron
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Vytvořte pro specifické platformy
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
npm run build:win
|
||||
|
||||
# macOS (x64 + arm64)
|
||||
npm run build:mac
|
||||
|
||||
# Linux
|
||||
npm run build:linux
|
||||
```
|
||||
|
||||
## Výstup
|
||||
|
||||
Vytvořené aplikace jsou umístěny v `dist-electron/` :
|
||||
|
||||
- Windows: `.exe` instalační program (NSIS) + přenosný `.exe`
|
||||
- macOS: instalační soubor `.dmg` (Intel + Apple Silicon)
|
||||
- Linux: `.AppImage`
|
||||
|
||||
## Instalace
|
||||
|
||||
### macOS
|
||||
|
||||
1. Stáhněte si nejnovější soubor `.dmg` ze stránky [Verze](https://github.com/diegosouzapw/OmniRoute/releases) .
|
||||
2. Otevřete soubor `.dmg` .
|
||||
3. Přetáhněte `OmniRoute.app` do složky Aplikace.
|
||||
4. Spustit z Aplikací.
|
||||
|
||||
> ⚠️ **Poznámka:** Aplikace zatím není podepsána certifikátem Apple Developer. Pokud macOS aplikaci blokuje, spusťte:
|
||||
>
|
||||
> ```bash
|
||||
> xattr -cr /Applications/OmniRoute.app
|
||||
> ```
|
||||
>
|
||||
> Nebo klikněte pravým tlačítkem myši na aplikaci → Otevřít → Otevřít (pro obejití Gatekeeperu při prvním spuštění).
|
||||
|
||||
### Windows
|
||||
|
||||
**Instalační program (doporučeno):**
|
||||
|
||||
1. Stáhněte si `OmniRoute.Setup.*.exe` z [Releases](https://github.com/diegosouzapw/OmniRoute/releases) .
|
||||
2. Spusťte instalační program.
|
||||
3. Spuštění z nabídky Start nebo zástupce na ploše.
|
||||
|
||||
**Přenosné (bez instalace):**
|
||||
|
||||
1. Stáhněte si soubor `OmniRoute.exe` ze [sekce Vydání](https://github.com/diegosouzapw/OmniRoute/releases) .
|
||||
2. Spouštět přímo z libovolné složky.
|
||||
|
||||
### Linux
|
||||
|
||||
1. Stáhněte si soubor `.AppImage` ze [sekce Releases](https://github.com/diegosouzapw/OmniRoute/releases) .
|
||||
2. Udělejte z něj spustitelný soubor:
|
||||
```bash
|
||||
chmod +x OmniRoute-*.AppImage
|
||||
```
|
||||
3. Běh:
|
||||
```bash
|
||||
./OmniRoute-*.AppImage
|
||||
```
|
||||
|
||||
## Funkce
|
||||
|
||||
- **Připravenost serveru** – Před zobrazením okna čeká na kontrolu stavu
|
||||
- **Systémový zásobník** — Minimalizace do systémového zásobníku s rychlými akcemi (otevřít, změnit port, ukončit)
|
||||
- **Správa portů** — Změna portu z nabídky v systémové liště (server se automaticky restartuje)
|
||||
- **Ovládací prvky oken** — Vlastní minimalizace, maximalizace, zavření přes IPC
|
||||
- **Zásady zabezpečení obsahu** – Omezující CSP prostřednictvím záhlaví relací
|
||||
- **Offline podpora** — Samostatný server Next.js v balíčku
|
||||
- **Jedna instance** – V daném okamžiku může běžet pouze jedna instance aplikace.
|
||||
|
||||
## Konfigurace
|
||||
|
||||
### Proměnné prostředí
|
||||
|
||||
Proměnná | Výchozí | Popis
|
||||
--- | --- | ---
|
||||
`OMNIROUTE_PORT` | `20128` | Port serveru
|
||||
`OMNIROUTE_MEMORY_MB` | `512` | Limit haldy Node.js (64–16384 MB)
|
||||
`NODE_ENV` | `production` | Nastavit na `development` pro vývojářský režim
|
||||
|
||||
### Vlastní ikona
|
||||
|
||||
Umístěte ikony do `assets/` :
|
||||
|
||||
- `icon.ico` — ikona Windows (256×256)
|
||||
- `icon.icns` — balíček ikon pro macOS
|
||||
- `icon.png` — Linux/obecné použití (512×512)
|
||||
- `tray-icon.png` — Ikona na systémové liště (16×16 nebo 32×32)
|
||||
|
||||
## Kanály IPC
|
||||
|
||||
### Vyvolání (Renderer → Hlavní, asynchronní)
|
||||
|
||||
Kanál | Vrácení zboží | Popis
|
||||
--- | --- | ---
|
||||
`get-app-info` | `AppInfo` | Název aplikace, verze, platforma, isDev, port
|
||||
`open-external` | `void` | Otevřít URL ve výchozím prohlížeči (pouze http/https)
|
||||
`get-data-dir` | `string` | Získat cestu k adresáři userData
|
||||
`restart-server` | `{ success }` | Zastavení + restart serveru (časový limit 5 s + SIGKILL)
|
||||
|
||||
### Odeslat (Renderer → Hlavní, spustit a zapomenout)
|
||||
|
||||
Kanál | Popis
|
||||
--- | ---
|
||||
`window-minimize` | Minimalizovat okno
|
||||
`window-maximize` | Přepnout maximalizaci/obnovení
|
||||
`window-close` | Zavřít okno (minimalizovat do zásobníku)
|
||||
|
||||
### Příjem (Hlavní → Renderer, události)
|
||||
|
||||
Kanál | Užitečné zatížení | Vydáno, když
|
||||
--- | --- | ---
|
||||
`server-status` | `ServerStatus` | Server se spouští, zastavuje, dochází k chybám nebo se restartuje
|
||||
`port-changed` | `number` | Změna portu přes menu zásobníku
|
||||
|
||||
> **Poznámka** : Posluchače vracejí funkce pro přesné čištění. Viz hooky `useServerStatus` a `usePortChanged` .
|
||||
|
||||
## Zabezpečení
|
||||
|
||||
Funkce | Implementace
|
||||
--- | ---
|
||||
Izolace kontextu | `contextIsolation: true` — renderer nemůže přistupovat k Node.js
|
||||
Integrace uzlů | `nodeIntegration: false` — v rendereru není `require()`
|
||||
Bílý seznam IPC | Názvy kanálů ověřené při předběžném načítání pomocí `safeInvoke` / `safeSend` / `safeOn`
|
||||
Ověření URL adresy | `shell.openExternal()` povoluje pouze protokoly `http:` / `https:`
|
||||
CSP | Záhlaví `Content-Security-Policy` nastavené pomocí `session.webRequest.onHeadersReceived`
|
||||
Zabezpečení webu | `webSecurity: true` – vynucena politika stejného původu
|
||||
|
||||
## React Hooky
|
||||
|
||||
Háček | Vrácení zboží | Popis
|
||||
--- | --- | ---
|
||||
`useIsElectron()` | `boolean` | Detekce nulového renderování pomocí `useSyncExternalStore`
|
||||
`useElectronAppInfo()` | `{ appInfo, loading, error }` | Informace o aplikaci z hlavního procesu
|
||||
`useDataDir()` | `{ dataDir, loading, error }` | Adresář uživatelských dat
|
||||
`useWindowControls()` | `{ minimize, maximize, close }` | Akce ovládání oken
|
||||
`useOpenExternal()` | `{ openExternal }` | Otevřít URL adresy v prohlížeči
|
||||
`useServerControls()` | `{ restart, restarting }` | Řízení restartu serveru
|
||||
`useServerStatus(cb)` | Drtič odpadu | Poslouchejte události stavu serveru
|
||||
`usePortChanged(cb)` | Drtič odpadu | Poslouchejte události změny portu
|
||||
|
||||
## Odstraňování problémů
|
||||
|
||||
### Aplikace se nespustí
|
||||
|
||||
1. Zkontrolujte, zda je port 20128 dostupný: `lsof -i :20128`
|
||||
2. Zkontrolujte protokoly konzole pro prefix `[Electron]`
|
||||
3. Ověřte, zda výstup sestavení existuje v souboru `.next/standalone`
|
||||
|
||||
### Bílá obrazovka
|
||||
|
||||
1. Ověření existence buildu Next.js – čekání na připravenost serveru maximálně 30 sekund
|
||||
2. Zkontrolujte výstup protokolů `[Server]` a `[Server:err]`
|
||||
3. Hledání porušení CSP v konzoli pro vývojáře
|
||||
|
||||
### Selhání sestavení
|
||||
|
||||
Ujistěte se, že máte nainstalované nástroje pro sestavení:
|
||||
|
||||
- Windows: Nástroje pro sestavení ve Visual Studiu
|
||||
- macOS: Nástroje příkazového řádku Xcode
|
||||
- Linux: `build-essential` , `libsecret-1-dev`
|
||||
|
||||
## Licence
|
||||
|
||||
MIT
|
||||
164
docs/i18n/cs/i18n/README.html
Normal file
164
docs/i18n/cs/i18n/README.html
Normal file
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Vícejazyčná dokumentace</title>
|
||||
<style>
|
||||
/* From extension vscode.github */
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.vscode-dark img[src$=\#gh-light-mode-only],
|
||||
.vscode-light img[src$=\#gh-dark-mode-only],
|
||||
.vscode-high-contrast:not(.vscode-high-contrast-light) img[src$=\#gh-light-mode-only],
|
||||
.vscode-high-contrast-light img[src$=\#gh-dark-mode-only] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/markdown.css">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Microsoft/vscode/extensions/markdown-language-features/media/highlight.css">
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.task-list-item-checkbox {
|
||||
margin-left: -20px;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
:root {
|
||||
--color-note: #0969da;
|
||||
--color-tip: #1a7f37;
|
||||
--color-warning: #9a6700;
|
||||
--color-severe: #bc4c00;
|
||||
--color-caution: #d1242f;
|
||||
--color-important: #8250df;
|
||||
}
|
||||
|
||||
</style>
|
||||
<style>
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--color-note: #2f81f7;
|
||||
--color-tip: #3fb950;
|
||||
--color-warning: #d29922;
|
||||
--color-severe: #db6d28;
|
||||
--color-caution: #f85149;
|
||||
--color-important: #a371f7;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
<style>
|
||||
.markdown-alert {
|
||||
padding: 0.5rem 1rem;
|
||||
margin-bottom: 16px;
|
||||
color: inherit;
|
||||
border-left: .25em solid #888;
|
||||
}
|
||||
|
||||
.markdown-alert>:first-child {
|
||||
margin-top: 0
|
||||
}
|
||||
|
||||
.markdown-alert>:last-child {
|
||||
margin-bottom: 0
|
||||
}
|
||||
|
||||
.markdown-alert .markdown-alert-title {
|
||||
display: flex;
|
||||
font-weight: 500;
|
||||
align-items: center;
|
||||
line-height: 1
|
||||
}
|
||||
|
||||
.markdown-alert .markdown-alert-title .octicon {
|
||||
margin-right: 0.5rem;
|
||||
display: inline-block;
|
||||
overflow: visible !important;
|
||||
vertical-align: text-bottom;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-note {
|
||||
border-left-color: var(--color-note);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-note .markdown-alert-title {
|
||||
color: var(--color-note);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-important {
|
||||
border-left-color: var(--color-important);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-important .markdown-alert-title {
|
||||
color: var(--color-important);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-warning {
|
||||
border-left-color: var(--color-warning);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-warning .markdown-alert-title {
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-tip {
|
||||
border-left-color: var(--color-tip);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-tip .markdown-alert-title {
|
||||
color: var(--color-tip);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-caution {
|
||||
border-left-color: var(--color-caution);
|
||||
}
|
||||
|
||||
.markdown-alert.markdown-alert-caution .markdown-alert-title {
|
||||
color: var(--color-caution);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body class="vscode-body vscode-light">
|
||||
<h1 id="vícejazyčná-dokumentace">Vícejazyčná dokumentace</h1>
|
||||
<p>Tento adresář obsahuje strojově asistované překlady založené na anglické dokumentaci.</p>
|
||||
<ul>
|
||||
<li><strong>API_REFERENCE.md</strong> : 🇺🇸 <a href="../API_REFERENCE.html">Česky</a> | 🇧🇷 <a href="./pt-BR/API_REFERENCE.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/API_REFERENCE.html">Español</a> | 🇫🇷 <a href="./fr/API_REFERENCE.html">Français</a> | 🇮🇹 <a href="./it/API_REFERENCE.html">Italiano</a> | 🇷🇺 <a href="./ru/API_REFERENCE.html">Русский</a> | 🇨🇳<a href="./zh-CN/API_REFERENCE.html">中文 (简体)</a> | 🇩🇪 <a href="./de/API_REFERENCE.html">Deutsch</a> | 🇮🇳 <a href="./in/API_REFERENCE.html">हिन्दी</a> | 🇹🇭 <a href="./th/API_REFERENCE.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/API_REFERENCE.html">Українська</a> | 🇸🇦 <a href="./ar/API_REFERENCE.html">العربية</a> | 🇯🇵<a href="./ja/API_REFERENCE.html">日本語</a>| 🇻🇳 <a href="./vi/API_REFERENCE.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/API_REFERENCE.html">Български</a> | 🇩🇰 <a href="./da/API_REFERENCE.html">Dánsko</a> | 🇫🇮 <a href="./fi/API_REFERENCE.html">Suomi</a> | 🇮🇱 <a href="./he/API_REFERENCE.html">עברית</a> | 🇭🇺 <a href="./hu/API_REFERENCE.html">maďarština</a> | 🇮🇩 <a href="./id/API_REFERENCE.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/API_REFERENCE.html">한국어</a> | 🇲🇾 <a href="./ms/API_REFERENCE.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/API_REFERENCE.html">Nizozemsko</a> | 🇳🇴 <a href="./no/API_REFERENCE.html">Norsk</a> | 🇵🇹 <a href="./pt/API_REFERENCE.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/API_REFERENCE.html">Română</a> | 🇵🇱 <a href="./pl/API_REFERENCE.html">Polski</a> | 🇸🇰 <a href="./sk/API_REFERENCE.html">Slovenčina</a> | 🇸🇪 <a href="./sv/API_REFERENCE.html">Svenska</a> | 🇵🇭 <a href="./phi/API_REFERENCE.html">Filipínec</a></li>
|
||||
<li><strong><a href="http://ARCHITECTURE.html">ARCHITECTURE.md</a></strong> : 🇺🇸 <a href="../ARCHITECTURE.html">anglicky</a> | 🇧🇷 <a href="./pt-BR/ARCHITECTURE.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/ARCHITECTURE.html">Español</a> | 🇫🇷 <a href="./fr/ARCHITECTURE.html">Français</a> | 🇮🇹 <a href="./it/ARCHITECTURE.html">Italiano</a> | 🇷🇺 <a href="./ru/ARCHITECTURE.html">Русский</a> | 🇨🇳<a href="./zh-CN/ARCHITECTURE.html">中文 (简体)</a> | 🇩🇪 <a href="./de/ARCHITECTURE.html">Deutsch</a> | 🇮🇳 <a href="./in/ARCHITECTURE.html">हिन्दी</a> | 🇹🇭 <a href="./th/ARCHITECTURE.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/ARCHITECTURE.html">Українська</a> | 🇸🇦 <a href="./ar/ARCHITECTURE.html">العربية</a> | 🇯🇵<a href="./ja/ARCHITECTURE.html">日本語</a>| 🇻🇳 <a href="./vi/ARCHITECTURE.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/ARCHITECTURE.html">Български</a> | 🇩🇰 <a href="./da/ARCHITECTURE.html">Dánsko</a> | 🇫🇮 <a href="./fi/ARCHITECTURE.html">Suomi</a> | 🇮🇱 <a href="./he/ARCHITECTURE.html">עברית</a> | 🇭🇺 <a href="./hu/ARCHITECTURE.html">maďarština</a> | 🇮🇩 <a href="./id/ARCHITECTURE.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/ARCHITECTURE.html">한국어</a> | 🇲🇾 <a href="./ms/ARCHITECTURE.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/ARCHITECTURE.html">Nizozemsko</a> | 🇳🇴 <a href="./no/ARCHITECTURE.html">Norsk</a> | 🇵🇹 <a href="./pt/ARCHITECTURE.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/ARCHITECTURE.html">Română</a> | 🇵🇱 <a href="./pl/ARCHITECTURE.html">Polski</a> | 🇸🇰 <a href="./sk/ARCHITECTURE.html">Slovenčina</a> | 🇸🇪 <a href="./sv/ARCHITECTURE.html">Svenska</a> | 🇵🇭 <a href="./phi/ARCHITECTURE.html">Filipínec</a></li>
|
||||
<li><strong>CODEBASE_DOCUMENTATION.md</strong> : 🇺🇸 <a href="../CODEBASE_DOCUMENTATION.html">anglicky</a> | 🇧🇷 <a href="./pt-BR/CODEBASE_DOCUMENTATION.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/CODEBASE_DOCUMENTATION.html">Español</a> | 🇫🇷 <a href="./fr/CODEBASE_DOCUMENTATION.html">Français</a> | 🇮🇹 <a href="./it/CODEBASE_DOCUMENTATION.html">Italiano</a> | 🇷🇺 <a href="./ru/CODEBASE_DOCUMENTATION.html">Русский</a> | 🇨🇳<a href="./zh-CN/CODEBASE_DOCUMENTATION.html">中文 (简体)</a> | 🇩🇪 <a href="./de/CODEBASE_DOCUMENTATION.html">Deutsch</a> | 🇮🇳 <a href="./in/CODEBASE_DOCUMENTATION.html">हिन्दी</a> | 🇹🇭 <a href="./th/CODEBASE_DOCUMENTATION.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/CODEBASE_DOCUMENTATION.html">Українська</a> | 🇸🇦 <a href="./ar/CODEBASE_DOCUMENTATION.html">العربية</a> | 🇯🇵<a href="./ja/CODEBASE_DOCUMENTATION.html">日本語</a>| 🇻🇳 <a href="./vi/CODEBASE_DOCUMENTATION.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/CODEBASE_DOCUMENTATION.html">Български</a> | 🇩🇰 <a href="./da/CODEBASE_DOCUMENTATION.html">Dánsko</a> | 🇫🇮 <a href="./fi/CODEBASE_DOCUMENTATION.html">Suomi</a> | 🇮🇱 <a href="./he/CODEBASE_DOCUMENTATION.html">עברית</a> | 🇭🇺 <a href="./hu/CODEBASE_DOCUMENTATION.html">maďarština</a> | 🇮🇩 <a href="./id/CODEBASE_DOCUMENTATION.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/CODEBASE_DOCUMENTATION.html">한국어</a> | 🇲🇾 <a href="./ms/CODEBASE_DOCUMENTATION.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/CODEBASE_DOCUMENTATION.html">Nizozemsko</a> | 🇳🇴 <a href="./no/CODEBASE_DOCUMENTATION.html">Norsk</a> | 🇵🇹 <a href="./pt/CODEBASE_DOCUMENTATION.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/CODEBASE_DOCUMENTATION.html">Română</a> | 🇵🇱 <a href="./pl/CODEBASE_DOCUMENTATION.html">Polski</a> | 🇸🇰 <a href="./sk/CODEBASE_DOCUMENTATION.html">Slovenčina</a> | 🇸🇪 <a href="./sv/CODEBASE_DOCUMENTATION.html">Svenska</a> | 🇵🇭 <a href="./phi/CODEBASE_DOCUMENTATION.html">Filipínec</a></li>
|
||||
<li><strong><a href="http://FEATURES.html">FEATURES.md</a></strong> : 🇺🇸 <a href="../FEATURES.html">anglicky</a> | 🇧🇷 <a href="./pt-BR/FEATURES.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/FEATURES.html">Español</a> | 🇫🇷 <a href="./fr/FEATURES.html">Français</a> | 🇮🇹 <a href="./it/FEATURES.html">Italiano</a> | 🇷🇺 <a href="./ru/FEATURES.html">Русский</a> | 🇨🇳<a href="./zh-CN/FEATURES.html">中文 (简体)</a> | 🇩🇪 <a href="./de/FEATURES.html">Deutsch</a> | 🇮🇳 <a href="./in/FEATURES.html">हिन्दी</a> | 🇹🇭 <a href="./th/FEATURES.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/FEATURES.html">Українська</a> | 🇸🇦 <a href="./ar/FEATURES.html">العربية</a> | 🇯🇵<a href="./ja/FEATURES.html">日本語</a>| 🇻🇳 <a href="./vi/FEATURES.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/FEATURES.html">Български</a> | 🇩🇰 <a href="./da/FEATURES.html">Dánsko</a> | 🇫🇮 <a href="./fi/FEATURES.html">Suomi</a> | 🇮🇱 <a href="./he/FEATURES.html">עברית</a> | 🇭🇺 <a href="./hu/FEATURES.html">maďarština</a> | 🇮🇩 <a href="./id/FEATURES.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/FEATURES.html">한국어</a> | 🇲🇾 <a href="./ms/FEATURES.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/FEATURES.html">Nizozemsko</a> | 🇳🇴 <a href="./no/FEATURES.html">Norsk</a> | 🇵🇹 <a href="./pt/FEATURES.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/FEATURES.html">Română</a> | 🇵🇱 <a href="./pl/FEATURES.html">Polski</a> | 🇸🇰 <a href="./sk/FEATURES.html">Slovenčina</a> | 🇸🇪 <a href="./sv/FEATURES.html">Svenska</a> | 🇵🇭 <a href="./phi/FEATURES.html">Filipínec</a></li>
|
||||
<li><strong><a href="http://TOUBLESHOOTING.html">TOUBLESHOOTING.md</a></strong> : 🇺🇸 <a href="../TROUBLESHOOTING.html">anglicky</a> | 🇧🇷 <a href="./pt-BR/TROUBLESHOOTING.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/TROUBLESHOOTING.html">Español</a> | 🇫🇷 <a href="./fr/TROUBLESHOOTING.html">Français</a> | 🇮🇹 <a href="./it/TROUBLESHOOTING.html">Italiano</a> | 🇷🇺 <a href="./ru/TROUBLESHOOTING.html">Русский</a> | 🇨🇳<a href="./zh-CN/TROUBLESHOOTING.html">中文 (简体)</a> | 🇩🇪 <a href="./de/TROUBLESHOOTING.html">Deutsch</a> | 🇮🇳 <a href="./in/TROUBLESHOOTING.html">हिन्दी</a> | 🇹🇭 <a href="./th/TROUBLESHOOTING.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/TROUBLESHOOTING.html">Українська</a> | 🇸🇦 <a href="./ar/TROUBLESHOOTING.html">العربية</a> | 🇯🇵<a href="./ja/TROUBLESHOOTING.html">日本語</a>| 🇻🇳 <a href="./vi/TROUBLESHOOTING.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/TROUBLESHOOTING.html">Български</a> | 🇩🇰 <a href="./da/TROUBLESHOOTING.html">Dánsko</a> | 🇫🇮 <a href="./fi/TROUBLESHOOTING.html">Suomi</a> | 🇮🇱 <a href="./he/TROUBLESHOOTING.html">עברית</a> | 🇭🇺 <a href="./hu/TROUBLESHOOTING.html">maďarština</a> | 🇮🇩 <a href="./id/TROUBLESHOOTING.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/TROUBLESHOOTING.html">한국어</a> | 🇲🇾 <a href="./ms/TROUBLESHOOTING.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/TROUBLESHOOTING.html">Nizozemsko</a> | 🇳🇴 <a href="./no/TROUBLESHOOTING.html">Norsk</a> | 🇵🇹 <a href="./pt/TROUBLESHOOTING.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/TROUBLESHOOTING.html">Română</a> | 🇵🇱 <a href="./pl/TROUBLESHOOTING.html">Polski</a> | 🇸🇰 <a href="./sk/TROUBLESHOOTING.html">Slovenčina</a> | 🇸🇪 <a href="./sv/TROUBLESHOOTING.html">Svenska</a> | 🇵🇭 <a href="./phi/TROUBLESHOOTING.html">Filipínec</a></li>
|
||||
<li><strong>USER_GUIDE.md</strong> : 🇺🇸 <a href="../USER_GUIDE.html">anglicky</a> | 🇧🇷 <a href="./pt-BR/USER_GUIDE.html">Português (Brazílie)</a> | 🇪🇸 <a href="./es/USER_GUIDE.html">Español</a> | 🇫🇷 <a href="./fr/USER_GUIDE.html">Français</a> | 🇮🇹 <a href="./it/USER_GUIDE.html">Italiano</a> | 🇷🇺 <a href="./ru/USER_GUIDE.html">Русский</a> | 🇨🇳<a href="./zh-CN/USER_GUIDE.html">中文 (简体)</a> | 🇩🇪 <a href="./de/USER_GUIDE.html">Deutsch</a> | 🇮🇳 <a href="./in/USER_GUIDE.html">हिन्दी</a> | 🇹🇭 <a href="./th/USER_GUIDE.html">ไทย</a> | 🇺🇦 <a href="./uk-UA/USER_GUIDE.html">Українська</a> | 🇸🇦 <a href="./ar/USER_GUIDE.html">العربية</a> | 🇯🇵<a href="./ja/USER_GUIDE.html">日本語</a>| 🇻🇳 <a href="./vi/USER_GUIDE.html">Tiếng Việt</a> | 🇧🇬 <a href="./bg/USER_GUIDE.html">Български</a> | 🇩🇰 <a href="./da/USER_GUIDE.html">Dánsko</a> | 🇫🇮 <a href="./fi/USER_GUIDE.html">Suomi</a> | 🇮🇱 <a href="./he/USER_GUIDE.html">עברית</a> | 🇭🇺 <a href="./hu/USER_GUIDE.html">maďarština</a> | 🇮🇩 <a href="./id/USER_GUIDE.html">Bahasa Indonésie</a> | 🇰🇷 <a href="./ko/USER_GUIDE.html">한국어</a> | 🇲🇾 <a href="./ms/USER_GUIDE.html">Bahasa Melayu</a> | 🇳🇱 <a href="./nl/USER_GUIDE.html">Nizozemsko</a> | 🇳🇴 <a href="./no/USER_GUIDE.html">Norsk</a> | 🇵🇹 <a href="./pt/USER_GUIDE.html">Português (Portugalsko)</a> | 🇷🇴 <a href="./ro/USER_GUIDE.html">Română</a> | 🇵🇱 <a href="./pl/USER_GUIDE.html">Polski</a> | 🇸🇰 <a href="./sk/USER_GUIDE.html">Slovenčina</a> | 🇸🇪 <a href="./sv/USER_GUIDE.html">Svenska</a> | 🇵🇭 <a href="./phi/USER_GUIDE.html">Filipínec</a></li>
|
||||
</ul>
|
||||
<h2 id="nedávná-poznámka-zásady-limitů-pro-účty-codex">Nedávná poznámka: Zásady limitů pro účty Codex</h2>
|
||||
<p>Dokumentace nyní zahrnuje chování zásad kvót na úrovni účtu Codex:</p>
|
||||
<ul>
|
||||
<li>Přepínání pro jednotlivé účty: <code>5h</code> a <code>Weekly</code> (ZAP/VYP).</li>
|
||||
<li>Zásady prahových hodnot: povolené okno dosahující >=90 % označuje účet jako nezpůsobilý k výběru.</li>
|
||||
<li>Automatická rotace: provoz se přesune na další způsobilý účet Codex.</li>
|
||||
<li>Automatické opětovné použití: účet se opět stane způsobilým po úspěšném <code>resetAt</code> poskytovatele.</li>
|
||||
</ul>
|
||||
<p>Vygenerováno 26. února 2026.</p>
|
||||
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
26
docs/i18n/cs/i18n/README.md
Normal file
26
docs/i18n/cs/i18n/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Vícejazyčná dokumentace
|
||||
|
||||
Tento adresář obsahuje strojově asistované překlady založené na anglické dokumentaci.
|
||||
|
||||
- **API_REFERENCE.md** : 🇺🇸 [Česky](../API_REFERENCE.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/API_REFERENCE.md) | 🇪🇸 [Español](./es/API_REFERENCE.md) | 🇫🇷 [Français](./fr/API_REFERENCE.md) | 🇮🇹 [Italiano](./it/API_REFERENCE.md) | 🇷🇺 [Русский](./ru/API_REFERENCE.md) | 🇨🇳[中文 (简体)](./zh-CN/API_REFERENCE.md) | 🇩🇪 [Deutsch](./de/API_REFERENCE.md) | 🇮🇳 [हिन्दी](./in/API_REFERENCE.md) | 🇹🇭 [ไทย](./th/API_REFERENCE.md) | 🇺🇦 [Українська](./uk-UA/API_REFERENCE.md) | 🇸🇦 [العربية](./ar/API_REFERENCE.md) | 🇯🇵[日本語](./ja/API_REFERENCE.md)| 🇻🇳 [Tiếng Việt](./vi/API_REFERENCE.md) | 🇧🇬 [Български](./bg/API_REFERENCE.md) | 🇩🇰 [Dánsko](./da/API_REFERENCE.md) | 🇫🇮 [Suomi](./fi/API_REFERENCE.md) | 🇮🇱 [עברית](./he/API_REFERENCE.md) | 🇭🇺 [maďarština](./hu/API_REFERENCE.md) | 🇮🇩 [Bahasa Indonésie](./id/API_REFERENCE.md) | 🇰🇷 [한국어](./ko/API_REFERENCE.md) | 🇲🇾 [Bahasa Melayu](./ms/API_REFERENCE.md) | 🇳🇱 [Nizozemsko](./nl/API_REFERENCE.md) | 🇳🇴 [Norsk](./no/API_REFERENCE.md) | 🇵🇹 [Português (Portugalsko)](./pt/API_REFERENCE.md) | 🇷🇴 [Română](./ro/API_REFERENCE.md) | 🇵🇱 [Polski](./pl/API_REFERENCE.md) | 🇸🇰 [Slovenčina](./sk/API_REFERENCE.md) | 🇸🇪 [Svenska](./sv/API_REFERENCE.md) | 🇵🇭 [Filipínec](./phi/API_REFERENCE.md) | 🇨🇿 [Čeština](./cs/API_REFERENCE.md)
|
||||
|
||||
- **ARCHITECTURE.md** : 🇺🇸 [anglicky](../ARCHITECTURE.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/ARCHITECTURE.md) | 🇪🇸 [Español](./es/ARCHITECTURE.md) | 🇫🇷 [Français](./fr/ARCHITECTURE.md) | 🇮🇹 [Italiano](./it/ARCHITECTURE.md) | 🇷🇺 [Русский](./ru/ARCHITECTURE.md) | 🇨🇳[中文 (简体)](./zh-CN/ARCHITECTURE.md) | 🇩🇪 [Deutsch](./de/ARCHITECTURE.md) | 🇮🇳 [हिन्दी](./in/ARCHITECTURE.md) | 🇹🇭 [ไทย](./th/ARCHITECTURE.md) | 🇺🇦 [Українська](./uk-UA/ARCHITECTURE.md) | 🇸🇦 [العربية](./ar/ARCHITECTURE.md) | 🇯🇵[日本語](./ja/ARCHITECTURE.md)| 🇻🇳 [Tiếng Việt](./vi/ARCHITECTURE.md) | 🇧🇬 [Български](./bg/ARCHITECTURE.md) | 🇩🇰 [Dánsko](./da/ARCHITECTURE.md) | 🇫🇮 [Suomi](./fi/ARCHITECTURE.md) | 🇮🇱 [עברית](./he/ARCHITECTURE.md) | 🇭🇺 [maďarština](./hu/ARCHITECTURE.md) | 🇮🇩 [Bahasa Indonésie](./id/ARCHITECTURE.md) | 🇰🇷 [한국어](./ko/ARCHITECTURE.md) | 🇲🇾 [Bahasa Melayu](./ms/ARCHITECTURE.md) | 🇳🇱 [Nizozemsko](./nl/ARCHITECTURE.md) | 🇳🇴 [Norsk](./no/ARCHITECTURE.md) | 🇵🇹 [Português (Portugalsko)](./pt/ARCHITECTURE.md) | 🇷🇴 [Română](./ro/ARCHITECTURE.md) | 🇵🇱 [Polski](./pl/ARCHITECTURE.md) | 🇸🇰 [Slovenčina](./sk/ARCHITECTURE.md) | 🇸🇪 [Svenska](./sv/ARCHITECTURE.md) | 🇵🇭 [Filipínec](./phi/ARCHITECTURE.md) | 🇨🇿 [Čeština](./cs/ARCHITECTURE.md)
|
||||
|
||||
- **CODEBASE_DOCUMENTATION.md** : 🇺🇸 [anglicky](../CODEBASE_DOCUMENTATION.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/CODEBASE_DOCUMENTATION.md) | 🇪🇸 [Español](./es/CODEBASE_DOCUMENTATION.md) | 🇫🇷 [Français](./fr/CODEBASE_DOCUMENTATION.md) | 🇮🇹 [Italiano](./it/CODEBASE_DOCUMENTATION.md) | 🇷🇺 [Русский](./ru/CODEBASE_DOCUMENTATION.md) | 🇨🇳[中文 (简体)](./zh-CN/CODEBASE_DOCUMENTATION.md) | 🇩🇪 [Deutsch](./de/CODEBASE_DOCUMENTATION.md) | 🇮🇳 [हिन्दी](./in/CODEBASE_DOCUMENTATION.md) | 🇹🇭 [ไทย](./th/CODEBASE_DOCUMENTATION.md) | 🇺🇦 [Українська](./uk-UA/CODEBASE_DOCUMENTATION.md) | 🇸🇦 [العربية](./ar/CODEBASE_DOCUMENTATION.md) | 🇯🇵[日本語](./ja/CODEBASE_DOCUMENTATION.md)| 🇻🇳 [Tiếng Việt](./vi/CODEBASE_DOCUMENTATION.md) | 🇧🇬 [Български](./bg/CODEBASE_DOCUMENTATION.md) | 🇩🇰 [Dánsko](./da/CODEBASE_DOCUMENTATION.md) | 🇫🇮 [Suomi](./fi/CODEBASE_DOCUMENTATION.md) | 🇮🇱 [עברית](./he/CODEBASE_DOCUMENTATION.md) | 🇭🇺 [maďarština](./hu/CODEBASE_DOCUMENTATION.md) | 🇮🇩 [Bahasa Indonésie](./id/CODEBASE_DOCUMENTATION.md) | 🇰🇷 [한국어](./ko/CODEBASE_DOCUMENTATION.md) | 🇲🇾 [Bahasa Melayu](./ms/CODEBASE_DOCUMENTATION.md) | 🇳🇱 [Nizozemsko](./nl/CODEBASE_DOCUMENTATION.md) | 🇳🇴 [Norsk](./no/CODEBASE_DOCUMENTATION.md) | 🇵🇹 [Português (Portugalsko)](./pt/CODEBASE_DOCUMENTATION.md) | 🇷🇴 [Română](./ro/CODEBASE_DOCUMENTATION.md) | 🇵🇱 [Polski](./pl/CODEBASE_DOCUMENTATION.md) | 🇸🇰 [Slovenčina](./sk/CODEBASE_DOCUMENTATION.md) | 🇸🇪 [Svenska](./sv/CODEBASE_DOCUMENTATION.md) | 🇵🇭 [Filipínec](./phi/CODEBASE_DOCUMENTATION.md) | 🇨🇿 [Čeština](./cs/CODEBASE_DOCUMENTATION.md)
|
||||
|
||||
- **FEATURES.md** : 🇺🇸 [anglicky](../FEATURES.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/FEATURES.md) | 🇪🇸 [Español](./es/FEATURES.md) | 🇫🇷 [Français](./fr/FEATURES.md) | 🇮🇹 [Italiano](./it/FEATURES.md) | 🇷🇺 [Русский](./ru/FEATURES.md) | 🇨🇳[中文 (简体)](./zh-CN/FEATURES.md) | 🇩🇪 [Deutsch](./de/FEATURES.md) | 🇮🇳 [हिन्दी](./in/FEATURES.md) | 🇹🇭 [ไทย](./th/FEATURES.md) | 🇺🇦 [Українська](./uk-UA/FEATURES.md) | 🇸🇦 [العربية](./ar/FEATURES.md) | 🇯🇵[日本語](./ja/FEATURES.md)| 🇻🇳 [Tiếng Việt](./vi/FEATURES.md) | 🇧🇬 [Български](./bg/FEATURES.md) | 🇩🇰 [Dánsko](./da/FEATURES.md) | 🇫🇮 [Suomi](./fi/FEATURES.md) | 🇮🇱 [עברית](./he/FEATURES.md) | 🇭🇺 [maďarština](./hu/FEATURES.md) | 🇮🇩 [Bahasa Indonésie](./id/FEATURES.md) | 🇰🇷 [한국어](./ko/FEATURES.md) | 🇲🇾 [Bahasa Melayu](./ms/FEATURES.md) | 🇳🇱 [Nizozemsko](./nl/FEATURES.md) | 🇳🇴 [Norsk](./no/FEATURES.md) | 🇵🇹 [Português (Portugalsko)](./pt/FEATURES.md) | 🇷🇴 [Română](./ro/FEATURES.md) | 🇵🇱 [Polski](./pl/FEATURES.md) | 🇸🇰 [Slovenčina](./sk/FEATURES.md) | 🇸🇪 [Svenska](./sv/FEATURES.md) | 🇵🇭 [Filipínec](./phi/FEATURES.md) | 🇨🇿 [Čeština](./cs/FEATURES.md)
|
||||
|
||||
- **TOUBLESHOOTING.md** : 🇺🇸 [anglicky](../TROUBLESHOOTING.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/TROUBLESHOOTING.md) | 🇪🇸 [Español](./es/TROUBLESHOOTING.md) | 🇫🇷 [Français](./fr/TROUBLESHOOTING.md) | 🇮🇹 [Italiano](./it/TROUBLESHOOTING.md) | 🇷🇺 [Русский](./ru/TROUBLESHOOTING.md) | 🇨🇳[中文 (简体)](./zh-CN/TROUBLESHOOTING.md) | 🇩🇪 [Deutsch](./de/TROUBLESHOOTING.md) | 🇮🇳 [हिन्दी](./in/TROUBLESHOOTING.md) | 🇹🇭 [ไทย](./th/TROUBLESHOOTING.md) | 🇺🇦 [Українська](./uk-UA/TROUBLESHOOTING.md) | 🇸🇦 [العربية](./ar/TROUBLESHOOTING.md) | 🇯🇵[日本語](./ja/TROUBLESHOOTING.md)| 🇻🇳 [Tiếng Việt](./vi/TROUBLESHOOTING.md) | 🇧🇬 [Български](./bg/TROUBLESHOOTING.md) | 🇩🇰 [Dánsko](./da/TROUBLESHOOTING.md) | 🇫🇮 [Suomi](./fi/TROUBLESHOOTING.md) | 🇮🇱 [עברית](./he/TROUBLESHOOTING.md) | 🇭🇺 [maďarština](./hu/TROUBLESHOOTING.md) | 🇮🇩 [Bahasa Indonésie](./id/TROUBLESHOOTING.md) | 🇰🇷 [한국어](./ko/TROUBLESHOOTING.md) | 🇲🇾 [Bahasa Melayu](./ms/TROUBLESHOOTING.md) | 🇳🇱 [Nizozemsko](./nl/TROUBLESHOOTING.md) | 🇳🇴 [Norsk](./no/TROUBLESHOOTING.md) | 🇵🇹 [Português (Portugalsko)](./pt/TROUBLESHOOTING.md) | 🇷🇴 [Română](./ro/TROUBLESHOOTING.md) | 🇵🇱 [Polski](./pl/TROUBLESHOOTING.md) | 🇸🇰 [Slovenčina](./sk/TROUBLESHOOTING.md) | 🇸🇪 [Svenska](./sv/TROUBLESHOOTING.md) | 🇵🇭 [Filipínec](./phi/TROUBLESHOOTING.md) | 🇨🇿 [Čeština](./cs/TROUBLESHOOTING.md)
|
||||
|
||||
- **USER_GUIDE.md** : 🇺🇸 [anglicky](../USER_GUIDE.md) | 🇧🇷 [Português (Brazílie)](./pt-BR/USER_GUIDE.md) | 🇪🇸 [Español](./es/USER_GUIDE.md) | 🇫🇷 [Français](./fr/USER_GUIDE.md) | 🇮🇹 [Italiano](./it/USER_GUIDE.md) | 🇷🇺 [Русский](./ru/USER_GUIDE.md) | 🇨🇳[中文 (简体)](./zh-CN/USER_GUIDE.md) | 🇩🇪 [Deutsch](./de/USER_GUIDE.md) | 🇮🇳 [हिन्दी](./in/USER_GUIDE.md) | 🇹🇭 [ไทย](./th/USER_GUIDE.md) | 🇺🇦 [Українська](./uk-UA/USER_GUIDE.md) | 🇸🇦 [العربية](./ar/USER_GUIDE.md) | 🇯🇵[日本語](./ja/USER_GUIDE.md)| 🇻🇳 [Tiếng Việt](./vi/USER_GUIDE.md) | 🇧🇬 [Български](./bg/USER_GUIDE.md) | 🇩🇰 [Dánsko](./da/USER_GUIDE.md) | 🇫🇮 [Suomi](./fi/USER_GUIDE.md) | 🇮🇱 [עברית](./he/USER_GUIDE.md) | 🇭🇺 [maďarština](./hu/USER_GUIDE.md) | 🇮🇩 [Bahasa Indonésie](./id/USER_GUIDE.md) | 🇰🇷 [한국어](./ko/USER_GUIDE.md) | 🇲🇾 [Bahasa Melayu](./ms/USER_GUIDE.md) | 🇳🇱 [Nizozemsko](./nl/USER_GUIDE.md) | 🇳🇴 [Norsk](./no/USER_GUIDE.md) | 🇵🇹 [Português (Portugalsko)](./pt/USER_GUIDE.md) | 🇷🇴 [Română](./ro/USER_GUIDE.md) | 🇵🇱 [Polski](./pl/USER_GUIDE.md) | 🇸🇰 [Slovenčina](./sk/USER_GUIDE.md) | 🇸🇪 [Svenska](./sv/USER_GUIDE.md) | 🇵🇭 [Filipínec](./phi/USER_GUIDE.md) | 🇨🇿 [Čeština](./cs/USER_GUIDE.md)
|
||||
|
||||
## Nedávná poznámka: Zásady limitů pro účty Codex
|
||||
|
||||
Dokumentace nyní zahrnuje chování zásad kvót na úrovni účtu Codex:
|
||||
|
||||
- Přepínání pro jednotlivé účty: `5h` a `Weekly` (ZAP/VYP).
|
||||
- Zásady prahových hodnot: povolené okno dosahující >=90 % označuje účet jako nezpůsobilý k výběru.
|
||||
- Automatická rotace: provoz se přesune na další způsobilý účet Codex.
|
||||
- Automatické opětovné použití: účet se opět stane způsobilým po úspěšném `resetAt` poskytovatele.
|
||||
|
||||
Vygenerováno 26. února 2026.
|
||||
587
docs/i18n/cs/open-sse/mcp-server/README.md
Normal file
587
docs/i18n/cs/open-sse/mcp-server/README.md
Normal file
@@ -0,0 +1,587 @@
|
||||
# Server OmniRoute MCP
|
||||
|
||||
> **Server protokolu modelového kontextu** , který zpřístupňuje inteligenci brány OmniRoute jako **16 nástrojů** pro agenty umělé inteligence.
|
||||
|
||||
Server MCP umožňuje libovolnému agentovi umělé inteligence (Claude Desktop, Cursor, VS Code Copilot, vlastním agentům) programově **monitorovat, řídit a optimalizovat** bránu umělé inteligence OmniRoute.
|
||||
|
||||
---
|
||||
|
||||
## Architektura
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ AI Agent / IDE │
|
||||
│ (Claude Desktop, Cursor, VS Code, Custom) │
|
||||
└──────────────────────┬───────────────────────────────────────────┘
|
||||
│ MCP Protocol (stdio or HTTP)
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ OmniRoute MCP Server │
|
||||
│ ┌──────────────┐ ┌─────────────────┐ ┌────────────────────┐ │
|
||||
│ │ Scope │ │ 16 MCP Tools │ │ Audit Logger │ │
|
||||
│ │ Enforcement │──│ (Phase 1 + 2) │──│ (SHA-256/SQLite) │ │
|
||||
│ └──────────────┘ └────────┬────────┘ └────────────────────┘ │
|
||||
└─────────────────────────────┼────────────────────────────────────┘
|
||||
│ HTTP (internal)
|
||||
▼
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ OmniRoute Gateway (port 20128) │
|
||||
│ /v1/chat/completions /api/combos /api/usage ... │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Rychlý start
|
||||
|
||||
### 1. Proměnné prostředí
|
||||
|
||||
```bash
|
||||
# Required: OmniRoute base URL
|
||||
export OMNIROUTE_BASE_URL="http://localhost:20128"
|
||||
|
||||
# Optional: API key for authenticated access
|
||||
export OMNIROUTE_API_KEY="your-api-key"
|
||||
|
||||
# Optional: Scope enforcement (default: disabled)
|
||||
export OMNIROUTE_MCP_ENFORCE_SCOPES="true"
|
||||
export OMNIROUTE_MCP_SCOPES="read:health,read:combos,read:quota,read:usage,read:models,execute:completions,write:combos,write:budget,write:resilience"
|
||||
```
|
||||
|
||||
### 2. Transport stdio (integrace IDE)
|
||||
|
||||
Přidejte do konfigurace klienta MCP:
|
||||
|
||||
**Claude Desktop** ( `claude_desktop_config.json` ):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"omniroute": {
|
||||
"command": "node",
|
||||
"args": ["path/to/9router/open-sse/mcp-server/server.ts"],
|
||||
"env": {
|
||||
"OMNIROUTE_BASE_URL": "http://localhost:20128",
|
||||
"OMNIROUTE_API_KEY": "your-key"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Cursor** ( `.cursor/mcp.json` ):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"omniroute": {
|
||||
"command": "npx",
|
||||
"args": ["tsx", "open-sse/mcp-server/server.ts"],
|
||||
"env": {
|
||||
"OMNIROUTE_BASE_URL": "http://localhost:20128"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**VS Code** ( `.vscode/settings.json` ):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"servers": {
|
||||
"omniroute": {
|
||||
"command": "npx",
|
||||
"args": ["tsx", "open-sse/mcp-server/server.ts"],
|
||||
"env": {
|
||||
"OMNIROUTE_BASE_URL": "http://localhost:20128"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Spuštění přes CLI
|
||||
|
||||
```bash
|
||||
# Direct start (stdio)
|
||||
npx tsx open-sse/mcp-server/server.ts
|
||||
|
||||
# Or via OmniRoute CLI
|
||||
omniroute --mcp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Referenční informace o nástrojích
|
||||
|
||||
### Fáze 1: Základní nástroje (8)
|
||||
|
||||
# | Nástroj | Rozsahy | Popis
|
||||
--- | --- | --- | ---
|
||||
1 | `omniroute_get_health` | `read:health` | Stav brány, dostupnost, paměť, jističe, limity rychlosti, statistiky mezipaměti
|
||||
2 | `omniroute_list_combos` | `read:combos` | Vypsat všechny kombinace (modelové řetězce) se strategiemi a volitelnými metrikami
|
||||
3 | `omniroute_get_combo_metrics` | `read:combos` | Metriky výkonu pro konkrétní kombinaci
|
||||
4 | `omniroute_switch_combo` | `write:combos` | Aktivace nebo deaktivace komba pro směrování
|
||||
5 | `omniroute_check_quota` | `read:quota` | Zbývající kvóta API na poskytovatele se stavem tokenu
|
||||
6 | `omniroute_route_request` | `execute:completions` | Odeslat dokončení chatu pomocí inteligentního směrování
|
||||
7 | `omniroute_cost_report` | `read:usage` | Zpráva o nákladech podle období (relace/den/týden/měsíc) s rozpisem podle poskytovatele
|
||||
8 | `omniroute_list_models_catalog` | `read:models` | Seznam všech dostupných modelů od různých poskytovatelů s funkcemi a cenami
|
||||
|
||||
### Fáze 2: Pokročilé nástroje (8)
|
||||
|
||||
# | Nástroj | Rozsahy | Popis
|
||||
--- | --- | --- | ---
|
||||
9 | `omniroute_simulate_route` | `read:health` , `read:combos` | Simulace trasy na dryru zobrazující záložní strom a odhadované náklady
|
||||
10 | `omniroute_set_budget_guard` | `write:budget` | Nastavit rozpočet relace s akcí při překročení: `degrade` , `block` nebo `alert`
|
||||
11 | `omniroute_set_resilience_profile` | `write:resilience` | Použijte profil odolnosti: `aggressive` , `balanced` nebo `conservative`
|
||||
12 | `omniroute_test_combo` | `execute:completions` , `read:combos` | Otestujte každého poskytovatele v kombinaci se skutečným výzvou a nahlaste latenci/náklady
|
||||
13 | `omniroute_get_provider_metrics` | `read:health` | Metriky pro jednotlivé poskytovatele s percentily latence (p50/p95/p99), jistič
|
||||
14 | `omniroute_best_combo_for_task` | `read:combos` , `read:health` | Doporučení kombinací podle typu úkolu s využitím umělé inteligence s omezeními rozpočtu/latence
|
||||
15 | `omniroute_explain_route` | `read:health` , `read:usage` | Vysvětlete, proč byl požadavek směrován k poskytovateli (faktory hodnocení, záložní metody)
|
||||
16 | `omniroute_get_session_snapshot` | `read:usage` | Snímek celého relace: náklady, tokeny, top modely, chyby, stav rozpočtu
|
||||
|
||||
---
|
||||
|
||||
## Příklady klientů
|
||||
|
||||
### Python — Kompletní pracovní postup agenta
|
||||
|
||||
```python
|
||||
"""
|
||||
OmniRoute MCP Client — Python example using the mcp SDK.
|
||||
Install: pip install mcp
|
||||
"""
|
||||
import asyncio
|
||||
from mcp import ClientSession, StdioServerParameters
|
||||
from mcp.client.stdio import stdio_client
|
||||
|
||||
async def main():
|
||||
server = StdioServerParameters(
|
||||
command="npx",
|
||||
args=["tsx", "open-sse/mcp-server/server.ts"],
|
||||
env={
|
||||
"OMNIROUTE_BASE_URL": "http://localhost:20128",
|
||||
"OMNIROUTE_API_KEY": "your-key",
|
||||
},
|
||||
)
|
||||
|
||||
async with stdio_client(server) as (read, write):
|
||||
async with ClientSession(read, write) as session:
|
||||
await session.initialize()
|
||||
|
||||
# 1. Check gateway health
|
||||
health = await session.call_tool("omniroute_get_health", {})
|
||||
print("Health:", health.content[0].text)
|
||||
|
||||
# 2. List available combos with metrics
|
||||
combos = await session.call_tool("omniroute_list_combos", {
|
||||
"includeMetrics": True
|
||||
})
|
||||
print("Combos:", combos.content[0].text)
|
||||
|
||||
# 3. Find the best combo for a coding task
|
||||
best = await session.call_tool("omniroute_best_combo_for_task", {
|
||||
"taskType": "coding",
|
||||
"budgetConstraint": 0.50,
|
||||
"latencyConstraint": 5000,
|
||||
})
|
||||
print("Best combo:", best.content[0].text)
|
||||
|
||||
# 4. Set a session budget guard
|
||||
budget = await session.call_tool("omniroute_set_budget_guard", {
|
||||
"maxCost": 1.00,
|
||||
"action": "degrade",
|
||||
"degradeToTier": "cheap",
|
||||
})
|
||||
print("Budget guard:", budget.content[0].text)
|
||||
|
||||
# 5. Route a request through intelligent pipeline
|
||||
response = await session.call_tool("omniroute_route_request", {
|
||||
"model": "claude-sonnet-4",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Write a Python hello world"}
|
||||
],
|
||||
"role": "coding",
|
||||
})
|
||||
print("Response:", response.content[0].text)
|
||||
|
||||
# 6. Get the session snapshot
|
||||
snapshot = await session.call_tool("omniroute_get_session_snapshot", {})
|
||||
print("Session:", snapshot.content[0].text)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### TypeScript — Programový agent
|
||||
|
||||
```typescript
|
||||
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
||||
|
||||
async function main() {
|
||||
const transport = new StdioClientTransport({
|
||||
command: "npx",
|
||||
args: ["tsx", "open-sse/mcp-server/server.ts"],
|
||||
env: {
|
||||
OMNIROUTE_BASE_URL: "http://localhost:20128",
|
||||
OMNIROUTE_API_KEY: "your-key",
|
||||
},
|
||||
});
|
||||
|
||||
const client = new Client({ name: "my-agent", version: "1.0.0" });
|
||||
await client.connect(transport);
|
||||
|
||||
// Check quota before deciding which model to use
|
||||
const quota = await client.callTool({
|
||||
name: "omniroute_check_quota",
|
||||
arguments: { provider: "claude" },
|
||||
});
|
||||
console.log("Claude quota:", quota.content);
|
||||
|
||||
// Simulate the route before actually calling
|
||||
const simulation = await client.callTool({
|
||||
name: "omniroute_simulate_route",
|
||||
arguments: {
|
||||
model: "claude-sonnet-4",
|
||||
promptTokenEstimate: 2000,
|
||||
},
|
||||
});
|
||||
console.log("Route simulation:", simulation.content);
|
||||
|
||||
// Send the actual request
|
||||
const result = await client.callTool({
|
||||
name: "omniroute_route_request",
|
||||
arguments: {
|
||||
model: "claude-sonnet-4",
|
||||
messages: [{ role: "user", content: "Explain async/await" }],
|
||||
},
|
||||
});
|
||||
console.log("Result:", result.content);
|
||||
|
||||
// Cost report
|
||||
const costs = await client.callTool({
|
||||
name: "omniroute_cost_report",
|
||||
arguments: { period: "session" },
|
||||
});
|
||||
console.log("Costs:", costs.content);
|
||||
|
||||
await client.close();
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### Go — HTTP klient
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Simplified direct-API approach (bypass MCP, hit OmniRoute APIs directly)
|
||||
// Useful if you don't need MCP protocol framing.
|
||||
|
||||
func callTool(baseURL, tool string, args map[string]any) (string, error) {
|
||||
// MCP tools map to OmniRoute APIs:
|
||||
endpoints := map[string]string{
|
||||
"health": "/api/monitoring/health",
|
||||
"combos": "/api/combos",
|
||||
"quota": "/api/usage/quota",
|
||||
"models": "/v1/models",
|
||||
}
|
||||
|
||||
url := baseURL + endpoints[tool]
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func routeRequest(baseURL, model, prompt string) (string, error) {
|
||||
payload := map[string]any{
|
||||
"model": model,
|
||||
"messages": []map[string]string{
|
||||
{"role": "user", "content": prompt},
|
||||
},
|
||||
"stream": false,
|
||||
}
|
||||
data, _ := json.Marshal(payload)
|
||||
|
||||
resp, err := http.Post(
|
||||
baseURL+"/v1/chat/completions",
|
||||
"application/json",
|
||||
bytes.NewReader(data),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
base := "http://localhost:20128"
|
||||
|
||||
health, _ := callTool(base, "health", nil)
|
||||
fmt.Println("Health:", health)
|
||||
|
||||
result, _ := routeRequest(base, "auto", "Hello from Go!")
|
||||
fmt.Println("Result:", result)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Případy použití
|
||||
|
||||
### 🔄 Případ použití 1: Agent pro automatické ozdravování
|
||||
|
||||
Agent, který monitoruje stav OmniRoute a automaticky přepíná kombinace, když se stav poskytovatelů zhorší.
|
||||
|
||||
```python
|
||||
async def auto_healing_loop(session):
|
||||
"""Monitor health and react to provider issues."""
|
||||
while True:
|
||||
# Check health
|
||||
health = await session.call_tool("omniroute_get_health", {})
|
||||
data = json.loads(health.content[0].text)
|
||||
|
||||
# Find providers with open circuit breakers
|
||||
broken = [
|
||||
cb for cb in data["circuitBreakers"]
|
||||
if cb["state"] == "OPEN"
|
||||
]
|
||||
|
||||
if broken:
|
||||
# Switch to a different resilience profile
|
||||
await session.call_tool("omniroute_set_resilience_profile", {
|
||||
"profile": "conservative"
|
||||
})
|
||||
|
||||
# Find best alternative combo
|
||||
best = await session.call_tool("omniroute_best_combo_for_task", {
|
||||
"taskType": "coding"
|
||||
})
|
||||
best_data = json.loads(best.content[0].text)
|
||||
combo_id = best_data["recommendedCombo"]["id"]
|
||||
|
||||
# Activate it
|
||||
await session.call_tool("omniroute_switch_combo", {
|
||||
"comboId": combo_id, "active": True
|
||||
})
|
||||
print(f"⚠️ Auto-healed: switched to {combo_id}")
|
||||
|
||||
await asyncio.sleep(30) # Check every 30 seconds
|
||||
```
|
||||
|
||||
### 💰 Případ užití 2: Programovací agent s ohledem na rozpočet
|
||||
|
||||
Agent, který sleduje náklady v reálném čase a při blížícím se vyčerpání rozpočtu přechází na levnější modely.
|
||||
|
||||
```python
|
||||
async def budget_aware_coding(session, task: str, max_budget: float):
|
||||
"""Complete a coding task within a budget."""
|
||||
# Set budget guard
|
||||
await session.call_tool("omniroute_set_budget_guard", {
|
||||
"maxCost": max_budget,
|
||||
"action": "degrade",
|
||||
"degradeToTier": "cheap",
|
||||
})
|
||||
|
||||
# Simulate first to estimate cost
|
||||
sim = await session.call_tool("omniroute_simulate_route", {
|
||||
"model": "claude-sonnet-4",
|
||||
"promptTokenEstimate": len(task.split()) * 2,
|
||||
})
|
||||
sim_data = json.loads(sim.content[0].text)
|
||||
estimated_cost = sim_data["fallbackTree"]["bestCaseCost"]
|
||||
print(f"Estimated cost: ${estimated_cost:.4f}")
|
||||
|
||||
# Send request
|
||||
result = await session.call_tool("omniroute_route_request", {
|
||||
"model": "claude-sonnet-4",
|
||||
"messages": [{"role": "user", "content": task}],
|
||||
"role": "coding",
|
||||
})
|
||||
|
||||
# Check remaining budget
|
||||
snapshot = await session.call_tool("omniroute_get_session_snapshot", {})
|
||||
snap_data = json.loads(snapshot.content[0].text)
|
||||
print(f"Session cost: ${snap_data['costTotal']:.4f}")
|
||||
if snap_data.get("budgetGuard"):
|
||||
print(f"Budget remaining: ${snap_data['budgetGuard']['remaining']:.4f}")
|
||||
|
||||
return json.loads(result.content[0].text)["response"]["content"]
|
||||
```
|
||||
|
||||
### 🧪 Případ použití 3: Kombinovaný benchmarkingový agent
|
||||
|
||||
Agent, který pravidelně porovnává všechna komba a hlásí nejrychlejší/nejlevnější.
|
||||
|
||||
```python
|
||||
async def benchmark_combos(session):
|
||||
"""Benchmark all enabled combos and rank them."""
|
||||
combos = await session.call_tool("omniroute_list_combos", {
|
||||
"includeMetrics": True,
|
||||
})
|
||||
combo_list = json.loads(combos.content[0].text)["combos"]
|
||||
|
||||
results = []
|
||||
for combo in combo_list:
|
||||
if not combo["enabled"]:
|
||||
continue
|
||||
|
||||
test = await session.call_tool("omniroute_test_combo", {
|
||||
"comboId": combo["id"],
|
||||
"testPrompt": "Return the number 42.",
|
||||
})
|
||||
test_data = json.loads(test.content[0].text)
|
||||
results.append({
|
||||
"combo": combo["name"],
|
||||
"fastest": test_data["summary"]["fastestProvider"],
|
||||
"cheapest": test_data["summary"]["cheapestProvider"],
|
||||
"success_rate": f'{test_data["summary"]["successful"]}/{test_data["summary"]["totalProviders"]}',
|
||||
})
|
||||
|
||||
print("📊 Combo Benchmark Results:")
|
||||
for r in results:
|
||||
print(f" {r['combo']}: fastest={r['fastest']}, cheapest={r['cheapest']}, success={r['success_rate']}")
|
||||
```
|
||||
|
||||
### 🔍 Případ použití 4: Agent pro ladění po smrti
|
||||
|
||||
Agent, který vysvětluje, proč byl požadavek směrován ke konkrétnímu poskytovateli.
|
||||
|
||||
```typescript
|
||||
async function debugRouting(client: Client, requestId: string) {
|
||||
// Explain the routing decision
|
||||
const explanation = await client.callTool({
|
||||
name: "omniroute_explain_route",
|
||||
arguments: { requestId },
|
||||
});
|
||||
const data = JSON.parse(explanation.content[0].text);
|
||||
|
||||
console.log(`Request ${requestId}:`);
|
||||
console.log(` Provider: ${data.decision.providerSelected}`);
|
||||
console.log(` Model: ${data.decision.modelUsed}`);
|
||||
console.log(` Score: ${data.decision.score}`);
|
||||
console.log(` Factors:`);
|
||||
for (const factor of data.decision.factors) {
|
||||
console.log(` ${factor.name}: ${factor.value} (weight: ${factor.weight})`);
|
||||
}
|
||||
if (data.decision.fallbacksTriggered.length > 0) {
|
||||
console.log(` Fallbacks triggered:`);
|
||||
for (const fb of data.decision.fallbacksTriggered) {
|
||||
console.log(` ${fb.provider}: ${fb.reason}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 📋 Případ použití 5: Agent pro vyhledávání modelů
|
||||
|
||||
Agent, který vyhledává nejlevnější modely pro danou funkci.
|
||||
|
||||
```python
|
||||
async def find_cheapest_models(session, capability="chat"):
|
||||
"""Find the cheapest available models for a capability."""
|
||||
catalog = await session.call_tool("omniroute_list_models_catalog", {
|
||||
"capability": capability,
|
||||
})
|
||||
models = json.loads(catalog.content[0].text)["models"]
|
||||
|
||||
# Filter available models with pricing
|
||||
priced = [
|
||||
m for m in models
|
||||
if m["status"] == "available" and m.get("pricing")
|
||||
]
|
||||
priced.sort(key=lambda m: m["pricing"]["inputPerMillion"] or float("inf"))
|
||||
|
||||
print(f"💡 Cheapest {capability} models:")
|
||||
for m in priced[:5]:
|
||||
input_cost = m["pricing"]["inputPerMillion"] or 0
|
||||
output_cost = m["pricing"]["outputPerMillion"] or 0
|
||||
print(f" {m['id']} ({m['provider']}): ${input_cost}/M in, ${output_cost}/M out")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Zabezpečení a vynucování rozsahu
|
||||
|
||||
Server MCP podporuje **detailní vynucování rozsahu** pro prostředí s více klienty:
|
||||
|
||||
Rozsah | Nástroje
|
||||
--- | ---
|
||||
`read:health` | `get_health` , `simulate_route` , `get_provider_metrics` , `best_combo_for_task` , `explain_route`
|
||||
`read:combos` | `list_combos` , `get_combo_metrics` , `simulate_route` , `best_combo_for_task` , `test_combo`
|
||||
`read:quota` | `check_quota`
|
||||
`read:usage` | `cost_report` , `explain_route` , `get_session_snapshot`
|
||||
`read:models` | `list_models_catalog`
|
||||
`write:combos` | `switch_combo`
|
||||
`write:budget` | `set_budget_guard`
|
||||
`write:resilience` | `set_resilience_profile`
|
||||
`execute:completions` | `route_request` , `test_combo`
|
||||
|
||||
**Rozsahy zástupných znaků:** Použijte `read:*` pro udělení všech rozsahů pro čtení nebo `*` pro plný přístup.
|
||||
|
||||
---
|
||||
|
||||
## Protokolování auditu
|
||||
|
||||
Každé volání nástroje je zaznamenáno do tabulky SQLite `mcp_tool_audit` :
|
||||
|
||||
- **Vstup:** SHA-256 hash (nikdy neukládá nezpracované výzvy)
|
||||
- **Výstup:** Zkráceno na 200 znaků
|
||||
- **Metadata:** Název nástroje, doba trvání, úspěch/chyba, ID klíče API
|
||||
|
||||
Přístup k auditním datům prostřednictvím:
|
||||
|
||||
```typescript
|
||||
import { getRecentAuditEntries, getAuditStats } from "./audit";
|
||||
|
||||
const entries = await getRecentAuditEntries(50);
|
||||
const stats = await getAuditStats();
|
||||
// stats: { totalCalls, successRate, avgDurationMs, topTools }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Struktura souboru
|
||||
|
||||
```
|
||||
mcp-server/
|
||||
├── server.ts # MCP server setup, essential tool handlers, entry point
|
||||
├── index.ts # Barrel export
|
||||
├── audit.ts # SQLite audit logger (SHA-256 input hashing)
|
||||
├── scopeEnforcement.ts # Fine-grained scope enforcement
|
||||
├── schemas/
|
||||
│ ├── tools.ts # Zod schemas for all 16 tools (input/output/scopes)
|
||||
│ ├── a2a.ts # A2A protocol types (Agent Card, Task, JSON-RPC)
|
||||
│ ├── audit.ts # Audit & routing decision types + hash helpers
|
||||
│ └── index.ts # Schema barrel export
|
||||
├── tools/
|
||||
│ └── advancedTools.ts # Phase 2 tool handlers (8 advanced tools)
|
||||
└── __tests__/
|
||||
├── essentialTools.test.ts
|
||||
├── advancedTools.test.ts
|
||||
└── a2aLifecycle.test.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Licence
|
||||
|
||||
Součást [OmniRoute](https://github.com/diegosouzapw/OmniRoute) — licence MIT.
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Installationsvejledning på VM med Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Komplet guide til at installere og konfigurere OmniRoute på en VM (VPS) med domæne administreret via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Forudsætninger
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Vare | Minimum | Anbefalet |
|
||||
| ---------- | ------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domæne** | Registreret på Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Testede udbydere**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurer VM'en
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Opret instansen
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
På din foretrukne VPS-udbyder:
|
||||
|
||||
---
|
||||
- Vælg Ubuntu 24.04 LTS
|
||||
- Vælg minimumsplanen (1 vCPU / 1 GB RAM)
|
||||
- Indstil en stærk root-adgangskode eller konfigurer SSH-nøgle
|
||||
- Bemærk den **offentlige IP** (f.eks. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Tilslut via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Opdater systemet
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Installer Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installer nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurer firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tip**: For maksimal sikkerhed skal du begrænse porte 80 og 443 til kun Cloudflare IP'er. Se afsnittet [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installer OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Opret konfigurationsmappe
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Opret fil med miljøvariabler
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **VIGTIG**: Generer unikke hemmelige nøgler! Brug `openssl rand -hex 32` for hver nøgle.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Start beholderen
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Bekræft, at den kører
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Den skal vise: `[DB] SQLite database ready` og `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurer nginx (omvendt proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generer SSL-certifikat (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
I Cloudflare-dashboardet:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Gå til **SSL/TLS → Origin Server**
|
||||
2. Klik på **Opret certifikat**
|
||||
3. Behold standardindstillingerne (15 år, \*.ditdomæne.com)
|
||||
4. Kopiér **Oprindelsescertifikatet** og den **Private nøgle**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-konfiguration
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Aktiver og test
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurer Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Tilføj DNS-post
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
I Cloudflare-dashboardet → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Skriv | Navn | Indhold | Fuldmagt |
|
||||
| ----- | ------ | ---------------------- | ----------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Fuldmagt |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurer SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Under **SSL/TLS → Oversigt**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Tilstand: **Fuld (streng)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Under **SSL/TLS → Edge-certifikater**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Brug altid HTTPS: ✅ Til
|
||||
- Minimum TLS-version: TLS 1.2
|
||||
- Automatiske HTTPS-omskrivninger: ✅ Til
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Test
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Drift og vedligeholdelse
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Opgrader til en ny version
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Se logfiler
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuel database backup
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Gendan fra backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Avanceret sikkerhed
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Begræns nginx til Cloudflare IP'er
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Tilføj følgende til `nginx.conf` inde i `http {}` blokken:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installer fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Bloker direkte adgang til Docker-porten
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implementer til Cloudflare-arbejdere (valgfrit)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
For fjernadgang via Cloudflare Workers (uden at eksponere VM'en direkte):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Se den fulde dokumentation på [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Portoversigt
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Havn | Service | Adgang |
|
||||
| ----- | ----------- | ------------------------- |
|
||||
| 22 | SSH | Offentlig (med fail2ban) |
|
||||
| 80 | nginx HTTP | Omdirigering → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Kun Localhost (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute – Bereitstellungshandbuch auf VM mit Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Vollständige Anleitung zur Installation und Konfiguration von OmniRoute auf einer VM (VPS) mit über Cloudflare verwalteter Domäne.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Voraussetzungen
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Artikel | Minimum | Empfohlen |
|
||||
| ------------------ | -------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Festplatte** | 10 GB SSD | 25 GB SSD |
|
||||
| **Betriebssystem** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domäne** | Registriert bei Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Getestete Anbieter**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurieren Sie die VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Erstellen Sie die Instanz
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Bei Ihrem bevorzugten VPS-Anbieter:
|
||||
|
||||
---
|
||||
- Wählen Sie Ubuntu 24.04 LTS
|
||||
- Wählen Sie den Mindestplan (1 vCPU / 1 GB RAM)
|
||||
- Legen Sie ein sicheres Root-Passwort fest oder konfigurieren Sie den SSH-Schlüssel
|
||||
- Notieren Sie sich die **öffentliche IP** (z. B. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Verbindung über SSH herstellen
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Aktualisieren Sie das System
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Docker installieren
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Nginx installieren
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Firewall (UFW) konfigurieren
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tipp**: Für maximale Sicherheit beschränken Sie die Ports 80 und 443 nur auf Cloudflare-IPs. Siehe den Abschnitt [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. OmniRoute installieren
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Konfigurationsverzeichnis erstellen
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Umgebungsvariablendatei erstellen
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **WICHTIG**: Generieren Sie einzigartige geheime Schlüssel! Verwenden Sie `openssl rand -hex 32` für jeden Schlüssel.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Starten Sie den Container
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Stellen Sie sicher, dass es ausgeführt wird
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Es sollte Folgendes anzeigen: `[DB] SQLite database ready` und `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Nginx (Reverse Proxy) konfigurieren
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 SSL-Zertifikat generieren (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Im Cloudflare-Dashboard:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Gehen Sie zu **SSL/TLS → Ursprungsserver**
|
||||
2. Klicken Sie auf **Zertifikat erstellen**
|
||||
3. Behalten Sie die Standardeinstellungen bei (15 Jahre, \*.yourdomain.com)
|
||||
4. Kopieren Sie das **Ursprungszertifikat** und den **Privaten Schlüssel**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-Konfiguration
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Aktivieren und testen
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurieren Sie Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 DNS-Eintrag hinzufügen
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Im Cloudflare-Dashboard → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Geben Sie | ein Name | Inhalt | Proxy |
|
||||
| --------- | -------- | ---------------------- | -------- |
|
||||
| A | `llms` | `203.0.113.10` (VM-IP) | ✅ Proxy |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 SSL konfigurieren
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Unter **SSL/TLS → Übersicht**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modus: **Vollständig (Streng)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Unter **SSL/TLS → Edge-Zertifikate**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Immer HTTPS verwenden: ✅ Ein
|
||||
- Mindest-TLS-Version: TLS 1.2
|
||||
- Automatische HTTPS-Rewrites: ✅ Ein
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testen
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Betrieb und Wartung
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Upgrade auf eine neue Version
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Protokolle anzeigen
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuelle Datenbanksicherung
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Aus Backup wiederherstellen
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Erweiterte Sicherheit
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Nginx auf Cloudflare-IPs beschränken
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Fügen Sie Folgendes zu `nginx.conf` im Block `http {}` hinzu:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Fail2ban installieren
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blockieren Sie den direkten Zugriff auf den Docker-Port
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Bereitstellung für Cloudflare-Worker (optional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Für den Fernzugriff über Cloudflare Workers (ohne die VM direkt verfügbar zu machen):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Die vollständige Dokumentation finden Sie unter [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Portzusammenfassung
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Hafen | Service | Zugriff |
|
||||
| ----- | ----------- | -------------------------- |
|
||||
| 22 | SSH | Öffentlich (mit fail2ban) |
|
||||
| 80 | nginx HTTP | Weiterleiten → HTTPS |
|
||||
| 443 | nginx HTTPS | Über Cloudflare-Proxy |
|
||||
| 20128 | OmniRoute | Nur Localhost (über Nginx) |
|
||||
|
||||
@@ -1,46 +1,44 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute: Guía de implementación en VM con Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Guía completa para instalar y configurar OmniRoute en una VM (VPS) con dominio administrado vía Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Requisitos previos
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Artículo | Mínimo | Recomendado |
|
||||
| -------------- | ------------------------ | --------------------- |
|
||||
| **procesador** | 1 CPU virtual | 2 CPU virtuales |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | SSD de 10 GB | SSD de 25 GB |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Dominio** | Registrado en Cloudflare | — |
|
||||
| **Acoplador** | Motor Docker 24+ | Ventana acoplable 27+ |
|
||||
|
||||
**Proveedores probados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configurar la máquina virtual
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Crear la instancia
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
En su proveedor VPS preferido:
|
||||
|
||||
---
|
||||
- Elija Ubuntu 24.04 LTS
|
||||
- Seleccione el plan mínimo (1 vCPU / 1 GB de RAM)
|
||||
- Establezca una contraseña de root segura o configure la clave SSH
|
||||
- Tenga en cuenta la **IP pública** (por ejemplo, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Conectarse vía SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Actualizar el sistema
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
@@ -49,14 +47,14 @@ apt update && apt upgrade -y
|
||||
### 1.4 Instalar Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
@@ -67,7 +65,7 @@ apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Configurar el cortafuegos (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Consejo**: Para máxima seguridad, restrinja los puertos 80 y 443 solo a las IP de Cloudflare. Consulte la sección [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Instalar OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Crear directorio de configuración
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Crear archivo de variables de entorno
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANTE**: ¡Genera claves secretas únicas! Utilice `openssl rand -hex 32` para cada clave.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Iniciar el contenedor
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verificar que esté funcionando
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Debería mostrar: `[DB] SQLite database ready` y `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configurar nginx (Proxy inverso)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generar certificado SSL (Origen Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
En el panel de Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Vaya a **SSL/TLS → Servidor de origen**
|
||||
2. Haga clic en **Crear certificado**
|
||||
3. Mantenga los valores predeterminados (15 años, \*.sudominio.com)
|
||||
4. Copie el **Certificado de Origen** y la **Clave Privada**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configuración de Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Habilitar y probar
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configurar DNS de Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Agregar registro DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
En el panel de Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tipo | Nombre | Contenido | Apoderado |
|
||||
| ---- | ------ | ----------------------------------------- | ------------ |
|
||||
| Un | `llms` | `203.0.113.10` (IP de la máquina virtual) | ✅ Apoderado |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
En **SSL/TLS → Descripción general**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modo: **Completo (Estricto)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
En **SSL/TLS → Certificados perimetrales**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Utilice siempre HTTPS: ✅ Activado
|
||||
- Versión mínima de TLS: TLS 1.2
|
||||
- Reescrituras HTTPS automáticas: ✅ Activado
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Pruebas
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Operaciones y Mantenimiento
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Actualizar a una nueva versión
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Ver registros
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Copia de seguridad manual de la base de datos
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restaurar desde copia de seguridad
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Seguridad avanzada
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restringir nginx a las IP de Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Agregue lo siguiente a `nginx.conf` dentro del bloque `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Instalar fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Bloquear el acceso directo al puerto Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implementación para trabajadores de Cloudflare (opcional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Para acceso remoto a través de Cloudflare Workers (sin exponer la VM directamente):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Consulte la documentación completa en [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Resumen de puerto
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Puerto | Servicio | Acceso |
|
||||
| ------ | ----------- | ---------------------------------- |
|
||||
| 22 | SSH | Público (con fail2ban) |
|
||||
| 80 | nginxHTTP | Redirigir → HTTPS |
|
||||
| 443 | nginx HTTPS | A través del proxy de Cloudflare |
|
||||
| 20128 | OmniRuta | Solo localhost (a través de nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Käyttöönottoopas VM:ssä Cloudflaren kanssa
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Täydellinen opas OmniRouten asentamiseen ja määrittämiseen VM:lle (VPS), jonka toimialuetta hallitaan Cloudflaren kautta.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Edellytykset
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Tuote | Minimi | Suositeltava |
|
||||
| ----------- | ------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 Gt | 2 Gt |
|
||||
| **Levy** | 10 Gt SSD | 25 Gt SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Rekisteröity Cloudflareen | — |
|
||||
| **Dokkeri** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Testatut palveluntarjoajat**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Määritä virtuaalikone
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Luo ilmentymä
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Valitsemallasi VPS-palveluntarjoajalla:
|
||||
|
||||
---
|
||||
- Valitse Ubuntu 24.04 LTS
|
||||
- Valitse vähimmäissuunnitelma (1 vCPU / 1 Gt RAM)
|
||||
- Aseta vahva root-salasana tai määritä SSH-avain
|
||||
- Huomaa **julkinen IP** (esim. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Yhdistä SSH:n kautta
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Päivitä järjestelmä
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Asenna Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Asenna nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Määritä palomuuri (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Vinkki**: Maksimaalista turvallisuutta varten rajaa portit 80 ja 443 vain Cloudflaren IP-osoitteisiin. Katso osio [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Asenna OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Luo asetushakemisto
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Luo ympäristömuuttujatiedosto
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **TÄRKEÄÄ**: Luo ainutlaatuisia salaisia avaimia! Käytä `openssl rand -hex 32` jokaiselle avaimelle.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Käynnistä kontti
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Varmista, että se on käynnissä
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Sen pitäisi näyttää: `[DB] SQLite database ready` ja `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Määritä nginx (käänteinen välityspalvelin)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Luo SSL-varmenne (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Cloudflare-hallintapaneelissa:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Siirry kohtaan **SSL/TLS → Origin Server**
|
||||
2. Napsauta **Luo varmenne**
|
||||
3. Säilytä oletusasetukset (15 vuotta, \*.omaverkkotunnus.com)
|
||||
4. Kopioi **alkuperätodistus** ja **yksityinen avain**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-kokoonpano
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Ota käyttöön ja testaa
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Määritä Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Lisää DNS-tietue
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Cloudflaren kojelaudassa → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tyyppi | Nimi | Sisältö | Välityspalvelin |
|
||||
| ------ | ------ | ---------------------- | ------------------ |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Välityspalvelin |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Määritä SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Kohdassa **SSL/TLS → Yleiskatsaus**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Tila: **Täysi (tiukka)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Alle **SSL/TLS → Edge-sertifikaatit**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Käytä aina HTTPS:ää: ✅ Käytössä
|
||||
- TLS:n vähimmäisversio: TLS 1.2
|
||||
- Automaattiset HTTPS-uudelleenkirjoitukset: ✅ Käytössä
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testaus
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Käyttö ja huolto
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Päivitä uuteen versioon
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Näytä lokit
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuaalinen tietokannan varmuuskopiointi
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Palauta varmuuskopiosta
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Lisäsuojaus
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Rajoita nginx Cloudflaren IP-osoitteisiin
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Lisää seuraava `nginx.conf` -lohkoon `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Asenna fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Estä suora pääsy Docker-porttiin
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Ota käyttöön Cloudflare-työntekijöille (valinnainen)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Etäkäyttö Cloudflare Workersin kautta (paljastamatta virtuaalikonetta suoraan):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Katso koko dokumentaatio osoitteessa [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Portin yhteenveto
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Portti | Palvelu | Pääsy |
|
||||
| ------ | ----------- | ----------------------------------- |
|
||||
| 22 | SSH | Julkinen (fail2banin kanssa) |
|
||||
| 80 | nginx HTTP | Uudelleenohjaus → HTTPS |
|
||||
| 443 | nginx HTTPS | Cloudflare-välityspalvelimen kautta |
|
||||
| 20128 | OmniRoute | Vain Localhost (nginxin kautta) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Guide de déploiement sur VM avec Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Guide complet pour installer et configurer OmniRoute sur une VM (VPS) avec domaine géré via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Prérequis
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Article | Minimum | Recommandé |
|
||||
| -------------- | ---------------------- | ---------------------- |
|
||||
| **processeur** | 1 processeur virtuel | 2 processeurs virtuels |
|
||||
| **RAM** | 1 Go | 2 Go |
|
||||
| **Disque** | Disque SSD de 10 Go | Disque SSD de 25 Go |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domaine** | Inscrit sur Cloudflare | — |
|
||||
| **Docker** | Moteur Docker 24+ | Docker 27+ |
|
||||
|
||||
**Fournisseurs testés** : Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configurer la VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Créer l'instance
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Sur votre fournisseur VPS préféré :
|
||||
|
||||
---
|
||||
- Choisissez Ubuntu 24.04 LTS
|
||||
- Sélectionnez le forfait minimum (1 vCPU / 1 Go de RAM)
|
||||
- Définissez un mot de passe root fort ou configurez la clé SSH
|
||||
- Notez l'**IP publique** (par exemple, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Connectez-vous via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Mettre à jour le système
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Installer Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installer nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Configurer le pare-feu (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Conseil** : Pour une sécurité maximale, limitez les ports 80 et 443 aux IP Cloudflare uniquement. Voir la section [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installez OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Créer un répertoire de configuration
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Créer un fichier de variables d'environnement
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANT** : Générez des clés secrètes uniques ! Utilisez `openssl rand -hex 32` pour chaque clé.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Démarrer le conteneur
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Vérifiez qu'il est en cours d'exécution
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Il doit afficher : `[DB] SQLite database ready` et `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configurer nginx (proxy inverse)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Générer un certificat SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Dans le tableau de bord Cloudflare :
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Accédez à **SSL/TLS → Serveur d'origine**
|
||||
2. Cliquez sur **Créer un certificat**
|
||||
3. Conservez les valeurs par défaut (15 ans, \*.votredomaine.com)
|
||||
4. Copiez le **Certificat d'origine** et la **Clé privée**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configuration de Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Activer et tester
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configurer le DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Ajouter un enregistrement DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Dans le tableau de bord Cloudflare → DNS :
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tapez | Nom | Contenu | Proxy |
|
||||
| ----- | ------ | ------------------------------------------- | ------------- |
|
||||
| Un | `llms` | `203.0.113.10` (IP de la machine virtuelle) | ✅ Mandataire |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Configurer SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Sous **SSL/TLS → Présentation** :
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mode : **Complet (strict)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Sous **SSL/TLS → Certificats Edge** :
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Utilisez toujours HTTPS : ✅ Activé
|
||||
-Version TLS minimale : TLS 1.2
|
||||
- Réécritures HTTPS automatiques : ✅ Activée
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Tests
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Exploitation et maintenance
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Mettre à niveau vers une nouvelle version
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Afficher les journaux
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Sauvegarde manuelle de la base de données
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restaurer à partir d'une sauvegarde
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Sécurité avancée
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restreindre nginx aux IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Ajoutez ce qui suit à `nginx.conf` à l'intérieur du bloc `http {}` :
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installer fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Bloquer l'accès direct au port Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Déployer sur Cloudflare Workers (facultatif)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Pour un accès à distance via Cloudflare Workers (sans exposer directement la VM) :
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Consultez la documentation complète sur [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Résumé des ports
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Services | Accès |
|
||||
| ----- | ----------- | -------------------------------- |
|
||||
| 22 | SSH | Public (avec fail2ban) |
|
||||
| 80 | nginx HTTP | Redirection → HTTPS |
|
||||
| 443 | nginx HTTPS | Via le proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Localhost uniquement (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — מדריך פריסה ב-VM עם Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
מדריך שלם להתקנה והגדרה של OmniRoute ב-VM (VPS) עם דומיין מנוהל באמצעות Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## דרישות מוקדמות
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| פריט | מינימום | מומלץ |
|
||||
| ---------- | ----------------- | ----------------- |
|
||||
| **מעבד** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **דיסק** | 10 GB SSD | SSD 25 GB |
|
||||
| **OS** | אובונטו 22.04 LTS | אובונטו 24.04 LTS |
|
||||
| **דומיין** | רשום ב-Cloudflare | — |
|
||||
| **דוקר** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**ספקים שנבדקו**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. הגדר את ה-VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 צור את המופע
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
בספק ה-VPS המועדף עליך:
|
||||
|
||||
---
|
||||
- בחר אובונטו 24.04 LTS
|
||||
- בחר את התוכנית המינימלית (1 vCPU / 1 GB RAM)
|
||||
- הגדר סיסמת שורש חזקה או הגדר את מפתח SSH
|
||||
- שימו לב ל-**IP הציבורי** (למשל, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 התחבר באמצעות SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 עדכן את המערכת
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 התקן Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 התקן את nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 הגדר חומת אש (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **טיפ**: לאבטחה מירבית, הגבל את היציאות 80 ו-443 ל-IP של Cloudflare בלבד. עיין בסעיף [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. התקן את OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 צור ספריית תצורה
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 צור קובץ משתני סביבה
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **חשוב**: צור מפתחות סודיים ייחודיים! השתמש ב-`openssl rand -hex 32` עבור כל מפתח.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 הפעל את המיכל
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 ודא שהוא פועל
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
זה אמור להציג: `[DB] SQLite database ready` ו-`listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. הגדר את nginx (פרוקסי הפוך)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 יצירת אישור SSL (מקור Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
בלוח המחוונים של Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. עבור אל **SSL/TLS → שרת מקור**
|
||||
2. לחץ על **צור אישור**
|
||||
3. שמור על ברירת המחדל (15 שנים, \*.yourdomain.com)
|
||||
4. העתק את **תעודת המקור** ואת **המפתח הפרטי**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 תצורת Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 הפעל ובדוק
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. הגדר את Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 הוסף רשומת DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
בלוח המחוונים של Cloudflare ← DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| הקלד | שם | תוכן | פרוקסי |
|
||||
| ---- | ------ | ---------------------- | --------- |
|
||||
| א | `llms` | `203.0.113.10` (VM IP) | ✅ פרוקסי |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 הגדר SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
תחת **SSL/TLS ← סקירה כללית**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- מצב: **מלא (קפדני)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
תחת **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- השתמש תמיד ב-HTTPS: ✅ פועל
|
||||
- גרסת TLS מינימלית: TLS 1.2
|
||||
- שכתובים אוטומטיים של HTTPS: ✅ פועל
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 בדיקה
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. תפעול ותחזוקה
|
||||
|
||||
### Atualizar para nova versão
|
||||
### שדרג לגרסה חדשה
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### הצג יומנים
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### גיבוי ידני של מסד הנתונים
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### שחזר מגיבוי
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. אבטחה מתקדמת
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### הגבל את nginx לכתובות IP של Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
הוסף את הדברים הבאים ל`nginx.conf` בתוך הבלוק `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### התקן fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### חסום גישה ישירה ליציאת Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. פריסה ל-Cloudflare Workers (אופציונלי)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
לגישה מרחוק דרך Cloudflare Workers (מבלי לחשוף ישירות את ה-VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
ראה את התיעוד המלא ב-[omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## סיכום יציאה
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| נמל | שירות | גישה |
|
||||
| ----- | ----------- | -------------------------- |
|
||||
| 22 | SSH | ציבורי (עם fail2ban) |
|
||||
| 80 | nginx HTTP | הפניה מחדש → HTTPS |
|
||||
| 443 | nginx HTTPS | דרך Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Localhost בלבד (דרך nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Telepítési útmutató a Cloudflare-rel rendelkező virtuális gépen
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Teljes útmutató az OmniRoute telepítéséhez és konfigurálásához Cloudflare-en keresztül kezelt tartományú virtuális gépen (VPS).
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Előfeltételek
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Tétel | Minimum | Ajánlott |
|
||||
| ----------- | ------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Lemez** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Regisztrálva a Cloudflare | — |
|
||||
| **Dokkoló** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Tesztelt szolgáltatók**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurálja a virtuális gépet
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Hozza létre a példányt
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
A választott VPS-szolgáltatónál:
|
||||
|
||||
---
|
||||
- Válassza az Ubuntu 24.04 LTS-t
|
||||
- Válassza ki a minimális csomagot (1 vCPU / 1 GB RAM)
|
||||
- Állítson be erős root jelszót vagy konfigurálja az SSH-kulcsot
|
||||
- Jegyezze fel a **nyilvános IP-címet** (pl. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Csatlakozás SSH-n keresztül
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Frissítse a rendszert
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Telepítse a Dockert
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Az nginx telepítése
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Tűzfal konfigurálása (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tipp**: A maximális biztonság érdekében korlátozza a 80-as és 443-as portot csak a Cloudflare IP-címekre. Lásd a [Advanced Security](#advanced-security) részt.
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Telepítse az OmniRoute programot
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Konfigurációs könyvtár létrehozása
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Környezeti változók fájl létrehozása
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **FONTOS**: Hozzon létre egyedi titkos kulcsokat! Minden kulcshoz használja az `openssl rand -hex 32` értéket.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Indítsa el a tárolót
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Ellenőrizze, hogy fut-e
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Meg kell jelennie: `[DB] SQLite database ready` és `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Az nginx (fordított proxy) konfigurálása
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 SSL-tanúsítvány generálása (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
A Cloudflare irányítópulton:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Nyissa meg az **SSL/TLS → Origin Server** lehetőséget.
|
||||
2. Kattintson a **Tanúsítvány létrehozása** lehetőségre.
|
||||
3. Tartsa meg az alapértelmezett értékeket (15 év, \*.sajatdomain.com)
|
||||
4. Másolja ki az **Eredeti tanúsítványt** és a **Privát kulcsot**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx konfiguráció
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Engedélyezés és tesztelés
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurálja a Cloudflare DNS-t
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 DNS-rekord hozzáadása
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
A Cloudflare irányítópulton → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Típus | Név | Tartalom | Proxy |
|
||||
| ----- | ------ | ---------------------- | ----------------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Meghatalmazott |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 SSL konfigurálása
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Az **SSL/TLS → Áttekintés** alatt:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mód: **Teljes (szigorú)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
**SSL/TLS → Edge Certificates** alatt:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Mindig használjon HTTPS-t: ✅ Be
|
||||
- Minimális TLS-verzió: TLS 1.2
|
||||
- Automatikus HTTPS-újraírások: ✅ Be
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Tesztelés
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Műveletek és karbantartás
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Frissítsen egy új verzióra
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Naplók megtekintése
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuális adatbázis-mentés
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Visszaállítás biztonsági másolatból
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Speciális biztonság
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Az nginx korlátozása a Cloudflare IP-címekre
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Adja hozzá a következőket a `nginx.conf` elemhez a `http {}` blokkon belül:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Telepítse a fail2ban-t
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### A Docker-porthoz való közvetlen hozzáférés letiltása
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Telepítés a Cloudflare Workers számára (opcionális)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
A Cloudflare Workersen keresztüli távoli eléréshez (a virtuális gép közvetlen feltárása nélkül):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Tekintse meg a teljes dokumentációt: [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Port összefoglaló
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Kikötő | Szolgáltatás | Hozzáférés |
|
||||
| ------ | ------------ | ----------------------------------- |
|
||||
| 22 | SSH | Nyilvános (fail2ban-nal) |
|
||||
| 80 | nginx HTTP | Átirányítás → HTTPS |
|
||||
| 443 | nginx HTTPS | Cloudflare Proxy segítségével |
|
||||
| 20128 | OmniRoute | Csak Localhost (nginx-en keresztül) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Panduan Penerapan pada VM dengan Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Panduan lengkap untuk menginstal dan mengkonfigurasi OmniRoute pada VM (VPS) dengan domain yang dikelola melalui Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Prasyarat
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Barang | Minimal | Direkomendasikan |
|
||||
| ------------------- | ----------------------- | ------------------- |
|
||||
| **CPU** | 1vCPU | 2vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | SSD 10 GB | SSD 25GB |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Terdaftar di Cloudflare | — |
|
||||
| **Buruh pelabuhan** | Mesin Docker 24+ | buruh pelabuhan 27+ |
|
||||
|
||||
**Penyedia teruji**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurasikan VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Membuat instance
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Pada penyedia VPS pilihan Anda:
|
||||
|
||||
---
|
||||
- Pilih Ubuntu 24.04 LTS
|
||||
- Pilih paket minimum (1 vCPU / 1 GB RAM)
|
||||
- Tetapkan kata sandi root yang kuat atau konfigurasikan kunci SSH
|
||||
- Catat **IP publik** (mis., `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Terhubung melalui SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Perbarui sistem
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Instal Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Instal nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurasi Firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tips**: Untuk keamanan maksimum, batasi port 80 dan 443 hanya untuk IP Cloudflare. Lihat bagian [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Instal OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Membuat direktori konfigurasi
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Membuat file variabel lingkungan
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **PENTING**: Hasilkan kunci rahasia unik! Gunakan `openssl rand -hex 32` untuk setiap kunci.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Mulai penampung
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Pastikan itu sedang berjalan
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Seharusnya menampilkan: `[DB] SQLite database ready` dan `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurasikan nginx (Proxy Terbalik)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Menghasilkan sertifikat SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Di dasbor Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Buka **SSL/TLS → Server Asal**
|
||||
2. Klik **Buat Sertifikat**
|
||||
3. Pertahankan default (15 tahun, \*.domainanda.com)
|
||||
4. Salin **Sertifikat Asal** dan **Kunci Pribadi**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Konfigurasi Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Aktifkan dan Uji
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurasikan DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Tambahkan data DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Di dasbor Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Ketik | Nama | Konten | Proksi |
|
||||
| ------ | ------ | ---------------------- | ----------- |
|
||||
| SEBUAH | `llms` | `203.0.113.10` (IP VM) | ✅ Diproksi |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurasikan SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Di bawah **SSL/TLS → Ikhtisar**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mode: **Penuh (Ketat)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Di bawah **SSL/TLS → Sertifikat Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Selalu Gunakan HTTPS: ✅ Aktif
|
||||
- Versi TLS Minimum: TLS 1.2
|
||||
- Penulisan Ulang HTTPS Otomatis: ✅ Aktif
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Pengujian
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Pengoperasian dan Pemeliharaan
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Tingkatkan ke versi baru
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Lihat log
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Pencadangan basis data manual
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Pulihkan dari cadangan
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Keamanan Tingkat Lanjut
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Batasi nginx ke IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Tambahkan yang berikut ini ke `nginx.conf` di dalam blok `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Instal fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blokir akses langsung ke port Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Deploy ke Cloudflare Worker (Opsional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Untuk akses jarak jauh melalui Cloudflare Workers (tanpa mengekspos VM secara langsung):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Lihat dokumentasi selengkapnya di [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Ringkasan Pelabuhan
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Pelabuhan | Layanan | Akses |
|
||||
| --------- | ----------- | ------------------------------- |
|
||||
| 22 | SSH | Publik (dengan fail2ban) |
|
||||
| 80 | nginx HTTP | Pengalihan → HTTPS |
|
||||
| 443 | nginx HTTPS | Melalui Proksi Cloudflare |
|
||||
| 20128 | OmniRoute | Hanya localhost (melalui nginx) |
|
||||
|
||||
@@ -1,73 +1,69 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# ओमनीरूट - क्लाउडफ्लेयर के साथ वीएम पर परिनियोजन गाइड
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
क्लाउडफ्लेयर के माध्यम से प्रबंधित डोमेन के साथ वीएम (वीपीएस) पर ओमनीरूट को स्थापित और कॉन्फ़िगर करने के लिए पूरी गाइड।
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## पूर्वावश्यकताएँ
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| आइटम | न्यूनतम | अनुशंसित |
|
||||
| ---------- | --------------------- | ------------------ |
|
||||
| **सीपीयू** | 1 वीसीपीयू | 2 वीसीपीयू |
|
||||
| **राम** | 1 जीबी | 2 जीबी |
|
||||
| **डिस्क** | 10 जीबी एसएसडी | 25 जीबी एसएसडी |
|
||||
| **ओएस** | उबंटू 22.04 एलटीएस | उबंटू 24.04 एलटीएस |
|
||||
| **डोमेन** | Cloudflare पर पंजीकृत | — |
|
||||
| **डॉकर** | डॉकर इंजन 24+ | डॉकर 27+ |
|
||||
|
||||
**परीक्षित प्रदाता**: अकामाई (लिनोड), डिजिटलओशन, वल्चर, हेट्ज़नर, एडब्ल्यूएस लाइटसेल।
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. वीएम को कॉन्फ़िगर करें
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 उदाहरण बनाएँ
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
आपके पसंदीदा VPS प्रदाता पर:
|
||||
|
||||
---
|
||||
- उबंटू 24.04 एलटीएस चुनें
|
||||
- न्यूनतम योजना चुनें (1 वीसीपीयू / 1 जीबी रैम)
|
||||
- एक मजबूत रूट पासवर्ड सेट करें या SSH कुंजी कॉन्फ़िगर करें
|
||||
- **सार्वजनिक आईपी** पर ध्यान दें (जैसे, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 एसएसएच के माध्यम से कनेक्ट करें
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 सिस्टम को अपडेट करें
|
||||
|
||||
**OMNI_टोकन_1**
|
||||
|
||||
### 1.4 डॉकर स्थापित करें
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 nginx स्थापित करें
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 फ़ायरवॉल कॉन्फ़िगर करें (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,53 +74,23 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **टिप**: अधिकतम सुरक्षा के लिए, पोर्ट 80 और 443 को केवल क्लाउडफ़ेयर आईपी तक सीमित रखें। [Advanced Security](#advanced-security) अनुभाग देखें।
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. ओमनीरूट स्थापित करें
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 कॉन्फ़िगरेशन निर्देशिका बनाएं
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
**OMNI_टोकन_5**
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 पर्यावरण चर फ़ाइल बनाएँ
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
**OMNI_टोकन_6**
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
NODE_ENV=production
|
||||
HOSTNAME=0.0.0.0
|
||||
DATA_DIR=/app/data
|
||||
STORAGE_DRIVER=sqlite
|
||||
ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
> ⚠️ **महत्वपूर्ण**: अद्वितीय गुप्त कुंजियाँ उत्पन्न करें! प्रत्येक कुंजी के लिए `openssl rand -hex 32` का उपयोग करें।
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 कंटेनर प्रारंभ करें
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +104,42 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 सत्यापित करें कि यह चल रहा है
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
**OMNI_टोकन_8**
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
इसे प्रदर्शित करना चाहिए: `[DB] SQLite database ready` और `listening on port 20128`।
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. nginx कॉन्फ़िगर करें (रिवर्स प्रॉक्सी)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 एसएसएल प्रमाणपत्र उत्पन्न करें (क्लाउडफ्लेयर ओरिजिन)
|
||||
|
||||
No painel da Cloudflare:
|
||||
क्लाउडफ्लेयर डैशबोर्ड में:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. **एसएसएल/टीएलएस → ओरिजिन सर्वर** पर जाएं
|
||||
2. **प्रमाणपत्र बनाएं** पर क्लिक करें
|
||||
3. डिफ़ॉल्ट रखें (15 वर्ष, \*.yourdomain.com)
|
||||
4. **मूल प्रमाणपत्र** और **निजी कुंजी** की प्रतिलिपि बनाएँ
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 नगनेक्स कॉन्फ़िगरेशन
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +155,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +173,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,180 +187,109 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 सक्षम करें और परीक्षण करें
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
**OMNI_टोकन_11**
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. क्लाउडफ्लेयर डीएनएस कॉन्फ़िगर करें
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 डीएनएस रिकॉर्ड जोड़ें
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
क्लाउडफ़ेयर डैशबोर्ड में → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| प्रकार | नाम | सामग्री | प्रॉक्सी |
|
||||
| ------ | ------ | ---------------------- | ----------- |
|
||||
| ए | `llms` | `203.0.113.10` (VM IP) | ✅ प्रॉक्सी |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 एसएसएल कॉन्फ़िगर करें
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
**एसएसएल/टीएलएस → अवलोकन** के अंतर्गत:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- मोड: **पूर्ण (सख्त)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
**एसएसएल/टीएलएस → एज सर्टिफिकेट** के अंतर्गत:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- हमेशा HTTPS का उपयोग करें: ✅ चालू
|
||||
- न्यूनतम टीएलएस संस्करण: टीएलएस 1.2
|
||||
- स्वचालित HTTPS पुनर्लेखन: ✅ चालू
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 परीक्षण
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
```
|
||||
**OMNI_टोकन_12**
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. संचालन एवं रखरखाव
|
||||
|
||||
### Atualizar para nova versão
|
||||
### नए संस्करण में अपग्रेड करें
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
docker stop omniroute && docker rm omniroute
|
||||
docker run -d --name omniroute --restart unless-stopped \
|
||||
--env-file /opt/omniroute/.env \
|
||||
-p 20128:20128 \
|
||||
-v omniroute-data:/app/data \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
**OMNI_टोकन_13**
|
||||
|
||||
### Ver logs
|
||||
### लॉग देखें
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
```
|
||||
**OMNI_टोकन_14**
|
||||
|
||||
### Backup manual do banco
|
||||
### मैनुअल डेटाबेस बैकअप
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
**OMNI_टोकन_15**
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
### बैकअप से पुनर्स्थापित करें
|
||||
|
||||
### Restaurar de backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
docker start omniroute
|
||||
```
|
||||
**OMNI_टोकन_16**
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. उन्नत सुरक्षा
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### nginx को Cloudflare IP तक सीमित करें
|
||||
|
||||
**OMNI_टोकन_17**
|
||||
|
||||
निम्नलिखित को `http {}` ब्लॉक के अंदर `nginx.conf` में जोड़ें:
|
||||
|
||||
**OMNI_टोकन_18**
|
||||
|
||||
### फेल2बैन स्थापित करें
|
||||
|
||||
**OMNI_टोकन_19**
|
||||
|
||||
### डॉकर पोर्ट तक सीधी पहुंच को अवरुद्ध करें
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
set_real_ip_from 103.22.200.0/22;
|
||||
set_real_ip_from 103.31.4.0/22;
|
||||
set_real_ip_from 141.101.64.0/18;
|
||||
set_real_ip_from 108.162.192.0/18;
|
||||
set_real_ip_from 190.93.240.0/20;
|
||||
set_real_ip_from 188.114.96.0/20;
|
||||
set_real_ip_from 197.234.240.0/22;
|
||||
set_real_ip_from 198.41.128.0/17;
|
||||
set_real_ip_from 162.158.0.0/15;
|
||||
set_real_ip_from 104.16.0.0/13;
|
||||
set_real_ip_from 104.24.0.0/14;
|
||||
set_real_ip_from 172.64.0.0/13;
|
||||
set_real_ip_from 131.0.72.0/22;
|
||||
real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. क्लाउडफ्लेयर श्रमिकों की तैनाती (वैकल्पिक)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
क्लाउडफ्लेयर वर्कर्स के माध्यम से रिमोट एक्सेस के लिए (वीएम को सीधे उजागर किए बिना):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
**OMNI_टोकन_21**
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
पूरा दस्तावेज़ [omnirouteCloud/README.md](../omnirouteCloud/README.md) पर देखें।
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## पोर्ट सारांश
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| बंदरगाह | सेवा | पहुंच |
|
||||
| ------- | ----------- | ----------------------------------- |
|
||||
| 22 | एसएसएच | सार्वजनिक (fail2ban के साथ) |
|
||||
| 80 | nginx HTTP | रीडायरेक्ट → HTTPS |
|
||||
| 443 | nginx HTTPS | क्लाउडफ्लेयर प्रॉक्सी के माध्यम से |
|
||||
| 20128 | ओमनीरूट | केवल लोकलहोस्ट (nginx के माध्यम से) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute: guida alla distribuzione su VM con Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Guida completa per installare e configurare OmniRoute su una VM (VPS) con dominio gestito tramite Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Prerequisiti
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Articolo | Minimo | Consigliato |
|
||||
| --------------------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 CPU virtuale | 2 vCPU |
|
||||
| **RAM** | 1GB | 2GB |
|
||||
| **Disco** | SSD da 10GB | SSD da 25GB |
|
||||
| **Sistema operativo** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Dominio** | Registrato su Cloudflare | — |
|
||||
| **Docker** | Motore Docker24+ | Docker27+ |
|
||||
|
||||
**Fornitori testati**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configura la VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Creare l'istanza
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Sul tuo provider VPS preferito:
|
||||
|
||||
---
|
||||
- Scegli Ubuntu 24.04 LTS
|
||||
- Seleziona il piano minimo (1 vCPU / 1 GB RAM)
|
||||
- Imposta una password root complessa o configura la chiave SSH
|
||||
- Prendi nota dell'**IP pubblico** (ad esempio, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Connetti tramite SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Aggiornare il sistema
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Installa Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installa nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Configurazione del firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Suggerimento**: per la massima sicurezza, limita le porte 80 e 443 solo agli IP Cloudflare. Consulta la sezione [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installa OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Creare la directory di configurazione
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Creare il file delle variabili d'ambiente
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANTE**: genera chiavi segrete uniche! Utilizza `openssl rand -hex 32` per ciascuna chiave.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Avviare il contenitore
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verificare che sia in esecuzione
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Dovrebbe essere visualizzato: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configura nginx (proxy inverso)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Genera certificato SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Nella dashboard di Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Vai su **SSL/TLS → Server di origine**
|
||||
2. Fai clic su **Crea certificato**
|
||||
3. Mantieni le impostazioni predefinite (15 anni, \*.tuodominio.com)
|
||||
4. Copia il **Certificato di Origine** e la **Chiave Privata**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configurazione Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Abilita e prova
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configura il DNS di Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Aggiungi record DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Nella dashboard di Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Digitare | Nome | Contenuto | Procura |
|
||||
| -------- | ------ | ------------------------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP della macchina virtuale) | ✅ Procura |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Configurare SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
In **SSL/TLS → Panoramica**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modalità: **Completa (Ristretta)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
In **SSL/TLS → Certificati Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Usa sempre HTTPS: ✅ Attivo
|
||||
- Versione TLS minima: TLS 1.2
|
||||
- Riscritture HTTPS automatiche: ✅ On
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Test
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Operazioni e manutenzione
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Aggiorna a una nuova versione
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Visualizza i registri
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Backup manuale del database
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Ripristina dal backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Sicurezza avanzata
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Limita nginx agli IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Aggiungi quanto segue a `nginx.conf` all'interno del blocco `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installa fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blocca l'accesso diretto alla porta Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Distribuzione ai dipendenti Cloudflare (facoltativo)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Per l'accesso remoto tramite Cloudflare Workers (senza esporre direttamente la VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Consulta la documentazione completa su [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Riepilogo delle porte
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Porto | Servizio | Accesso |
|
||||
| ----- | ----------- | -------------------------------- |
|
||||
| 22 | SSH | Pubblico (con fail2ban) |
|
||||
| 80 | nginxHTTP | Reindirizzamento → HTTPS |
|
||||
| 443 | nginx HTTPS | Tramite proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Solo host locale (tramite nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Cloudflare を使用した VM の導入ガイド
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Cloudflare経由でドメインが管理されているVM(VPS)にOmniRouteをインストールして構成するための完全なガイド。
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## 前提条件
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| アイテム | 最小 | おすすめ |
|
||||
| ------------ | -------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1GB | 2GB |
|
||||
| **ディスク** | 10GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **ドメイン** | Cloudflareに登録済み | — |
|
||||
| **ドッカー** | Docker エンジン 24+ | ドッカー 27+ |
|
||||
|
||||
**テスト済みプロバイダ**: Akamai (Linode)、DigitalOcean、Vultr、Hetzner、AWS Lightsail。
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. VM を構成する
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 インスタンスを作成する
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
好みの VPS プロバイダーで:
|
||||
|
||||
---
|
||||
- Ubuntu 24.04 LTS を選択します
|
||||
- 最小プラン (1 vCPU / 1 GB RAM) を選択します。
|
||||
- 強力な root パスワードを設定するか、SSH キーを構成します
|
||||
- **パブリック IP** に注意してください (例: `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 SSH 経由で接続する
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 システムをアップデートする
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Docker のインストール
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 nginx をインストールする
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 ファイアウォールの構成 (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **ヒント**: セキュリティを最大限に高めるには、ポート 80 と 443 を Cloudflare IP のみに制限します。 [Advanced Security](#advanced-security) セクションを参照してください。
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. OmniRoute をインストールする
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 構成ディレクトリの作成
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 環境変数ファイルの作成
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **重要**: 一意の秘密キーを生成してください。各キーに `openssl rand -hex 32` を使用します。
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 コンテナの起動
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 実行中であることを確認する
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
`[DB] SQLite database ready` および `listening on port 20128` と表示されます。
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. nginx (リバースプロキシ) の設定
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 SSL証明書の生成(Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Cloudflareダッシュボードで:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. **SSL/TLS → オリジンサーバー** に移動します。
|
||||
2. [**証明書の作成**] をクリックします。
|
||||
3. デフォルトのまま (15 年、\*.yourdomain.com)
|
||||
4. **送信元証明書**と**秘密キー**をコピーします。
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx の構成
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 有効化とテスト
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Cloudflare DNS を構成する
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 DNS レコードの追加
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Cloudflareダッシュボード → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| タイプ | 名前 | コンテンツ | プロキシ |
|
||||
| ------ | ------ | ---------------------- | ----------- |
|
||||
| あ | `llms` | `203.0.113.10` (VM IP) | ✅ プロキシ |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 SSL の構成
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
**SSL/TLS → 概要** の下:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- モード: **フル (厳密)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
**SSL/TLS → エッジ証明書** の下:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- 常に HTTPS を使用する: ✅ オン
|
||||
- 最小 TLS バージョン: TLS 1.2
|
||||
- 自動 HTTPS 書き換え: ✅ オン
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 テスト
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. 運用と保守
|
||||
|
||||
### Atualizar para nova versão
|
||||
### 新しいバージョンにアップグレードする
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### ログを表示する
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### データベースの手動バックアップ
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### バックアップから復元する
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. 高度なセキュリティ
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### nginx を Cloudflare IP に制限する
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
`http {}` ブロック内の `nginx.conf` に以下を追加します。
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
###fail2ban をインストールする
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Docker ポートへの直接アクセスをブロックする
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Cloudflare ワーカーへのデプロイ (オプション)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Cloudflare Workersを介したリモートアクセスの場合(VMを直接公開しない):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
[omnirouteCloud/README.md](../omnirouteCloud/README.md) で完全なドキュメントを参照してください。
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## ポートの概要
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| ポート | サービス | アクセス |
|
||||
| ------ | ------------ | ------------------------------- |
|
||||
| 22 | SSH | パブリック (fail2ban あり) |
|
||||
| 80 | nginx HTTP | リダイレクト → HTTPS |
|
||||
| 443 | nginx HTTPS | Cloudflare プロキシ経由 |
|
||||
| 20128 | オムニルート | ローカルホストのみ (nginx 経由) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Cloudflare를 사용한 VM 배포 가이드
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Cloudflare를 통해 관리되는 도메인이 있는 VM(VPS)에 OmniRoute를 설치하고 구성하기 위한 전체 가이드입니다.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## 전제조건
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| 아이템 | 최소 | 추천 |
|
||||
| ---------- | ------------------- | ---------------- |
|
||||
| **CPU** | vCPU 1개 | vCPU 2개 |
|
||||
| **램** | 1GB | 2GB |
|
||||
| **디스크** | 10GB SSD | 25GB SSD |
|
||||
| **OS** | 우분투 22.04 LTS | 우분투 24.04 LTS |
|
||||
| **도메인** | Cloudflare에 등록됨 | — |
|
||||
| **도커** | 도커 엔진 24+ | 도커 27+ |
|
||||
|
||||
**테스트된 공급자**: Akamai(Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. VM 구성
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 인스턴스 생성
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
선호하는 VPS 제공업체에서:
|
||||
|
||||
---
|
||||
- 우분투 24.04 LTS를 선택하세요
|
||||
- 최소 요금제 선택(vCPU 1개 / RAM 1GB)
|
||||
- 강력한 루트 비밀번호를 설정하거나 SSH 키를 구성하세요.
|
||||
- **공용 IP**(예: `203.0.113.10`)를 기록해 두세요.
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 SSH를 통해 연결
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 시스템 업데이트
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 도커 설치
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 nginx 설치
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 방화벽 구성(UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **팁**: 보안을 극대화하려면 포트 80과 443을 Cloudflare IP로만 제한하세요. [Advanced Security](#advanced-security) 섹션을 참조하세요.
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. OmniRoute 설치
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 구성 디렉터리 생성
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 환경변수 파일 생성
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **중요**: 고유한 비밀 키를 생성하세요! 각 키에 `openssl rand -hex 32`을 사용하세요.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 컨테이너 시작
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 실행 중인지 확인
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
`[DB] SQLite database ready` 및 `listening on port 20128`이 표시되어야 합니다.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. nginx(역방향 프록시) 구성
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 SSL 인증서 생성(Cloudflare 원본)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Cloudflare 대시보드에서:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. **SSL/TLS → 원본 서버**로 이동합니다.
|
||||
2. **인증서 만들기**를 클릭하세요.
|
||||
3. 기본값(15년, \*.yourdomain.com)을 유지합니다.
|
||||
4. **원본 인증서** 및 **개인 키**를 복사합니다.
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx 구성
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 활성화 및 테스트
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Cloudflare DNS 구성
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 DNS 레코드 추가
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Cloudflare 대시보드 → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| 유형 | 이름 | 내용 | 프록시 |
|
||||
| ---- | ------ | --------------------- | ----------- |
|
||||
| A | `llms` | `203.0.113.10`(VM IP) | ✅ 프록시됨 |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 SSL 구성
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
**SSL/TLS → 개요**에서:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- 모드: **전체(엄격)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
**SSL/TLS → 에지 인증서**에서:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- 항상 HTTPS 사용: ✅ 켜기
|
||||
- 최소 TLS 버전: TLS 1.2
|
||||
- 자동 HTTPS 재작성: ✅ 켜짐
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 테스트
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. 운영 및 유지 관리
|
||||
|
||||
### Atualizar para nova versão
|
||||
### 새 버전으로 업그레이드
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### 로그 보기
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### 수동 데이터베이스 백업
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### 백업에서 복원
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. 고급 보안
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### nginx를 Cloudflare IP로 제한
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
`http {}` 블록 내부의 `nginx.conf`에 다음을 추가합니다.
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Fail2ban 설치
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Docker 포트에 대한 직접 액세스 차단
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Cloudflare Workers에 배포(선택 사항)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Cloudflare Workers를 통한 원격 액세스의 경우(VM을 직접 노출하지 않고):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
[omnirouteCloud/README.md](../omnirouteCloud/README.md)에서 전체 문서를 참조하세요.
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## 포트 요약
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| 포트 | 서비스 | 액세스 |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| 22 | SSH | 공개(fail2ban 포함) |
|
||||
| 80 | nginx HTTP | 리디렉션 → HTTPS |
|
||||
| 443 | nginx HTTPS | Cloudflare 프록시를 통해 |
|
||||
| 20128 | 옴니루트 | 로컬호스트 전용(nginx를 통해) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Panduan Penggunaan pada VM dengan Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Panduan lengkap untuk memasang dan mengkonfigurasi OmniRoute pada VM (VPS) dengan domain yang diuruskan melalui Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Prasyarat
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Item | Minimum | Disyorkan |
|
||||
| ---------- | ----------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Cakera** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Berdaftar di Cloudflare | — |
|
||||
| **Docker** | Enjin Docker 24+ | Docker 27+ |
|
||||
|
||||
**Pembekal yang diuji**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurasikan VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Cipta contoh
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Pada pembekal VPS pilihan anda:
|
||||
|
||||
---
|
||||
- Pilih Ubuntu 24.04 LTS
|
||||
- Pilih pelan minimum (1 vCPU / 1 GB RAM)
|
||||
- Tetapkan kata laluan akar yang kuat atau konfigurasikan kunci SSH
|
||||
- Perhatikan **IP awam** (cth., `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Sambung melalui SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Kemas kini sistem
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Pasang Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Pasang nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurasi Firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Petua**: Untuk keselamatan maksimum, hadkan port 80 dan 443 kepada IP Cloudflare sahaja. Lihat bahagian [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Pasang OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Cipta direktori konfigurasi
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Cipta fail pembolehubah persekitaran
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **PENTING**: Jana kunci rahsia unik! Gunakan `openssl rand -hex 32` untuk setiap kunci.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Mulakan bekas
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Sahkan bahawa ia sedang berjalan
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Ia sepatutnya memaparkan: `[DB] SQLite database ready` dan `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurasikan nginx (Proksi Songsang)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Jana sijil SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Dalam papan pemuka Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Pergi ke **SSL/TLS → Pelayan Asal**
|
||||
2. Klik **Buat Sijil**
|
||||
3. Kekalkan lalai (15 tahun, \*.yourdomain.com)
|
||||
4. Salin **Sijil Asal** dan **Kunci Persendirian**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Konfigurasi Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Dayakan dan Uji
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurasikan Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Tambah rekod DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Dalam papan pemuka Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Taip | Nama | Kandungan | Proksi |
|
||||
| ---- | ------ | ---------------------- | ----------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Diproksi |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurasikan SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Di bawah **SSL/TLS → Gambaran Keseluruhan**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mod: **Penuh (Ketat)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Di bawah **SSL/TLS → Sijil Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Sentiasa Gunakan HTTPS: ✅ Hidup
|
||||
- Versi TLS minimum: TLS 1.2
|
||||
- Penulisan Semula HTTPS Automatik: ✅ Hidup
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Pengujian
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Operasi dan Penyelenggaraan
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Naik taraf kepada versi baharu
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Lihat log
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Sandaran pangkalan data manual
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Pulihkan daripada sandaran
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Keselamatan Lanjutan
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Hadkan nginx kepada IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Tambahkan yang berikut pada `nginx.conf` di dalam blok `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Pasang fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Sekat akses terus ke pelabuhan Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Sebarkan ke Cloudflare Workers (Pilihan)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Untuk akses jauh melalui Cloudflare Workers (tanpa mendedahkan VM secara langsung):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Lihat dokumentasi penuh di [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Ringkasan Pelabuhan
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Pelabuhan | Perkhidmatan | Akses |
|
||||
| --------- | ------------ | -------------------------------- |
|
||||
| 22 | SSH | Awam (dengan fail2ban) |
|
||||
| 80 | nginx HTTP | Ubah hala → HTTPS |
|
||||
| 443 | nginx HTTPS | Melalui Proksi Cloudflare |
|
||||
| 20128 | OmniRoute | Localhost sahaja (melalui nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Implementatiehandleiding op VM met Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Volledige gids voor het installeren en configureren van OmniRoute op een VM (VPS) met een domein beheerd via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Vereisten
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Artikel | Minimaal | Aanbevolen |
|
||||
| --------------------- | --------------------------- | --------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Schijf** | 10 GB SSD | 25 GB SSD |
|
||||
| **Besturingssysteem** | Ubuntu 22.04LTS | Ubuntu 24.04LTS |
|
||||
| **Domein** | Geregistreerd op Cloudflare | — |
|
||||
| **Dokker** | Docker-engine 24+ | Dokwerker 27+ |
|
||||
|
||||
**Geteste providers**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configureer de VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Maak het exemplaar
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Op uw favoriete VPS-provider:
|
||||
|
||||
---
|
||||
- Kies Ubuntu 24.04 LTS
|
||||
- Selecteer het minimale abonnement (1 vCPU / 1 GB RAM)
|
||||
- Stel een sterk rootwachtwoord in of configureer de SSH-sleutel
|
||||
- Noteer het **openbare IP** (bijvoorbeeld `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Verbinding maken via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Update het systeem
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Docker installeren
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installeer nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Firewall configureren (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tip**: Voor maximale veiligheid beperkt u poort 80 en 443 alleen tot Cloudflare IP's. Zie de sectie [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installeer OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Maak een configuratiedirectory
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Maak een bestand met omgevingsvariabelen
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **BELANGRIJK**: Genereer unieke geheime sleutels! Gebruik `openssl rand -hex 32` voor elke sleutel.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Start de container
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Controleer of het actief is
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Het zou moeten verschijnen: `[DB] SQLite database ready` en `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Nginx configureren (Reverse Proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 SSL-certificaat genereren (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
In het Cloudflare-dashboard:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Ga naar **SSL/TLS → Origin Server**
|
||||
2. Klik op **Certificaat maken**
|
||||
3. Behoud de standaardwaarden (15 jaar, \*.uwdomein.com)
|
||||
4. Kopieer het **Oorsprongscertificaat** en de **Privésleutel**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-configuratie
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Inschakelen en testen
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configureer Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 DNS-record toevoegen
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
In het Cloudflare-dashboard → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Typ | Naam | Inhoud | Proxy |
|
||||
| --- | ------ | ---------------------- | ---------------- |
|
||||
| Een | `llms` | `203.0.113.10` (VM-IP) | ✅ Gevolmachtigd |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 SSL configureren
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Onder **SSL/TLS → Overzicht**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modus: **Volledig (streng)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Onder **SSL/TLS → Edge-certificaten**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Gebruik altijd HTTPS: ✅ Aan
|
||||
- Minimale TLS-versie: TLS 1.2
|
||||
- Automatische HTTPS-herschrijvingen: ✅ Aan
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testen
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Bediening en onderhoud
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Upgrade naar een nieuwe versie
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Logboeken bekijken
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Handmatige databaseback-up
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Herstellen vanaf back-up
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Geavanceerde beveiliging
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Beperk nginx tot Cloudflare IP's
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Voeg het volgende toe aan `nginx.conf` in het blok `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installeer fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blokkeer directe toegang tot de Docker-poort
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implementeren naar Cloudflare-werknemers (optioneel)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Voor externe toegang via Cloudflare Workers (zonder de VM rechtstreeks bloot te leggen):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Bekijk de volledige documentatie op [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Poortsamenvatting
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Haven | Dienst | Toegang |
|
||||
| ----- | ----------- | ---------------------------- |
|
||||
| 22 | SSH | Openbaar (met fail2ban) |
|
||||
| 80 | nginx-HTTP | Omleiding → HTTPS |
|
||||
| 443 | nginx-HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Alleen Localhost (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Implementeringsveiledning på VM med Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Komplett veiledning for å installere og konfigurere OmniRoute på en VM (VPS) med domene administrert via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Forutsetninger
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Vare | Minimum | Anbefalt |
|
||||
| ---------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domene** | Registrert på Cloudflare | — |
|
||||
| **Dokker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Testede leverandører**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurer VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Opprett forekomsten
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
På din foretrukne VPS-leverandør:
|
||||
|
||||
---
|
||||
- Velg Ubuntu 24.04 LTS
|
||||
- Velg minimumsplanen (1 vCPU / 1 GB RAM)
|
||||
- Angi et sterkt root-passord eller konfigurer SSH-nøkkel
|
||||
– Legg merke til **offentlig IP** (f.eks. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Koble til via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Oppdater systemet
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Installer Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installer nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurer brannmur (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tips**: For maksimal sikkerhet, begrense portene 80 og 443 til bare Cloudflare IP-er. Se avsnittet [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installer OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Opprett konfigurasjonskatalog
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Lag miljøvariabler-fil
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **VIKTIG**: Generer unike hemmelige nøkler! Bruk `openssl rand -hex 32` for hver nøkkel.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Start beholderen
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Bekreft at den kjører
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Den skal vise: `[DB] SQLite database ready` og `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurer nginx (omvendt proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generer SSL-sertifikat (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
I Cloudflare-dashbordet:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Gå til **SSL/TLS → Origin Server**
|
||||
2. Klikk på **Opprett sertifikat**
|
||||
3. Behold standardinnstillingene (15 år, \*.dittdomene.com)
|
||||
4. Kopier **opprinnelsessertifikatet** og **privatnøkkelen**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-konfigurasjon
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Aktiver og test
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurer Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Legg til DNS-post
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
I Cloudflare-dashbordet → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Skriv inn | Navn | Innhold | Fullmakt |
|
||||
| --------- | ------ | ---------------------- | ----------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Fullmakt |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurer SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Under **SSL/TLS → Oversikt**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modus: **Full (Streng)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Under **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Bruk alltid HTTPS: ✅ På
|
||||
- Minimum TLS-versjon: TLS 1.2
|
||||
- Automatiske HTTPS-omskrivinger: ✅ På
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testing
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Drift og vedlikehold
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Oppgrader til en ny versjon
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Vis logger
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuell sikkerhetskopiering av database
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Gjenopprett fra sikkerhetskopi
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Avansert sikkerhet
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Begrens nginx til Cloudflare IP-er
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Legg til følgende til `nginx.conf` inne i `http {}`-blokken:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installer fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blokker direkte tilgang til Docker-porten
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Distribuer til Cloudflare-arbeidere (valgfritt)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
For ekstern tilgang via Cloudflare Workers (uten å eksponere VM direkte):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Se hele dokumentasjonen på [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Portsammendrag
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Service | Tilgang |
|
||||
| ----- | ----------- | -------------------------- |
|
||||
| 22 | SSH | Offentlig (med fail2ban) |
|
||||
| 80 | nginx HTTP | Omdirigere → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Kun lokal vert (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Gabay sa Deployment sa VM gamit ang Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Kumpletong gabay sa pag-install at pag-configure ng OmniRoute sa isang VM (VPS) na may domain na pinamamahalaan sa pamamagitan ng Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Mga kinakailangan
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| aytem | Pinakamababa | Inirerekomenda |
|
||||
| ---------- | -------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domain** | Nakarehistro sa Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Mga nasubok na provider**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. I-configure ang VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Lumikha ng instance
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Sa iyong gustong VPS provider:
|
||||
|
||||
---
|
||||
- Piliin ang Ubuntu 24.04 LTS
|
||||
- Piliin ang minimum na plano (1 vCPU / 1 GB RAM)
|
||||
- Magtakda ng malakas na root password o i-configure ang SSH key
|
||||
- Tandaan ang **pampublikong IP** (hal., `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Kumonekta sa pamamagitan ng SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 I-update ang system
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 I-install ang Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 I-install ang nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 I-configure ang Firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tip**: Para sa maximum na seguridad, paghigpitan ang mga port 80 at 443 sa mga Cloudflare IP lamang. Tingnan ang seksyong [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. I-install ang OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Lumikha ng direktoryo ng pagsasaayos
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Lumikha ng file ng mga variable ng kapaligiran
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **MAHALAGA**: Bumuo ng mga natatanging lihim na key! Gamitin ang `openssl rand -hex 32` para sa bawat key.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Simulan ang lalagyan
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 I-verify na ito ay tumatakbo
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Dapat itong magpakita ng: `[DB] SQLite database ready` at `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. I-configure ang nginx (Reverse Proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Bumuo ng SSL certificate (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Sa dashboard ng Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Pumunta sa **SSL/TLS → Origin Server**
|
||||
2. I-click ang **Gumawa ng Sertipiko**
|
||||
3. Panatilihin ang mga default (15 taon, \*.yourdomain.com)
|
||||
4. Kopyahin ang **Origin Certificate** at ang **Private Key**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configuration ng Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Paganahin at Pagsubok
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. I-configure ang Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Magdagdag ng DNS record
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Sa Cloudflare dashboard → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Uri | Pangalan | Nilalaman | Proxy |
|
||||
| ----- | -------- | ---------------------- | ---------- |
|
||||
| Isang | `llms` | `203.0.113.10` (VM IP) | ✅ Proxied |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 I-configure ang SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Sa ilalim ng **SSL/TLS → Pangkalahatang-ideya**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mode: **Buong (Mahigpit)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Sa ilalim ng **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Palaging Gumamit ng HTTPS: ✅ Naka-on
|
||||
- Minimum na Bersyon ng TLS: TLS 1.2
|
||||
- Mga Awtomatikong HTTPS Rewrite: ✅ Naka-on
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Pagsubok
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Mga Operasyon at Pagpapanatili
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Mag-upgrade sa bagong bersyon
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Tingnan ang mga log
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manu-manong backup ng database
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Ibalik mula sa backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Advanced na Seguridad
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Limitahan ang nginx sa mga Cloudflare IP
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Idagdag ang sumusunod sa `nginx.conf` sa loob ng `http {}` block:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### I-install ang fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### I-block ang direktang access sa Docker port
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. I-deploy sa Cloudflare Workers (Opsyonal)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Para sa malayuang pag-access sa pamamagitan ng Cloudflare Workers (nang hindi direktang inilalantad ang VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Tingnan ang buong dokumentasyon sa [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Buod ng Port
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Serbisyo | Access |
|
||||
| ----- | ----------- | ---------------------------------------- |
|
||||
| 22 | SSH | Pampubliko (na may fail2ban) |
|
||||
| 80 | nginx HTTP | I-redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Sa pamamagitan ng Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Localhost lang (sa pamamagitan ng nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Przewodnik wdrażania na maszynie wirtualnej z Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Kompletny przewodnik dotyczący instalacji i konfiguracji OmniRoute na maszynie wirtualnej (VPS) z domeną zarządzaną przez Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Warunki wstępne
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Pozycja | Minimalne | Polecane |
|
||||
| --------------------- | --------------------------- | --------------------- |
|
||||
| **Procesor** | 1 procesor wirtualny | 2 procesory wirtualne |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Dysk** | Dysk SSD 10 GB | Dysk SSD 25 GB |
|
||||
| **System operacyjny** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domena** | Zarejestrowany w Cloudflare | — |
|
||||
| **Doker** | Silnik Dockera 24+ | Doker 27+ |
|
||||
|
||||
**Testowani dostawcy**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Skonfiguruj maszynę wirtualną
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Utwórz instancję
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
U preferowanego dostawcy VPS:
|
||||
|
||||
---
|
||||
- Wybierz Ubuntu 24.04 LTS
|
||||
- Wybierz plan minimalny (1 vCPU / 1 GB RAM)
|
||||
- Ustaw silne hasło roota lub skonfiguruj klucz SSH
|
||||
- Zanotuj **publiczny adres IP** (np. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Połącz się przez SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Zaktualizuj system
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Zainstaluj Dockera
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Zainstaluj nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Skonfiguruj zaporę sieciową (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Wskazówka**: Dla maksymalnego bezpieczeństwa ogranicz porty 80 i 443 tylko do adresów IP Cloudflare. Zobacz sekcję [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Zainstaluj OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Utwórz katalog konfiguracyjny
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Utwórz plik zmiennych środowiskowych
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **WAŻNE**: Wygeneruj unikalne tajne klucze! Użyj `openssl rand -hex 32` dla każdego klucza.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Uruchom kontener
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Sprawdź, czy działa
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Powinien wyświetlić: `[DB] SQLite database ready` i `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Skonfiguruj nginx (odwrotny serwer proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Wygeneruj certyfikat SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
W panelu Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Przejdź do **SSL/TLS → Serwer Origin**
|
||||
2. Kliknij **Utwórz certyfikat**
|
||||
3. Zachowaj ustawienia domyślne (15 lat, \*.twojadomena.com)
|
||||
4. Skopiuj **Certyfikat pochodzenia** i **Klucz prywatny**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Konfiguracja Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Włącz i przetestuj
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Skonfiguruj DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Dodaj rekord DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
W panelu Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Wpisz | Imię | Treść | Pełnomocnik |
|
||||
| ----- | ------ | -------------------------------------- | ----------- |
|
||||
| | `llms` | `203.0.113.10` (IP maszyny wirtualnej) | ✅Przesłane |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Skonfiguruj SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
W obszarze **SSL/TLS → Przegląd**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Tryb: **Pełny (ścisły)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
W obszarze **SSL/TLS → Certyfikaty brzegowe**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Zawsze używaj protokołu HTTPS: ✅ Włącz
|
||||
- Minimalna wersja TLS: TLS 1.2
|
||||
- Automatyczne przepisywanie protokołu HTTPS: ✅ Włączone
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testowanie
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Obsługa i konserwacja
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Uaktualnij do nowej wersji
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Wyświetl logi
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Ręczna kopia zapasowa bazy danych
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Przywróć z kopii zapasowej
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Zaawansowane zabezpieczenia
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Ogranicz nginx do adresów IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Dodaj następujące polecenie do `nginx.conf` wewnątrz bloku `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Zainstaluj Fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Zablokuj bezpośredni dostęp do portu Dockera
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Wdróż do pracowników Cloudflare (opcjonalnie)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
W przypadku zdalnego dostępu za pośrednictwem Cloudflare Workers (bez bezpośredniego ujawniania maszyny wirtualnej):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Zobacz pełną dokumentację pod adresem [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Podsumowanie portu
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Usługa | Dostęp |
|
||||
| ----- | ----------- | ------------------------------ |
|
||||
| 22 | SSH | Publiczne (z funkcją Fail2ban) |
|
||||
| 80 | Nginx HTTP | Przekierowanie → HTTPS |
|
||||
| 443 | nginx HTTPS | Przez serwer proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Tylko Localhost (przez Nginx) |
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Guia de implantação em VM com Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Guia completo para instalar e configurar OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Pré-requisitos
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Artigo | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | --------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPUs |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | SSD de 10 GB | SSD de 25 GB |
|
||||
| **SO** | Ubuntu 22.04LTS | Ubuntu 24.04LTS |
|
||||
| **Domínio** | Registrado na Cloudflare | — |
|
||||
| **Docker** | Motor Docker 24+ | Docker 27+ |
|
||||
|
||||
**Provedores testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configure a VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Crie a instância
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
No seu provedor VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
- Defina uma senha root forte ou configure a chave SSH
|
||||
- Observe o **IP público** (por exemplo, `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
|
||||
@@ -46,22 +44,22 @@ ssh root@203.0.113.10
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Instalar o Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Instale o nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
@@ -78,11 +76,11 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Dica**: para segurança máxima, restrinja as portas 80 e 443 apenas aos IPs da Cloudflare. Consulte a seção [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Instale o OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
|
||||
@@ -93,14 +91,14 @@ mkdir -p /opt/omniroute
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas exclusivas! Use `openssl rand -hex 32` para cada chave.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Inicie o contêiner
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,7 +136,7 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verifique se está em execução
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
@@ -149,34 +147,34 @@ Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configurar nginx (proxy reverso)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Gerar certificado SSL (Origem Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Vá para **SSL/TLS → Servidor de Origem**
|
||||
2. Clique em **Criar certificado**
|
||||
3. Mantenha os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Certificado de Origem** e a **Chave Privada**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configuração Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Habilitar e testar
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configurar DNS da Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
No painel Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tipo | Nome | Conteúdo | Procuração |
|
||||
| ---- | ------ | ------------------------- | ------------ |
|
||||
| Um | `llms` | `203.0.113.10` (IP da VM) | ✅ Procurado |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Em **SSL/TLS → Visão geral**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modo: **Completo (estrito)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Em **SSL/TLS → Certificados Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Sempre use HTTPS: ✅ Ligado
|
||||
- Versão mínima do TLS: TLS 1.2
|
||||
- Reescritas automáticas de HTTPS: ✅ Ativado
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Teste
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Atualize para uma nova versão
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,30 +288,30 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Ver registros
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Backup manual do banco de dados
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restaurar do backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
@@ -321,11 +319,11 @@ docker start omniroute
|
||||
|
||||
## 6. Segurança Avançada
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restringir o nginx aos IPs da Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Adicione o seguinte a `nginx.conf` dentro do bloco `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Instale o fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Bloqueie o acesso direto à porta Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implantar em Cloudflare Workers (opcional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Veja a documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Resumo da porta
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Porto | Serviço | Acesso |
|
||||
| ----- | ----------- | ---------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | HTTP nginx | Redirecionar → HTTPS |
|
||||
| 443 | HTTPS nginx | Através do proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Apenas localhost (via nginx) |
|
||||
|
||||
@@ -1,38 +1,36 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Guia de implantação em VM com Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Guia completo para instalar e configurar OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Pré-requisitos
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Artigo | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | --------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPUs |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | SSD de 10 GB | SSD de 25 GB |
|
||||
| **SO** | Ubuntu 22.04LTS | Ubuntu 24.04LTS |
|
||||
| **Domínio** | Registrado na Cloudflare | — |
|
||||
| **Docker** | Motor Docker 24+ | Docker 27+ |
|
||||
|
||||
**Provedores testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configure a VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Crie a instância
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
No seu provedor VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
- Defina uma senha root forte ou configure a chave SSH
|
||||
- Observe o **IP público** (por exemplo, `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
|
||||
@@ -46,22 +44,22 @@ ssh root@203.0.113.10
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Instalar o Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Instale o nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
@@ -78,11 +76,11 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Dica**: para segurança máxima, restrinja as portas 80 e 443 apenas aos IPs da Cloudflare. Consulte a seção [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Instale o OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
|
||||
@@ -93,14 +91,14 @@ mkdir -p /opt/omniroute
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas exclusivas! Use `openssl rand -hex 32` para cada chave.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Inicie o contêiner
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,7 +136,7 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verifique se está em execução
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
@@ -149,34 +147,34 @@ Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configurar nginx (proxy reverso)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Gerar certificado SSL (Origem Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Vá para **SSL/TLS → Servidor de Origem**
|
||||
2. Clique em **Criar certificado**
|
||||
3. Mantenha os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Certificado de Origem** e a **Chave Privada**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configuração Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Habilitar e testar
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configurar DNS da Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
No painel Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tipo | Nome | Conteúdo | Procuração |
|
||||
| ---- | ------ | ------------------------- | ------------ |
|
||||
| Um | `llms` | `203.0.113.10` (IP da VM) | ✅ Procurado |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Em **SSL/TLS → Visão geral**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Modo: **Completo (estrito)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Em **SSL/TLS → Certificados Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Sempre use HTTPS: ✅ Ligado
|
||||
- Versão mínima do TLS: TLS 1.2
|
||||
- Reescritas automáticas de HTTPS: ✅ Ativado
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Teste
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Atualize para uma nova versão
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,30 +288,30 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Ver registros
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Backup manual do banco de dados
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restaurar do backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
@@ -321,11 +319,11 @@ docker start omniroute
|
||||
|
||||
## 6. Segurança Avançada
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restringir o nginx aos IPs da Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Adicione o seguinte a `nginx.conf` dentro do bloco `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Instale o fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Bloqueie o acesso direto à porta Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implantar em Cloudflare Workers (opcional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Veja a documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Resumo da porta
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Porto | Serviço | Acesso |
|
||||
| ----- | ----------- | ---------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | HTTP nginx | Redirecionar → HTTPS |
|
||||
| 443 | HTTPS nginx | Através do proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Apenas localhost (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Ghid de implementare pe VM cu Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Ghid complet pentru instalarea și configurarea OmniRoute pe o VM (VPS) cu domeniu gestionat prin Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Cerințe preliminare
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Articol | Minimum | Recomandat |
|
||||
| ----------- | ------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disc** | SSD de 10 GB | SSD de 25 GB |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domeniu** | Înregistrat pe Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Furnizori testați**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Configurați VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Creați instanța
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Pe furnizorul dvs. VPS preferat:
|
||||
|
||||
---
|
||||
- Alegeți Ubuntu 24.04 LTS
|
||||
- Selectați planul minim (1 vCPU / 1 GB RAM)
|
||||
- Setați o parolă de root puternică sau configurați cheia SSH
|
||||
- Rețineți **IP-ul public** (de exemplu, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Conectați-vă prin SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Actualizați sistemul
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Instalați Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Instalați nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Configurați firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Sfat**: pentru securitate maximă, restricționați porturile 80 și 443 doar la IP-uri Cloudflare. Consultați secțiunea [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Instalați OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Creați directorul de configurare
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Creați fișierul cu variabile de mediu
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **IMPORTANT**: Generați chei secrete unice! Folosiți `openssl rand -hex 32` pentru fiecare cheie.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Porniți containerul
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Verificați dacă rulează
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Ar trebui să afișeze: `[DB] SQLite database ready` și `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Configurați nginx (proxy invers)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generați certificat SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
În tabloul de bord Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Accesați **SSL/TLS → Origin Server**
|
||||
2. Faceți clic pe **Creați certificat**
|
||||
3. Păstrează valorile implicite (15 ani, \*.domeniul tău.com)
|
||||
4. Copiați **Certificatul de origine** și **Cheia privată**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Configurare Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Activare și testare
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Configurați Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Adăugați înregistrarea DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
În tabloul de bord Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Tip | Nume | Conținut | Proxy |
|
||||
| --- | ------ | ---------------------- | -------- |
|
||||
| A | `llms` | `203.0.113.10` (IP VM) | ✅ Proxy |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Configurați SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Sub **SSL/TLS → Prezentare generală**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Mod: **Complet (strict)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Sub **SSL/TLS → Certificate Edge**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Utilizați întotdeauna HTTPS: ✅ Activat
|
||||
- Versiune TLS minimă: TLS 1.2
|
||||
- Rescrieri automate HTTPS: ✅ Activat
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testare
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Operațiuni și întreținere
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Faceți upgrade la o versiune nouă
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Vizualizați jurnalele
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Backup manual al bazei de date
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Restaurați din backup
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Securitate avansată
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Restricționați nginx la IP-urile Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Adăugați următoarele la `nginx.conf` în interiorul blocului `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Instalați fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blocați accesul direct la portul Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Implementați la lucrătorii Cloudflare (opțional)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Pentru acces la distanță prin Cloudflare Workers (fără a expune VM-ul direct):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Consultați documentația completă la [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Rezumatul portului
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Port | Serviciu | Acces |
|
||||
| ----- | ----------- | ---------------------------- |
|
||||
| 22 | SSH | Public (cu fail2ban) |
|
||||
| 80 | nginx HTTP | Redirecționare → HTTPS |
|
||||
| 443 | nginx HTTPS | Prin Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Numai Localhost (prin nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Руководство по развертыванию на виртуальной машине с Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Полное руководство по установке и настройке OmniRoute на виртуальной машине (VPS) с доменом, управляемым через Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Предварительные условия
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Товар | Минимум | Рекомендуется |
|
||||
| --------- | ------------------------------- | -------------------- |
|
||||
| **ЦП** | 1 виртуальный ЦП | 2 виртуальных ЦП |
|
||||
| **ОЗУ** | 1 ГБ | 2 ГБ |
|
||||
| **Диск** | SSD-накопитель на 10 ГБ | SSD-накопитель 25 ГБ |
|
||||
| **ОС** | Убунту 22.04 ЛТС | Убунту 24.04 ЛТС |
|
||||
| **Домен** | Зарегистрировался на Cloudflare | — |
|
||||
| **Докер** | Докер-движок 24+ | Докер 27+ |
|
||||
|
||||
**Проверенные провайдеры**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Настройте виртуальную машину
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Создайте экземпляр
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
У предпочитаемого вами VPS-провайдера:
|
||||
|
||||
---
|
||||
- Выберите Ubuntu 24.04 LTS.
|
||||
- Выберите минимальный план (1 виртуальный ЦП / 1 ГБ ОЗУ)
|
||||
- Установите надежный пароль root или настройте ключ SSH.
|
||||
– Обратите внимание на **публичный IP-адрес** (например, `203.0.113.10`).
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Подключение через SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
###1.3 Обновить систему
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Установите Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Установите nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Настройка брандмауэра (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Совет**. Для максимальной безопасности ограничьте порты 80 и 443 только IP-адресами Cloudflare. См. раздел [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Установите OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Создание каталога конфигурации
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Создание файла переменных среды
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **ВАЖНО**: создавайте уникальные секретные ключи! Используйте `openssl rand -hex 32` для каждого ключа.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Запускаем контейнер
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Убедитесь, что он работает
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Должно отображаться: `[DB] SQLite database ready` и `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Настройте nginx (обратный прокси)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Создание SSL-сертификата (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
В панели управления Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Перейдите в **SSL/TLS → Исходный сервер**.
|
||||
2. Нажмите **Создать сертификат**.
|
||||
3. Оставьте настройки по умолчанию (15 лет, \*.yourdomain.com).
|
||||
4. Скопируйте **Сертификат происхождения** и **Закрытый ключ**.
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Конфигурация Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Включение и тестирование
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Настройте DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Добавление DNS-записи
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
В панели управления Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Тип | Имя | Содержание | Прокси |
|
||||
| --- | ------ | -------------------------------------------- | --------- |
|
||||
| А | `llms` | `203.0.113.10` (IP-адрес виртуальной машины) | ✅ Прокси |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Настройка SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
В разделе **SSL/TLS → Обзор**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Режим: **Полный (Строгий)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
В разделе **SSL/TLS → Пограничные сертификаты**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Всегда используйте HTTPS: ✅ Вкл.
|
||||
- Минимальная версия TLS: TLS 1.2.
|
||||
- Автоматическая перезапись HTTPS: ✅ Вкл.
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Тестирование
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Эксплуатация и техническое обслуживание
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Обновление до новой версии
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Просмотр журналов
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Резервное копирование базы данных вручную
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Восстановление из резервной копии
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Повышенная безопасность
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Ограничить nginx IP-адресами Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Добавьте следующее в `nginx.conf` внутри блока `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Установить Fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Блокируем прямой доступ к порту Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Развертывание в рабочих средах Cloudflare (необязательно)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Для удаленного доступа через Cloudflare Workers (без прямого доступа к виртуальной машине):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Полную документацию смотрите на странице [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Сводка портов
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Порт | Сервис | Доступ |
|
||||
| ----- | ----------- | ----------------------------------- |
|
||||
| 22 | СШ | Публичный (с Fail2ban) |
|
||||
| 80 | nginx HTTP | Перенаправление → HTTPS |
|
||||
| 443 | nginx HTTPS | Через прокси-сервер Cloudflare |
|
||||
| 20128 | ОмниРоут | Только локальный хост (через nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Sprievodca nasadením na VM s Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Kompletný sprievodca inštaláciou a konfiguráciou OmniRoute na VM (VPS) s doménou spravovanou cez Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Predpoklady
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Položka | Minimálne | Odporúčané |
|
||||
| ---------- | -------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Doména** | Registrovaný na Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Testovaní poskytovatelia**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Nakonfigurujte VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Vytvorte inštanciu
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
U preferovaného poskytovateľa VPS:
|
||||
|
||||
---
|
||||
- Vyberte Ubuntu 24.04 LTS
|
||||
- Vyberte minimálny plán (1 vCPU / 1 GB RAM)
|
||||
- Nastavte silné heslo root alebo nakonfigurujte kľúč SSH
|
||||
– Všimnite si **verejnú IP** (napr. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Pripojenie cez SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Aktualizujte systém
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Nainštalujte Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Nainštalujte nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurácia brány firewall (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tip**: Pre maximálnu bezpečnosť obmedzte porty 80 a 443 iba na IP adresy Cloudflare. Pozrite si časť [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Nainštalujte OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Vytvorte konfiguračný adresár
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Vytvorenie súboru premenných prostredia
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **DÔLEŽITÉ**: Vytvorte jedinečné tajné kľúče! Pre každý kľúč použite `openssl rand -hex 32`.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Spustite kontajner
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Overte, či beží
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Malo by sa zobraziť: `[DB] SQLite database ready` a `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurácia nginx (reverzný proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generovanie SSL certifikátu (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Na hlavnom paneli Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Prejdite na **SSL/TLS → Pôvodný server**
|
||||
2. Kliknite na **Vytvoriť certifikát**
|
||||
3. Ponechajte predvolené hodnoty (15 rokov, \*.yourdomain.com)
|
||||
4. Skopírujte **Certifikát o pôvode** a **Súkromný kľúč**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Konfigurácia Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Povoliť a otestovať
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Nakonfigurujte Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Pridať DNS záznam
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Na hlavnom paneli Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Typ | Meno | Obsah | Proxy |
|
||||
| --- | ------ | ---------------------- | ------------------ |
|
||||
| A | `llms` | `203.0.113.10` (IP VM) | ✅ Sprostredkovaný |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurácia SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
V časti **SSL/TLS → Prehľad**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Režim: **Plný (prísny)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
V časti **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Vždy používať HTTPS: ✅ Zap
|
||||
- Minimálna verzia TLS: TLS 1.2
|
||||
- Automatické prepisy HTTPS: ✅ Zap
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testovanie
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Prevádzka a údržba
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Inovujte na novú verziu
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Zobraziť denníky
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuálne zálohovanie databázy
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Obnoviť zo zálohy
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Pokročilé zabezpečenie
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Obmedzte nginx na IP adresy Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Pridajte nasledujúce do `nginx.conf` v rámci bloku `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Nainštalujte fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blokovať priamy prístup k portu Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Nasadenie pre pracovníkov Cloudflare (voliteľné)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Pre vzdialený prístup cez Cloudflare Workers (bez priameho odhalenia VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Úplnú dokumentáciu nájdete na stránke [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Súhrn portov
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Prístav | Služba | Prístup |
|
||||
| ------- | ----------- | ------------------------- |
|
||||
| 22 | SSH | Verejné (s fail2ban) |
|
||||
| 80 | nginx HTTP | Presmerovanie → HTTPS |
|
||||
| 443 | nginx HTTPS | Cez Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Iba Localhost (cez nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Implementeringsguide på virtuell dator med Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Komplett guide för att installera och konfigurera OmniRoute på en virtuell dator (VPS) med domän som hanteras via Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Förutsättningar
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Objekt | Minsta | Rekommenderas |
|
||||
| ---------- | ------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disk** | 10 GB SSD | 25 GB SSD |
|
||||
| **OS** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domän** | Registrerad på Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
|
||||
**Testade leverantörer**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Konfigurera den virtuella datorn
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Skapa instansen
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
På din föredragna VPS-leverantör:
|
||||
|
||||
---
|
||||
- Välj Ubuntu 24.04 LTS
|
||||
- Välj minimiplan (1 vCPU / 1 GB RAM)
|
||||
- Ställ in ett starkt root-lösenord eller konfigurera SSH-nyckel
|
||||
- Notera den **offentliga IP-adressen** (t.ex. `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Anslut via SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Uppdatera systemet
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Installera Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Installera nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Konfigurera brandvägg (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Tips**: För maximal säkerhet, begränsa portarna 80 och 443 till endast Cloudflare IP-adresser. Se avsnittet [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Installera OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Skapa konfigurationskatalog
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Skapa fil med miljövariabler
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **VIKTIGT**: Skapa unika hemliga nycklar! Använd `openssl rand -hex 32` för varje nyckel.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Starta behållaren
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Kontrollera att den körs
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Den ska visa: `[DB] SQLite database ready` och `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Konfigurera nginx (omvänd proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Generera SSL-certifikat (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
I Cloudflares instrumentpanel:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Gå till **SSL/TLS → Origin Server**
|
||||
2. Klicka på **Skapa certifikat**
|
||||
3. Behåll standardinställningarna (15 år, \*.dindomän.com)
|
||||
4. Kopiera **ursprungscertifikatet** och **privat nyckel**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx-konfiguration
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Aktivera och testa
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Konfigurera Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Lägg till DNS-post
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
I Cloudflares instrumentpanel → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Skriv | Namn | Innehåll | Proxy |
|
||||
| ----- | ------ | ---------------------- | ----------- |
|
||||
| A | `llms` | `203.0.113.10` (VM IP) | ✅ Fullmakt |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Konfigurera SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Under **SSL/TLS → Översikt**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Läge: **Fullständig (Strikt)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Under **SSL/TLS → Edge-certifikat**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Använd alltid HTTPS: ✅ På
|
||||
- Minsta TLS-version: TLS 1.2
|
||||
- Automatiska HTTPS-omskrivningar: ✅ På
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Testning
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Drift och underhåll
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Uppgradera till en ny version
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Visa loggar
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Manuell säkerhetskopiering av databas
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Återställ från säkerhetskopia
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Avancerad säkerhet
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Begränsa nginx till Cloudflare IP-adresser
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Lägg till följande till `nginx.conf` inuti `http {}`-blocket:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Installera fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Blockera direktåtkomst till Docker-porten
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Distribuera till Cloudflare-arbetare (valfritt)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
För fjärråtkomst via Cloudflare Workers (utan att exponera den virtuella datorn direkt):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Se hela dokumentationen på [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Portsammanfattning
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Hamn | Service | Tillgång |
|
||||
| ----- | ----------- | ---------------------------- |
|
||||
| 22 | SSH | Public (med fail2ban) |
|
||||
| 80 | nginx HTTP | Omdirigera → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Endast Localhost (via nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — คู่มือการปรับใช้บน VM พร้อม Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
คำแนะนำฉบับสมบูรณ์ในการติดตั้งและกำหนดค่า OmniRoute บน VM (VPS) ด้วยโดเมนที่จัดการผ่าน Cloudflare
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## ข้อกำหนดเบื้องต้น
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| รายการ | ขั้นต่ำ | แนะนำ |
|
||||
| ------------------ | -------------------------- | ----------------- |
|
||||
| **ซีพียู** | 1 vCPU | 2 vCPU |
|
||||
| **แรม** | 1 กิกะไบต์ | 2 กิกะไบต์ |
|
||||
| **ดิสก์** | SSD 10GB | 25 GB SSD |
|
||||
| **ระบบปฏิบัติการ** | อูบุนตู 22.04 LTS | อูบุนตู 24.04 LTS |
|
||||
| **โดเมน** | ลงทะเบียนบน Cloudflare | — |
|
||||
| **นักเทียบท่า** | นักเทียบท่าเครื่องยนต์ 24+ | นักเทียบท่า 27+ |
|
||||
|
||||
**ผู้ให้บริการที่ผ่านการทดสอบ**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. กำหนดค่า VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 สร้างอินสแตนซ์
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
บนผู้ให้บริการ VPS ที่คุณต้องการ:
|
||||
|
||||
---
|
||||
- เลือก Ubuntu 24.04 LTS
|
||||
- เลือกแผนขั้นต่ำ (1 vCPU / 1 GB RAM)
|
||||
- ตั้งรหัสผ่านรูทที่รัดกุมหรือกำหนดค่าคีย์ SSH
|
||||
- หมายเหตุ **IP สาธารณะ** (เช่น `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 เชื่อมต่อผ่าน SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 อัพเดตระบบ
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 ติดตั้งนักเทียบท่า
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 ติดตั้ง nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 กำหนดค่าไฟร์วอลล์ (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **เคล็ดลับ**: เพื่อความปลอดภัยสูงสุด จำกัดพอร์ต 80 และ 443 ไว้เฉพาะ IP ของ Cloudflare เท่านั้น ดูส่วน [Advanced Security](#advanced-security)
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. ติดตั้ง OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 สร้างไดเร็กทอรีการกำหนดค่า
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 สร้างไฟล์ตัวแปรสภาพแวดล้อม
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **สำคัญ**: สร้างคีย์ลับที่ไม่ซ้ำใคร! ใช้ `openssl rand -hex 32` สำหรับแต่ละคีย์
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 เริ่มคอนเทนเนอร์
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 ตรวจสอบว่ามันกำลังทำงานอยู่
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
ควรแสดง: `[DB] SQLite database ready` และ `listening on port 20128`
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. กำหนดค่า nginx (Reverse Proxy)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 สร้างใบรับรอง SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
ในแดชบอร์ด Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. ไปที่ **SSL/TLS → เซิร์ฟเวอร์ต้นทาง**
|
||||
2. คลิก **สร้างใบรับรอง**
|
||||
3. คงค่าเริ่มต้นไว้ (15 ปี \*.yourdomain.com)
|
||||
4. คัดลอก **Origin Certificate** และ **Private Key**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 การกำหนดค่า Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 เปิดใช้งานและทดสอบ
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. กำหนดค่า Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 เพิ่มบันทึก DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
ในแดชบอร์ด Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| พิมพ์ | ชื่อ | เนื้อหา | หนังสือมอบฉันทะ |
|
||||
| ----- | ------ | ---------------------- | --------------- |
|
||||
| ก | `llms` | `203.0.113.10` (VM IP) | ✅ พร็อกซี |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 กำหนดค่า SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
ภายใต้ **SSL/TLS → ภาพรวม**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- โหมด: **เต็ม (เข้มงวด)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
ภายใต้ **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- ใช้ HTTPS เสมอ: ✅ เปิด
|
||||
- เวอร์ชัน TLS ขั้นต่ำ: TLS 1.2
|
||||
- การเขียน HTTPS อัตโนมัติ: ✅เปิด
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 การทดสอบ
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. การดำเนินงานและการบำรุงรักษา
|
||||
|
||||
### Atualizar para nova versão
|
||||
### อัปเกรดเป็นเวอร์ชันใหม่
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### ดูบันทึก
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### สำรองฐานข้อมูลด้วยตนเอง
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### กู้คืนจากข้อมูลสำรอง
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. การรักษาความปลอดภัยขั้นสูง
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### จำกัด nginx ไว้ที่ IP ของ Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
เพิ่มสิ่งต่อไปนี้ใน `nginx.conf` ภายในบล็อก `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### ติดตั้ง Fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### บล็อกการเข้าถึงพอร์ต Docker โดยตรง
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. ปรับใช้กับ Cloudflare Workers (ไม่บังคับ)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
สำหรับการเข้าถึงระยะไกลผ่าน Cloudflare Workers (โดยไม่ต้องเปิดเผย VM โดยตรง):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
ดูเอกสารฉบับเต็มได้ที่ [omnirouteCloud/README.md](../omnirouteCloud/README.md)
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## สรุปพอร์ต
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| พอร์ต | บริการ | เข้าถึง |
|
||||
| ----- | ----------- | ------------------------------- |
|
||||
| 22 | เอสเอสเอช | สาธารณะ (พร้อม Fail2ban) |
|
||||
| 80 | nginx HTTP | เปลี่ยนเส้นทาง → HTTPS |
|
||||
| 443 | nginx HTTPS | ผ่าน Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Localhost เท่านั้น (ผ่าน nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — посібник із розгортання на віртуальній машині з Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Повний посібник із встановлення та налаштування OmniRoute на віртуальній машині (VPS) із доменом, керованим через Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Передумови
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Пункт | Мінімум | Рекомендовано |
|
||||
| --------- | --------------------------- | ---------------- |
|
||||
| **ЦП** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 Гб | 2 ГБ |
|
||||
| **Диск** | 10 ГБ SSD | 25 ГБ SSD |
|
||||
| **ОС** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Домен** | Зареєстровано на Cloudflare | — |
|
||||
| **Докер** | Docker Engine 24+ | Докер 27+ |
|
||||
|
||||
**Перевірені постачальники**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Налаштуйте віртуальну машину
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Створіть екземпляр
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
У бажаного постачальника VPS:
|
||||
|
||||
---
|
||||
- Виберіть Ubuntu 24.04 LTS
|
||||
- Виберіть мінімальний план (1 vCPU / 1 GB RAM)
|
||||
- Встановіть надійний пароль root або налаштуйте ключ SSH
|
||||
- Зверніть увагу на **публічну IP** (наприклад, `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Підключіться через SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Оновіть систему
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Встановіть Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Встановіть nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Налаштувати брандмауер (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Порада**: для максимальної безпеки обмежте порти 80 і 443 лише IP-адресами Cloudflare. Перегляньте розділ [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Встановіть OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Створіть каталог конфігурації
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Створіть файл змінних середовища
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **ВАЖЛИВО**: генеруйте унікальні секретні ключі! Використовуйте `openssl rand -hex 32` для кожного ключа.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Запустіть контейнер
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Переконайтеся, що він працює
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Має відображатися: `[DB] SQLite database ready` та `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Налаштувати nginx (зворотний проксі)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Створення сертифіката SSL (Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
На інформаційній панелі Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Перейдіть до **SSL/TLS → Оригінальний сервер**
|
||||
2. Натисніть **Створити сертифікат**
|
||||
3. Зберігайте значення за умовчанням (15 років, \*.yourdomain.com)
|
||||
4. Скопіюйте **Сертифікат походження** та **Приватний ключ**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Конфігурація Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Увімкнути та перевірити
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Налаштуйте DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Додайте запис DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
На інформаційній панелі Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Тип | Ім'я | Зміст | Проксі |
|
||||
| --- | ------ | --------------------------------------------- | --------- |
|
||||
| A | `llms` | `203.0.113.10` (IP-адреса віртуальної машини) | ✅ Проксі |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Налаштувати SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
У розділі **SSL/TLS → Огляд**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Режим: **Повний (Строгий)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
У розділі **SSL/TLS → Edge Certificates**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Завжди використовувати HTTPS: ✅ Увімк
|
||||
- Мінімальна версія TLS: TLS 1.2
|
||||
- Автоматичне перезапис HTTPS: ✅ Увімкнено
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Тестування
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Експлуатація та технічне обслуговування
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Оновлення до нової версії
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Переглянути журнали
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Резервне копіювання бази даних вручну
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Відновити з резервної копії
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Розширений захист
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Обмежити nginx IP-адресами Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Додайте наступне до `nginx.conf` всередині блоку `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Встановити fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Заблокувати прямий доступ до порту Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Розгортання в Cloudflare Workers (необов’язково)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Для віддаленого доступу через Cloudflare Workers (без безпосереднього доступу до віртуальної машини):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Перегляньте повну документацію за адресою [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Короткий опис порту
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Порт | Сервіс | Доступ |
|
||||
| ----- | ----------- | --------------------------------- |
|
||||
| 22 | SSH | Загальнодоступний (з fail2ban) |
|
||||
| 80 | nginx HTTP | Перенаправлення → HTTPS |
|
||||
| 443 | nginx HTTPS | Через проксі Cloudflare |
|
||||
| 20128 | OmniRoute | Лише локальний хост (через nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — Hướng dẫn triển khai trên VM với Cloudflare
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
Hướng dẫn đầy đủ để cài đặt và định cấu hình OmniRoute trên VM (VPS) với miền được quản lý qua Cloudflare.
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## Điều kiện tiên quyết
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| Mục | Tối thiểu | Được đề xuất |
|
||||
| ---------- | -------------------------- | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Đĩa** | SSD 10GB | SSD 25 GB |
|
||||
| **HĐH** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Miền** | Đã đăng ký trên Cloudflare | — |
|
||||
| **Docker** | Công cụ Docker 24+ | Docker 27+ |
|
||||
|
||||
**Các nhà cung cấp đã được thử nghiệm**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1. Cấu hình VM
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 Tạo phiên bản
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
Trên nhà cung cấp VPS ưa thích của bạn:
|
||||
|
||||
---
|
||||
- Chọn Ubuntu 24.04 LTS
|
||||
- Chọn gói tối thiểu (1 vCPU / 1 GB RAM)
|
||||
- Đặt mật khẩu root mạnh hoặc định cấu hình khóa SSH
|
||||
- Lưu ý **IP công cộng** (ví dụ: `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 Kết nối qua SSH
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 Cập nhật hệ thống
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 Cài đặt Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 Cài đặt nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 Cấu hình tường lửa (UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **Mẹo**: Để bảo mật tối đa, hãy hạn chế cổng 80 và 443 đối với IP Cloudflare. Xem phần [Advanced Security](#advanced-security).
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. Cài đặt OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 Tạo thư mục cấu hình
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 Tạo tệp biến môi trường
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **QUAN TRỌNG**: Tạo các khóa bí mật duy nhất! Sử dụng `openssl rand -hex 32` cho mỗi khóa.
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 Khởi động container
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 Xác minh rằng nó đang chạy
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
Nó sẽ hiển thị: `[DB] SQLite database ready` và `listening on port 20128`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3. Cấu hình nginx (Proxy ngược)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 Tạo chứng chỉ SSL (Nguồn gốc Cloudflare)
|
||||
|
||||
No painel da Cloudflare:
|
||||
Trong bảng điều khiển Cloudflare:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. Đi tới **SSL/TLS → Máy chủ gốc**
|
||||
2. Nhấp vào **Tạo chứng chỉ**
|
||||
3. Giữ nguyên giá trị mặc định (15 năm, \*.yourdomain.com)
|
||||
4. Sao chép **Chứng chỉ xuất xứ** và **Khóa riêng**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Cấu hình Nginx
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 Kích hoạt và kiểm tra
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4. Cấu hình DNS Cloudflare
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 Thêm bản ghi DNS
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
Trong bảng điều khiển Cloudflare → DNS:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| Loại | Tên | Nội dung | Ủy nhiệm |
|
||||
| ---- | ------ | ---------------------- | ---------------- |
|
||||
| A | `llms` | `203.0.113.10` (IP VM) | ✅ Được ủy quyền |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 Định cấu hình SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
Trong **SSL/TLS → Tổng quan**:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- Chế độ: **Đầy đủ (Nghiêm ngặt)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
Trong **SSL/TLS → Chứng chỉ biên**:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- Luôn sử dụng HTTPS: ✅ Bật
|
||||
- Phiên bản TLS tối thiểu: TLS 1.2
|
||||
- Tự động ghi lại HTTPS: ✅ Bật
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 Kiểm tra
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. Vận hành và bảo trì
|
||||
|
||||
### Atualizar para nova versão
|
||||
### Nâng cấp lên phiên bản mới
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### Xem nhật ký
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### Sao lưu cơ sở dữ liệu thủ công
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### Khôi phục từ bản sao lưu
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. Bảo mật nâng cao
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### Hạn chế nginx đối với IP Cloudflare
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
Thêm phần sau vào `nginx.conf` bên trong khối `http {}`:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### Cài đặt failed2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### Chặn quyền truy cập trực tiếp vào cổng Docker
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. Triển khai lên Cloudflare Workers (Tùy chọn)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
Để truy cập từ xa thông qua Cloudflare Workers (không để lộ trực tiếp VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
Xem tài liệu đầy đủ tại [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## Tóm tắt cổng
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| Cảng | Dịch vụ | Truy cập |
|
||||
| ----- | ----------- | ------------------------------- |
|
||||
| 22 | SSH | Công khai (với Fail2ban) |
|
||||
| 80 | nginx HTTP | Chuyển hướng → HTTPS |
|
||||
| 443 | nginx HTTPS | Qua Proxy Cloudflare |
|
||||
| 20128 | OmniRoute | Chỉ Localhost (thông qua nginx) |
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
🌐 **Languages:** 🇺🇸 [English](../../README.md) · 🇧🇷 [pt-BR](../pt-BR/VM_DEPLOYMENT_GUIDE.md) · 🇪🇸 [es](../es/VM_DEPLOYMENT_GUIDE.md) · 🇫🇷 [fr](../fr/VM_DEPLOYMENT_GUIDE.md) · 🇩🇪 [de](../de/VM_DEPLOYMENT_GUIDE.md) · 🇮🇹 [it](../it/VM_DEPLOYMENT_GUIDE.md) · 🇷🇺 [ru](../ru/VM_DEPLOYMENT_GUIDE.md) · 🇨🇳 [zh-CN](../zh-CN/VM_DEPLOYMENT_GUIDE.md) · 🇯🇵 [ja](../ja/VM_DEPLOYMENT_GUIDE.md) · 🇰🇷 [ko](../ko/VM_DEPLOYMENT_GUIDE.md) · 🇸🇦 [ar](../ar/VM_DEPLOYMENT_GUIDE.md) · 🇮🇳 [in](../in/VM_DEPLOYMENT_GUIDE.md) · 🇹🇭 [th](../th/VM_DEPLOYMENT_GUIDE.md) · 🇻🇳 [vi](../vi/VM_DEPLOYMENT_GUIDE.md) · 🇮🇩 [id](../id/VM_DEPLOYMENT_GUIDE.md) · 🇲🇾 [ms](../ms/VM_DEPLOYMENT_GUIDE.md) · 🇳🇱 [nl](../nl/VM_DEPLOYMENT_GUIDE.md) · 🇵🇱 [pl](../pl/VM_DEPLOYMENT_GUIDE.md) · 🇸🇪 [sv](../sv/VM_DEPLOYMENT_GUIDE.md) · 🇳🇴 [no](../no/VM_DEPLOYMENT_GUIDE.md) · 🇩🇰 [da](../da/VM_DEPLOYMENT_GUIDE.md) · 🇫🇮 [fi](../fi/VM_DEPLOYMENT_GUIDE.md) · 🇵🇹 [pt](../pt/VM_DEPLOYMENT_GUIDE.md) · 🇷🇴 [ro](../ro/VM_DEPLOYMENT_GUIDE.md) · 🇭🇺 [hu](../hu/VM_DEPLOYMENT_GUIDE.md) · 🇧🇬 [bg](../bg/VM_DEPLOYMENT_GUIDE.md) · 🇸🇰 [sk](../sk/VM_DEPLOYMENT_GUIDE.md) · 🇺🇦 [uk-UA](../uk-UA/VM_DEPLOYMENT_GUIDE.md) · 🇮🇱 [he](../he/VM_DEPLOYMENT_GUIDE.md) · 🇵🇭 [phi](../phi/VM_DEPLOYMENT_GUIDE.md)
|
||||
# OmniRoute — 使用 Cloudflare 在虚拟机上部署指南
|
||||
|
||||
🌐 **Languages:** 🇺🇸 [English](../../VM_DEPLOYMENT_GUIDE.md) | 🇧🇷 [Português (Brasil)](../pt-BR/VM_DEPLOYMENT_GUIDE.md) | 🇪🇸 [Español](../es/VM_DEPLOYMENT_GUIDE.md) | 🇫🇷 [Français](../fr/VM_DEPLOYMENT_GUIDE.md) | 🇮🇹 [Italiano](../it/VM_DEPLOYMENT_GUIDE.md) | 🇷🇺 [Русский](../ru/VM_DEPLOYMENT_GUIDE.md) | 🇨🇳 [中文 (简体)](../zh-CN/VM_DEPLOYMENT_GUIDE.md) | 🇩🇪 [Deutsch](../de/VM_DEPLOYMENT_GUIDE.md) | 🇮🇳 [हिन्दी](../in/VM_DEPLOYMENT_GUIDE.md) | 🇹🇭 [ไทย](../th/VM_DEPLOYMENT_GUIDE.md) | 🇺🇦 [Українська](../uk-UA/VM_DEPLOYMENT_GUIDE.md) | 🇸🇦 [العربية](../ar/VM_DEPLOYMENT_GUIDE.md) | 🇯🇵 [日本語](../ja/VM_DEPLOYMENT_GUIDE.md) | 🇻🇳 [Tiếng Việt](../vi/VM_DEPLOYMENT_GUIDE.md) | 🇧🇬 [Български](../bg/VM_DEPLOYMENT_GUIDE.md) | 🇩🇰 [Dansk](../da/VM_DEPLOYMENT_GUIDE.md) | 🇫🇮 [Suomi](../fi/VM_DEPLOYMENT_GUIDE.md) | 🇮🇱 [עברית](../he/VM_DEPLOYMENT_GUIDE.md) | 🇭🇺 [Magyar](../hu/VM_DEPLOYMENT_GUIDE.md) | 🇮🇩 [Bahasa Indonesia](../id/VM_DEPLOYMENT_GUIDE.md) | 🇰🇷 [한국어](../ko/VM_DEPLOYMENT_GUIDE.md) | 🇲🇾 [Bahasa Melayu](../ms/VM_DEPLOYMENT_GUIDE.md) | 🇳🇱 [Nederlands](../nl/VM_DEPLOYMENT_GUIDE.md) | 🇳🇴 [Norsk](../no/VM_DEPLOYMENT_GUIDE.md) | 🇵🇹 [Português (Portugal)](../pt/VM_DEPLOYMENT_GUIDE.md) | 🇷🇴 [Română](../ro/VM_DEPLOYMENT_GUIDE.md) | 🇵🇱 [Polski](../pl/VM_DEPLOYMENT_GUIDE.md) | 🇸🇰 [Slovenčina](../sk/VM_DEPLOYMENT_GUIDE.md) | 🇸🇪 [Svenska](../sv/VM_DEPLOYMENT_GUIDE.md) | 🇵🇭 [Filipino](../phi/VM_DEPLOYMENT_GUIDE.md) | 🇨🇿 [Čeština](../cs/VM_DEPLOYMENT_GUIDE.md)
|
||||
|
||||
在通过 Cloudflare 管理域的 VM (VPS) 上安装和配置 OmniRoute 的完整指南。
|
||||
|
||||
---
|
||||
|
||||
# OmniRoute — Guia de Deploy em VM com Cloudflare
|
||||
## 先决条件
|
||||
|
||||
Guia completo para instalar e configurar o OmniRoute em uma VM (VPS) com domínio gerenciado via Cloudflare.
|
||||
| 项目 | 最低 | 推荐 |
|
||||
| ------------ | -------------------- | --------------------------------- | ---------------- |
|
||||
| **CPU** | 1 个虚拟CPU | 2 个虚拟CPU |
|
||||
| **内存** | 1 GB | 2GB |
|
||||
| **磁盘** | 10 GB 固态硬盘 | 25 GB 固态硬盘 |
|
||||
| **操作系统** | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS Ubuntu 24.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **域名** | 在 Cloudflare 上注册 | — |
|
||||
| **码头工人** | Docker 引擎 24+ | Docker 27+ |
|
||||
|
||||
**经过测试的提供商**:Akamai (Linode)、DigitalOcean、Vultr、Hetzner、AWS Lightsail。
|
||||
|
||||
---
|
||||
|
||||
## Pré-Requisitos
|
||||
## 1.配置虚拟机
|
||||
|
||||
| Item | Mínimo | Recomendado |
|
||||
| ----------- | ------------------------ | ---------------- |
|
||||
| **CPU** | 1 vCPU | 2 vCPU |
|
||||
| **RAM** | 1 GB | 2 GB |
|
||||
| **Disco** | 10 GB SSD | 25 GB SSD |
|
||||
| **SO** | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
|
||||
| **Domínio** | Registrado no Cloudflare | — |
|
||||
| **Docker** | Docker Engine 24+ | Docker 27+ |
|
||||
### 1.1 创建实例
|
||||
|
||||
**Providers testados**: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
|
||||
在您首选的 VPS 提供商上:
|
||||
|
||||
---
|
||||
- 选择 Ubuntu 24.04 LTS
|
||||
- 选择最低计划(1 vCPU / 1 GB RAM)
|
||||
- 设置强root密码或配置SSH密钥
|
||||
- 记下 **公共 IP**(例如 `203.0.113.10`)
|
||||
|
||||
## 1. Configurar a VM
|
||||
|
||||
### 1.1 Criar a instância
|
||||
|
||||
No seu provider de VPS preferido:
|
||||
|
||||
- Escolha Ubuntu 24.04 LTS
|
||||
- Selecione o plano mínimo (1 vCPU / 1 GB RAM)
|
||||
- Defina uma senha forte para root ou configure SSH key
|
||||
- Anote o **IP público** (ex: `203.0.113.10`)
|
||||
|
||||
### 1.2 Conectar via SSH
|
||||
### 1.2 通过 SSH 连接
|
||||
|
||||
```bash
|
||||
ssh root@203.0.113.10
|
||||
```
|
||||
|
||||
### 1.3 Atualizar o sistema
|
||||
### 1.3 更新系统
|
||||
|
||||
```bash
|
||||
apt update && apt upgrade -y
|
||||
```
|
||||
|
||||
### 1.4 Instalar Docker
|
||||
### 1.4 安装 Docker
|
||||
|
||||
```bash
|
||||
# Instalar dependências
|
||||
# Install dependencies
|
||||
apt install -y ca-certificates curl gnupg
|
||||
|
||||
# Adicionar repositório oficial do Docker
|
||||
# Add official Docker repository
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo “$VERSION_CODENAME”) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
||||
```
|
||||
|
||||
### 1.5 Instalar nginx
|
||||
### 1.5 安装nginx
|
||||
|
||||
```bash
|
||||
apt install -y nginx
|
||||
```
|
||||
|
||||
### 1.6 Configurar Firewall (UFW)
|
||||
### 1.6 配置防火墙(UFW)
|
||||
|
||||
```bash
|
||||
ufw default deny incoming
|
||||
@@ -78,29 +76,29 @@ ufw allow 443/tcp # HTTPS
|
||||
ufw enable
|
||||
```
|
||||
|
||||
> **Dica**: Para segurança máxima, restrinja as portas 80 e 443 apenas para IPs da Cloudflare. Veja a seção [Segurança Avançada](#segurança-avançada).
|
||||
> **提示**:为了获得最大的安全性,请将端口 80 和 443 仅限制为 Cloudflare IP。请参阅 [Advanced Security](#advanced-security) 部分。
|
||||
|
||||
---
|
||||
|
||||
## 2. Instalar o OmniRoute
|
||||
## 2. 安装 OmniRoute
|
||||
|
||||
### 2.1 Criar diretório de configuração
|
||||
### 2.1 创建配置目录
|
||||
|
||||
```bash
|
||||
mkdir -p /opt/omniroute
|
||||
```
|
||||
|
||||
### 2.2 Criar arquivo de variáveis de ambiente
|
||||
### 2.2 创建环境变量文件
|
||||
|
||||
```bash
|
||||
cat > /opt/omniroute/.env << 'EOF'
|
||||
# === Segurança ===
|
||||
JWT_SECRET=ALTERE-PARA-CHAVE-SECRETA-UNICA-64-CHARS
|
||||
INITIAL_PASSWORD=SuaSenhaSegura123!
|
||||
API_KEY_SECRET=ALTERE-PARA-OUTRA-CHAVE-SECRETA
|
||||
STORAGE_ENCRYPTION_KEY=ALTERE-PARA-TERCEIRA-CHAVE-SECRETA
|
||||
cat > /opt/omniroute/.env << ‘EOF’
|
||||
# === Security ===
|
||||
JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEY
|
||||
INITIAL_PASSWORD=YourSecurePassword123!
|
||||
API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEY
|
||||
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
||||
MACHINE_ID_SALT=ALTERE-PARA-SALT-UNICO
|
||||
MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALT
|
||||
|
||||
# === App ===
|
||||
PORT=20128
|
||||
@@ -112,19 +110,19 @@ ENABLE_REQUEST_LOGS=true
|
||||
AUTH_COOKIE_SECURE=false
|
||||
REQUIRE_API_KEY=false
|
||||
|
||||
# === Domain (altere para seu domínio) ===
|
||||
# === Domain (change to your domain) ===
|
||||
BASE_URL=https://llms.seudominio.com
|
||||
NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com
|
||||
|
||||
# === Cloud Sync (opcional) ===
|
||||
# === Cloud Sync (optional) ===
|
||||
# CLOUD_URL=https://cloud.omniroute.online
|
||||
# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.online
|
||||
EOF
|
||||
```
|
||||
|
||||
> ⚠️ **IMPORTANTE**: Gere chaves secretas únicas! Use `openssl rand -hex 32` para cada chave.
|
||||
> ⚠️ **重要**:生成唯一的密钥!对每个密钥使用 `openssl rand -hex 32`。
|
||||
|
||||
### 2.3 Iniciar o container
|
||||
### 2.3 启动容器
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -138,45 +136,45 @@ docker run -d \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### 2.4 Verificar se está rodando
|
||||
### 2.4 验证其是否正在运行
|
||||
|
||||
```bash
|
||||
docker ps | grep omniroute
|
||||
docker logs omniroute --tail 20
|
||||
```
|
||||
|
||||
Deve exibir: `[DB] SQLite database ready` e `listening on port 20128`.
|
||||
它应显示:`[DB] SQLite database ready` 和 `listening on port 20128`。
|
||||
|
||||
---
|
||||
|
||||
## 3. Configurar nginx (Reverse Proxy)
|
||||
## 3.配置nginx(反向代理)
|
||||
|
||||
### 3.1 Gerar certificado SSL (Cloudflare Origin)
|
||||
### 3.1 生成 SSL 证书(Cloudflare Origin)
|
||||
|
||||
No painel da Cloudflare:
|
||||
在 Cloudflare 仪表板中:
|
||||
|
||||
1. Vá em **SSL/TLS → Origin Server**
|
||||
2. Clique **Create Certificate**
|
||||
3. Deixe os padrões (15 anos, \*.seudominio.com)
|
||||
4. Copie o **Origin Certificate** e a **Private Key**
|
||||
1. 转到 **SSL/TLS → 源服务器**
|
||||
2. 单击**创建证书**
|
||||
3. 保留默认值(15 年,\*.yourdomain.com)
|
||||
4. 复制**原始证书**和**私钥**
|
||||
|
||||
```bash
|
||||
mkdir -p /etc/nginx/ssl
|
||||
|
||||
# Colar o certificado
|
||||
# Paste the certificate
|
||||
nano /etc/nginx/ssl/origin.crt
|
||||
|
||||
# Colar a chave privada
|
||||
# Paste the private key
|
||||
nano /etc/nginx/ssl/origin.key
|
||||
|
||||
chmod 600 /etc/nginx/ssl/origin.key
|
||||
```
|
||||
|
||||
### 3.2 Configuração do nginx
|
||||
### 3.2 Nginx 配置
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/sites-available/omniroute << 'NGINX'
|
||||
# Default server — bloqueia acesso direto por IP
|
||||
cat > /etc/nginx/sites-available/omniroute << ‘NGINX’
|
||||
# Default server — blocks direct access via IP
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
@@ -192,7 +190,7 @@ server {
|
||||
server {
|
||||
listen 443 ssl;
|
||||
listen [::]:443 ssl;
|
||||
server_name llms.seudominio.com; # Altere para seu domínio
|
||||
server_name llms.yourdomain.com; # Change to your domain
|
||||
|
||||
ssl_certificate /etc/nginx/ssl/origin.crt;
|
||||
ssl_certificate_key /etc/nginx/ssl/origin.key;
|
||||
@@ -210,7 +208,7 @@ server {
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Connection “upgrade”;
|
||||
|
||||
# SSE (Server-Sent Events) — streaming AI responses
|
||||
proxy_buffering off;
|
||||
@@ -224,61 +222,61 @@ server {
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name llms.seudominio.com;
|
||||
server_name llms.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
NGINX
|
||||
```
|
||||
|
||||
### 3.3 Ativar e testar
|
||||
### 3.3 启用和测试
|
||||
|
||||
```bash
|
||||
# Remover config padrão
|
||||
# Remove default configuration
|
||||
rm -f /etc/nginx/sites-enabled/default
|
||||
|
||||
# Ativar OmniRoute
|
||||
# Enable OmniRoute
|
||||
ln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
|
||||
|
||||
# Testar e recarregar
|
||||
# Test and reload
|
||||
nginx -t && systemctl reload nginx
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Configurar Cloudflare DNS
|
||||
## 4.配置 Cloudflare DNS
|
||||
|
||||
### 4.1 Adicionar registro DNS
|
||||
### 4.1 添加DNS记录
|
||||
|
||||
No painel da Cloudflare → DNS:
|
||||
在 Cloudflare 仪表板 → DNS 中:
|
||||
|
||||
| Type | Name | Content | Proxy |
|
||||
| ---- | ------ | ------------------------- | ---------- |
|
||||
| A | `llms` | `203.0.113.10` (IP da VM) | ✅ Proxied |
|
||||
| 类型 | 名称 | 内容 | 代理 |
|
||||
| ---- | ------ | --------------------------- | ------- |
|
||||
| 一个 | `llms` | `203.0.113.10`(虚拟机 IP) | ✅ 代理 |
|
||||
|
||||
### 4.2 Configurar SSL
|
||||
### 4.2 配置SSL
|
||||
|
||||
Em **SSL/TLS → Overview**:
|
||||
在 **SSL/TLS → 概述** 下:
|
||||
|
||||
- Modo: **Full (Strict)**
|
||||
- 模式:**完全(严格)**
|
||||
|
||||
Em **SSL/TLS → Edge Certificates**:
|
||||
在**SSL/TLS → 边缘证书**下:
|
||||
|
||||
- Always Use HTTPS: ✅ On
|
||||
- Minimum TLS Version: TLS 1.2
|
||||
- Automatic HTTPS Rewrites: ✅ On
|
||||
- 始终使用 HTTPS: ✅ 打开
|
||||
- 最低 TLS 版本:TLS 1.2
|
||||
- 自动 HTTPS 重写: ✅ 开启
|
||||
|
||||
### 4.3 Testar
|
||||
### 4.3 测试
|
||||
|
||||
```bash
|
||||
curl -sI https://llms.seudominio.com/health
|
||||
# Deve retornar HTTP/2 200
|
||||
# Should return HTTP/2 200
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Operações e Manutenção
|
||||
## 5. 操作与维护
|
||||
|
||||
### Atualizar para nova versão
|
||||
### 升级到新版本
|
||||
|
||||
```bash
|
||||
docker pull diegosouzapw/omniroute:latest
|
||||
@@ -290,42 +288,42 @@ docker run -d --name omniroute --restart unless-stopped \
|
||||
diegosouzapw/omniroute:latest
|
||||
```
|
||||
|
||||
### Ver logs
|
||||
### 查看日志
|
||||
|
||||
```bash
|
||||
docker logs -f omniroute # Stream em tempo real
|
||||
docker logs omniroute --tail 50 # Últimas 50 linhas
|
||||
docker logs -f omniroute # Real-time stream
|
||||
docker logs omniroute --tail 50 # Last 50 lines
|
||||
```
|
||||
|
||||
### Backup manual do banco
|
||||
### 手动数据库备份
|
||||
|
||||
```bash
|
||||
# Copiar dados do volume para o host
|
||||
# Copy data from the volume to the host
|
||||
docker cp omniroute:/app/data ./backup-$(date +%F)
|
||||
|
||||
# Ou comprimir todo o volume
|
||||
# Or compress the entire volume
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /data
|
||||
```
|
||||
|
||||
### Restaurar de backup
|
||||
### 从备份恢复
|
||||
|
||||
```bash
|
||||
docker stop omniroute
|
||||
docker run --rm -v omniroute-data:/data -v $(pwd):/backup \
|
||||
alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"
|
||||
alpine sh -c “rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /”
|
||||
docker start omniroute
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Segurança Avançada
|
||||
## 6. 高级安全性
|
||||
|
||||
### Restringir nginx para Cloudflare IPs
|
||||
### 将 nginx 限制为 Cloudflare IP
|
||||
|
||||
```bash
|
||||
cat > /etc/nginx/cloudflare-ips.conf << 'CF'
|
||||
# Cloudflare IPv4 ranges — atualizar periodicamente
|
||||
cat > /etc/nginx/cloudflare-ips.conf << ‘CF’
|
||||
# Cloudflare IPv4 ranges — update periodically
|
||||
# https://www.cloudflare.com/ips-v4/
|
||||
set_real_ip_from 173.245.48.0/20;
|
||||
set_real_ip_from 103.21.244.0/22;
|
||||
@@ -346,58 +344,58 @@ real_ip_header CF-Connecting-IP;
|
||||
CF
|
||||
```
|
||||
|
||||
Adicionar no `nginx.conf` dentro do bloco `http {}`:
|
||||
将以下内容添加到 `http {}` 块内的 `nginx.conf` 中:
|
||||
|
||||
```nginx
|
||||
include /etc/nginx/cloudflare-ips.conf;
|
||||
```
|
||||
|
||||
### Install fail2ban
|
||||
### 安装fail2ban
|
||||
|
||||
```bash
|
||||
apt install -y fail2ban
|
||||
systemctl enable fail2ban
|
||||
systemctl start fail2ban
|
||||
|
||||
# Verificar status
|
||||
# Check status
|
||||
fail2ban-client status sshd
|
||||
```
|
||||
|
||||
### Bloquear acesso direto na porta do Docker
|
||||
### 阻止直接访问 Docker 端口
|
||||
|
||||
```bash
|
||||
# Impedir acesso externo direto à porta 20128
|
||||
# Prevent direct external access to port 20128
|
||||
iptables -I DOCKER-USER -p tcp --dport 20128 -j DROP
|
||||
iptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
|
||||
|
||||
# Persistir as regras
|
||||
# Persist the rules
|
||||
apt install -y iptables-persistent
|
||||
netfilter-persistent save
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Deploy do Cloud Worker (Opcional)
|
||||
## 7. 部署到 Cloudflare Workers(可选)
|
||||
|
||||
Para acesso remoto via Cloudflare Workers (sem expor a VM diretamente):
|
||||
对于通过 Cloudflare Workers 进行远程访问(无需直接公开 VM):
|
||||
|
||||
```bash
|
||||
# No repositório local
|
||||
# In the local repository
|
||||
cd omnirouteCloud
|
||||
npm install
|
||||
npx wrangler login
|
||||
npx wrangler deploy
|
||||
```
|
||||
|
||||
Ver documentação completa em [omnirouteCloud/README.md](../omnirouteCloud/README.md).
|
||||
请参阅 [omnirouteCloud/README.md](../omnirouteCloud/README.md) 处的完整文档。
|
||||
|
||||
---
|
||||
|
||||
## Resumo de Portas
|
||||
## 端口总结
|
||||
|
||||
| Porta | Serviço | Acesso |
|
||||
| ----- | ----------- | ----------------------------- |
|
||||
| 22 | SSH | Público (com fail2ban) |
|
||||
| 80 | nginx HTTP | Redirect → HTTPS |
|
||||
| 443 | nginx HTTPS | Via Cloudflare Proxy |
|
||||
| 20128 | OmniRoute | Somente localhost (via nginx) |
|
||||
| 港口 | 服务 | 访问 |
|
||||
| ----- | --------------- | ------------------------ |
|
||||
| 22 | 22 SSH | 公共(带有fail2ban) |
|
||||
| 80 | nginx HTTP | 重定向 → HTTPS |
|
||||
| 443 | 443 nginx HTTPS | 通过 Cloudflare 代理 |
|
||||
| 20128 | 20128全方位路线 | 仅本地主机(通过 nginx) |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: OmniRoute API
|
||||
version: 2.7.4
|
||||
version: 2.9.0
|
||||
description: |
|
||||
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
|
||||
endpoint that routes requests to multiple AI providers with load balancing,
|
||||
|
||||
4
llm.txt
4
llm.txt
@@ -59,7 +59,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ │ └── login/ # Login page
|
||||
│ ├── domain/ # Domain types and business logic interfaces
|
||||
│ ├── i18n/ # Internationalization
|
||||
│ │ └── messages/ # 30 language JSON files (ar, bg, da, de, en, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
│ │ └── messages/ # 30 language JSON files (ar, bg, cs, da, de, en, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
│ ├── lib/ # Core libraries
|
||||
│ │ ├── acp/ # ACP agent registry and manager (14 built-in agents + custom)
|
||||
│ │ ├── db/ # SQLite database layer (providers, combos, prompts, logs)
|
||||
@@ -91,7 +91,7 @@ OmniRoute solves the problem of managing multiple AI provider subscriptions, quo
|
||||
│ ├── unit/ # Unit tests (32+ test files)
|
||||
│ └── integration/ # Integration tests
|
||||
├── docs/ # Documentation (with 29-language i18n subdirectories)
|
||||
│ ├── i18n/ # Translated docs (ar, bg, da, de, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
│ ├── i18n/ # Translated docs (ar, bg, cs, da, de, es, fi, fr, he, hu, id, in, it, ja, ko, ms, nl, no, phi, pl, pt, pt-BR, ro, ru, sk, sv, th, uk-UA, vi, zh-CN)
|
||||
│ ├── screenshots/ # Dashboard screenshots
|
||||
│ ├── a2a-server.md # A2A agent protocol documentation
|
||||
│ ├── auto-combo.md # Auto-combo engine (6-factor scoring)
|
||||
|
||||
@@ -121,6 +121,10 @@ const nextConfig = {
|
||||
source: "/responses",
|
||||
destination: "/api/v1/responses",
|
||||
},
|
||||
{
|
||||
source: "/responses/:path*",
|
||||
destination: "/api/v1/responses/:path*",
|
||||
},
|
||||
{
|
||||
source: "/models",
|
||||
destination: "/api/v1/models",
|
||||
|
||||
@@ -4,9 +4,9 @@ import { loadProviderCredentials } from "./credentialLoader.ts";
|
||||
export const FETCH_TIMEOUT_MS = parseInt(process.env.FETCH_TIMEOUT_MS || "120000", 10);
|
||||
|
||||
// Idle timeout for SSE streams (ms). Closes stream if no data for this duration.
|
||||
// Default: 300s to support extended-thinking models (claude-opus-4-6, o3, etc.)
|
||||
// that may pause for >60s during deep reasoning phases. Override with STREAM_IDLE_TIMEOUT_MS env var.
|
||||
export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "300000", 10);
|
||||
// Default: 120s balances deep-reasoning pauses with fast zombie stream detection (#473).
|
||||
// Extended-thinking models rarely pause >90s between chunks. Override with STREAM_IDLE_TIMEOUT_MS env var.
|
||||
export const STREAM_IDLE_TIMEOUT_MS = parseInt(process.env.STREAM_IDLE_TIMEOUT_MS || "120000", 10);
|
||||
|
||||
// Provider configurations
|
||||
// OAuth credentials read from env vars with hardcoded fallbacks for backward compatibility.
|
||||
|
||||
@@ -78,6 +78,22 @@ interface LegacyProvider {
|
||||
clientVersion?: string;
|
||||
}
|
||||
|
||||
const KIMI_CODING_SHARED = {
|
||||
format: "claude",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
authHeader: "x-api-key",
|
||||
headers: {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Anthropic-Beta": "claude-code-20250219,interleaved-thinking-2025-05-14",
|
||||
},
|
||||
models: [
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" },
|
||||
] as RegistryModel[],
|
||||
} as const;
|
||||
|
||||
// ── Registry ──────────────────────────────────────────────────────────────
|
||||
|
||||
export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
@@ -521,6 +537,32 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
"bailian-coding-plan": {
|
||||
id: "bailian-coding-plan",
|
||||
alias: "bcp",
|
||||
format: "claude",
|
||||
executor: "default",
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/apps/anthropic/v1/messages",
|
||||
chatPath: "/messages",
|
||||
urlSuffix: "?beta=true",
|
||||
authType: "apikey",
|
||||
authHeader: "x-api-key",
|
||||
headers: {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Anthropic-Beta": "claude-code-20250219,interleaved-thinking-2025-05-14",
|
||||
},
|
||||
models: [
|
||||
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus" },
|
||||
{ id: "qwen3-max-2026-01-23", name: "Qwen3 Max (2026-01-23)" },
|
||||
{ id: "qwen3-coder-next", name: "Qwen3 Coder Next" },
|
||||
{ id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" },
|
||||
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
|
||||
{ id: "glm-5", name: "GLM 5" },
|
||||
{ id: "glm-4.7", name: "GLM 4.7" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
],
|
||||
},
|
||||
|
||||
zai: {
|
||||
id: "zai",
|
||||
alias: "zai",
|
||||
@@ -559,16 +601,9 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
"kimi-coding": {
|
||||
id: "kimi-coding",
|
||||
alias: "kmc",
|
||||
format: "claude",
|
||||
executor: "default",
|
||||
baseUrl: "https://api.kimi.com/coding/v1/messages",
|
||||
...KIMI_CODING_SHARED,
|
||||
urlSuffix: "?beta=true",
|
||||
authType: "oauth",
|
||||
authHeader: "x-api-key",
|
||||
headers: {
|
||||
"Anthropic-Version": "2023-06-01",
|
||||
"Anthropic-Beta": "claude-code-20250219,interleaved-thinking-2025-05-14",
|
||||
},
|
||||
oauth: {
|
||||
clientIdEnv: "KIMI_CODING_OAUTH_CLIENT_ID",
|
||||
clientIdDefault: "17e5f671-d194-4dfb-9706-5516cb48c098",
|
||||
@@ -576,11 +611,13 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
refreshUrl: "https://auth.kimi.com/api/oauth/token",
|
||||
authUrl: "https://auth.kimi.com/api/oauth/device_authorization",
|
||||
},
|
||||
models: [
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "kimi-k2.5-thinking", name: "Kimi K2.5 Thinking" },
|
||||
{ id: "kimi-latest", name: "Kimi Latest" },
|
||||
],
|
||||
},
|
||||
|
||||
"kimi-coding-apikey": {
|
||||
id: "kimi-coding-apikey",
|
||||
alias: "kmca",
|
||||
...KIMI_CODING_SHARED,
|
||||
authType: "apikey",
|
||||
},
|
||||
|
||||
kilocode: {
|
||||
@@ -699,6 +736,46 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
],
|
||||
},
|
||||
|
||||
alicode: {
|
||||
id: "alicode",
|
||||
alias: "alicode",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://coding.dashscope.aliyuncs.com/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "glm-5", name: "GLM 5" },
|
||||
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
|
||||
{ id: "qwen3-max-2026-01-23", name: "Qwen3 Max" },
|
||||
{ id: "qwen3-coder-next", name: "Qwen3 Coder Next" },
|
||||
{ id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" },
|
||||
{ id: "glm-4.7", name: "GLM 4.7" },
|
||||
],
|
||||
},
|
||||
|
||||
"alicode-intl": {
|
||||
id: "alicode-intl",
|
||||
alias: "alicode-intl",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1/chat/completions",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "qwen3.5-plus", name: "Qwen3.5 Plus" },
|
||||
{ id: "kimi-k2.5", name: "Kimi K2.5" },
|
||||
{ id: "glm-5", name: "GLM 5" },
|
||||
{ id: "MiniMax-M2.5", name: "MiniMax M2.5" },
|
||||
{ id: "qwen3-max-2026-01-23", name: "Qwen3 Max" },
|
||||
{ id: "qwen3-coder-next", name: "Qwen3 Coder Next" },
|
||||
{ id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" },
|
||||
{ id: "glm-4.7", name: "GLM 4.7" },
|
||||
],
|
||||
},
|
||||
|
||||
deepseek: {
|
||||
id: "deepseek",
|
||||
alias: "ds",
|
||||
@@ -1048,6 +1125,35 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
{ id: "claude-sonnet-4-5@20251101", name: "Claude Sonnet 4.5 (Vertex)" },
|
||||
],
|
||||
},
|
||||
|
||||
alibaba: {
|
||||
id: "alibaba",
|
||||
alias: "ali",
|
||||
format: "openai",
|
||||
executor: "default",
|
||||
// DashScope international OpenAI-compatible endpoint.
|
||||
// China users should set providerSpecificData.baseUrl to:
|
||||
// https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions
|
||||
baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions",
|
||||
modelsUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/models",
|
||||
authType: "apikey",
|
||||
authHeader: "bearer",
|
||||
models: [
|
||||
{ id: "qwen-max", name: "Qwen Max" },
|
||||
{ id: "qwen-max-2025-01-25", name: "Qwen Max (2025-01-25)" },
|
||||
{ id: "qwen-plus", name: "Qwen Plus" },
|
||||
{ id: "qwen-plus-2025-07-14", name: "Qwen Plus (2025-07-14)" },
|
||||
{ id: "qwen-turbo", name: "Qwen Turbo" },
|
||||
{ id: "qwen-turbo-2025-11-01", name: "Qwen Turbo (2025-11-01)" },
|
||||
{ id: "qwen3-coder-plus", name: "Qwen3 Coder Plus" },
|
||||
{ id: "qwen3-coder-flash", name: "Qwen3 Coder Flash" },
|
||||
{ id: "qwq-plus", name: "QwQ Plus (Reasoning)" },
|
||||
{ id: "qwq-32b", name: "QwQ 32B" },
|
||||
{ id: "qwen3-32b", name: "Qwen3 32B" },
|
||||
{ id: "qwen3-235b-a22b", name: "Qwen3 235B A22B" },
|
||||
],
|
||||
passthroughModels: true,
|
||||
},
|
||||
};
|
||||
|
||||
// ── Generator Functions ───────────────────────────────────────────────────
|
||||
|
||||
@@ -2,6 +2,20 @@ import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.ts";
|
||||
import { applyFingerprint, isCliCompatEnabled } from "../config/cliFingerprints.ts";
|
||||
import { getRotatingApiKey } from "../services/apiKeyRotator.ts";
|
||||
|
||||
/**
|
||||
* Sanitizes a custom API path to prevent path traversal attacks.
|
||||
* Valid paths must start with '/', contain no '..' segments,
|
||||
* no null bytes, and be reasonable in length.
|
||||
*/
|
||||
function sanitizePath(path: string): boolean {
|
||||
if (typeof path !== "string") return false;
|
||||
if (!path.startsWith("/")) return false;
|
||||
if (path.includes("\0")) return false; // null byte
|
||||
if (path.includes("..")) return false; // path traversal
|
||||
if (path.length > 512) return false; // sanity limit
|
||||
return true;
|
||||
}
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
export type ProviderConfig = {
|
||||
@@ -26,6 +40,7 @@ export type ProviderCredentials = {
|
||||
expiresAt?: string;
|
||||
connectionId?: string; // T07: used for API key rotation index
|
||||
providerSpecificData?: JsonRecord;
|
||||
requestEndpointPath?: string;
|
||||
};
|
||||
|
||||
export type ExecutorLog = {
|
||||
@@ -102,7 +117,9 @@ export class BaseExecutor {
|
||||
const psd = credentials?.providerSpecificData;
|
||||
const baseUrl = typeof psd?.baseUrl === "string" ? psd.baseUrl : "https://api.openai.com/v1";
|
||||
const normalized = baseUrl.replace(/\/$/, "");
|
||||
const customPath = typeof psd?.chatPath === "string" && psd.chatPath ? psd.chatPath : null;
|
||||
// Sanitize custom path: must start with '/', no path traversal, no null bytes
|
||||
const rawPath = typeof psd?.chatPath === "string" && psd.chatPath ? psd.chatPath : null;
|
||||
const customPath = rawPath && sanitizePath(rawPath) ? rawPath : null;
|
||||
if (customPath) return `${normalized}${customPath}`;
|
||||
const path = this.provider.includes("responses") ? "/responses" : "/chat/completions";
|
||||
return `${normalized}${path}`;
|
||||
|
||||
@@ -9,6 +9,17 @@ type EffortLevel = (typeof EFFORT_ORDER)[number];
|
||||
const CODEX_FAST_WIRE_VALUE = "priority";
|
||||
let defaultFastServiceTierEnabled = false;
|
||||
|
||||
function getResponsesSubpath(endpointPath: unknown): string | null {
|
||||
const normalizedEndpoint = String(endpointPath || "").replace(/\/+$/, "");
|
||||
const match = normalizedEndpoint.match(/(?:^|\/)responses(?:(\/.*))?$/i);
|
||||
if (!match) return null;
|
||||
return match[1] || "";
|
||||
}
|
||||
|
||||
function isCompactResponsesEndpoint(endpointPath: unknown): boolean {
|
||||
return getResponsesSubpath(endpointPath)?.toLowerCase() === "/compact";
|
||||
}
|
||||
|
||||
function normalizeServiceTierValue(value: unknown): string | undefined {
|
||||
if (typeof value !== "string") return undefined;
|
||||
const normalized = value.trim().toLowerCase();
|
||||
@@ -60,13 +71,31 @@ export class CodexExecutor extends BaseExecutor {
|
||||
super("codex", PROVIDERS.codex);
|
||||
}
|
||||
|
||||
buildUrl(model, stream, urlIndex = 0, credentials = null) {
|
||||
void model;
|
||||
void stream;
|
||||
void urlIndex;
|
||||
|
||||
const responsesSubpath = getResponsesSubpath(credentials?.requestEndpointPath);
|
||||
if (responsesSubpath !== null) {
|
||||
const baseUrl = String(this.config.baseUrl || "").replace(/\/$/, "");
|
||||
if (baseUrl.endsWith("/responses")) {
|
||||
return `${baseUrl}${responsesSubpath}`;
|
||||
}
|
||||
return `${baseUrl}/responses${responsesSubpath}`;
|
||||
}
|
||||
|
||||
return super.buildUrl(model, stream, urlIndex, credentials);
|
||||
}
|
||||
|
||||
/**
|
||||
* Codex Responses endpoint is SSE-first.
|
||||
* Always request event-stream from upstream, even when client requested stream=false.
|
||||
* Includes chatgpt-account-id header for strict workspace binding.
|
||||
*/
|
||||
buildHeaders(credentials, stream = true) {
|
||||
const headers = super.buildHeaders(credentials, true);
|
||||
const isCompactRequest = isCompactResponsesEndpoint(credentials?.requestEndpointPath);
|
||||
const headers = super.buildHeaders(credentials, isCompactRequest ? false : true);
|
||||
|
||||
// Add workspace binding header if workspaceId is persisted
|
||||
const workspaceId = credentials?.providerSpecificData?.workspaceId;
|
||||
@@ -107,9 +136,15 @@ export class CodexExecutor extends BaseExecutor {
|
||||
*/
|
||||
transformRequest(model, body, stream, credentials) {
|
||||
const nativeCodexPassthrough = body?._nativeCodexPassthrough === true;
|
||||
const isCompactRequest = isCompactResponsesEndpoint(credentials?.requestEndpointPath);
|
||||
|
||||
// Codex /responses rejects stream=false; we aggregate SSE back to JSON when needed.
|
||||
body.stream = true;
|
||||
// Codex /responses rejects stream=false, but /responses/compact rejects the stream field entirely.
|
||||
if (isCompactRequest) {
|
||||
delete body.stream;
|
||||
delete body.stream_options;
|
||||
} else {
|
||||
body.stream = true;
|
||||
}
|
||||
delete body._nativeCodexPassthrough;
|
||||
|
||||
const requestServiceTier = normalizeServiceTierValue(body.service_tier);
|
||||
|
||||
@@ -54,6 +54,8 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
break;
|
||||
case "glm":
|
||||
case "kimi-coding":
|
||||
case "bailian-coding-plan":
|
||||
case "kimi-coding-apikey":
|
||||
case "minimax":
|
||||
case "minimax-cn":
|
||||
headers["x-api-key"] = credentials.apiKey || credentials.accessToken;
|
||||
@@ -78,18 +80,14 @@ export class DefaultExecutor extends BaseExecutor {
|
||||
}
|
||||
|
||||
/**
|
||||
* For compatible providers, ensure the model name sent upstream
|
||||
* is the clean model name without internal routing prefixes.
|
||||
* e.g. "openapi-chat-anti/claude-opus-4-6-thinking" → "claude-opus-4-6-thinking"
|
||||
* For compatible providers, the model name is already clean by the time
|
||||
* it reaches the executor (chatCore sets body.model = modelInfo.model,
|
||||
* which is the parsed model ID without any internal routing prefix).
|
||||
*
|
||||
* Models may legitimately contain "/" as part of their ID (e.g. "zai-org/GLM-5-FP8",
|
||||
* "org/model-name") — we must NOT strip path segments. (Fix #493)
|
||||
*/
|
||||
transformRequest(model, body, stream, credentials) {
|
||||
if (
|
||||
this.provider?.startsWith?.("openai-compatible-") ||
|
||||
this.provider?.startsWith?.("anthropic-compatible-")
|
||||
) {
|
||||
const cleanModel = model.includes("/") ? model.split("/").slice(1).join("/") : model;
|
||||
return { ...body, model: cleanModel };
|
||||
}
|
||||
return body;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,10 +77,13 @@ export class KiroExecutor extends BaseExecutor {
|
||||
}
|
||||
|
||||
transformRequest(model: string, body: unknown, stream: boolean, credentials: unknown): unknown {
|
||||
void model;
|
||||
void stream;
|
||||
void credentials;
|
||||
return body;
|
||||
// Kiro uses conversationState.currentMessage.userInputMessage.modelId,
|
||||
// not a top-level "model" field. chatCore injects translatedBody.model
|
||||
// which Kiro API rejects as unknown top-level field.
|
||||
const { model: _model, ...rest } = body as Record<string, unknown>;
|
||||
return rest;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
appendRequestLog,
|
||||
saveCallLog,
|
||||
} from "@/lib/usageDb";
|
||||
import { getModelNormalizeToolCallId, getModelPreserveOpenAIDeveloperRole } from "@/lib/localDb";
|
||||
import { getExecutor } from "../executors/index.ts";
|
||||
import { translateNonStreamingResponse } from "./responseTranslator.ts";
|
||||
import { extractUsageFromResponse } from "./usageExtractor.ts";
|
||||
@@ -60,9 +61,8 @@ export function shouldUseNativeCodexPassthrough({
|
||||
}): boolean {
|
||||
if (provider !== "codex") return false;
|
||||
if (sourceFormat !== FORMATS.OPENAI_RESPONSES) return false;
|
||||
return String(endpointPath || "")
|
||||
.toLowerCase()
|
||||
.endsWith("/responses");
|
||||
const normalizedEndpoint = String(endpointPath || "").replace(/\/+$/, "");
|
||||
return /(?:^|\/)responses(?:\/.*)?$/i.test(normalizedEndpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -140,8 +140,8 @@ export async function handleChatCore({
|
||||
}
|
||||
|
||||
const sourceFormat = detectFormat(body);
|
||||
const endpointPath = (clientRawRequest?.endpoint || "").toLowerCase();
|
||||
const isResponsesEndpoint = endpointPath.endsWith("/responses");
|
||||
const endpointPath = String(clientRawRequest?.endpoint || "");
|
||||
const isResponsesEndpoint = /(?:^|\/)responses(?:\/.*)?$/i.test(endpointPath);
|
||||
const nativeCodexPassthrough = shouldUseNativeCodexPassthrough({
|
||||
provider,
|
||||
sourceFormat,
|
||||
@@ -157,10 +157,16 @@ export async function handleChatCore({
|
||||
// Detect source format and get target format
|
||||
// Model-specific targetFormat takes priority over provider default
|
||||
|
||||
// Apply custom model aliases (Settings → Model Aliases → Pattern→Target) before routing (#315)
|
||||
// Apply custom model aliases (Settings → Model Aliases → Pattern→Target) before routing (#315, #472)
|
||||
// Custom aliases take priority over built-in and must be resolved here so the
|
||||
// downstream getModelTargetFormat() lookup uses the correct, aliased model ID.
|
||||
// downstream getModelTargetFormat() lookup AND the actual provider request use
|
||||
// the correct, aliased model ID. Without this, aliases only affect format detection.
|
||||
const resolvedModel = resolveModelAlias(model);
|
||||
// Use resolvedModel for all downstream operations (routing, provider requests, logging)
|
||||
const effectiveModel = resolvedModel !== model ? resolvedModel : model;
|
||||
if (resolvedModel !== model) {
|
||||
log?.info?.("ALIAS", `Model alias applied: ${model} → ${resolvedModel}`);
|
||||
}
|
||||
|
||||
const alias = PROVIDER_ID_TO_ALIAS[provider] || provider;
|
||||
const modelTargetFormat = getModelTargetFormat(alias, resolvedModel);
|
||||
@@ -311,6 +317,11 @@ export async function handleChatCore({
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeToolCallId = getModelNormalizeToolCallId(provider || "", model || "");
|
||||
const preserveDeveloperRole = getModelPreserveOpenAIDeveloperRole(
|
||||
provider || "",
|
||||
model || ""
|
||||
);
|
||||
translatedBody = translateRequest(
|
||||
sourceFormat,
|
||||
targetFormat,
|
||||
@@ -319,7 +330,8 @@ export async function handleChatCore({
|
||||
stream,
|
||||
credentials,
|
||||
provider,
|
||||
reqLogger
|
||||
reqLogger,
|
||||
{ normalizeToolCallId, preserveDeveloperRole }
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -365,8 +377,8 @@ export async function handleChatCore({
|
||||
delete translatedBody._toolNameMap;
|
||||
delete translatedBody._disableToolPrefix;
|
||||
|
||||
// Update model in body
|
||||
translatedBody.model = model;
|
||||
// Update model in body — use resolved alias so the provider gets the correct model ID (#472)
|
||||
translatedBody.model = effectiveModel;
|
||||
|
||||
// Strip unsupported parameters for reasoning models (o1, o3, etc.)
|
||||
const unsupported = getUnsupportedParams(provider, model);
|
||||
@@ -385,6 +397,8 @@ export async function handleChatCore({
|
||||
|
||||
// Get executor for this provider
|
||||
const executor = getExecutor(provider);
|
||||
const getExecutionCredentials = () =>
|
||||
nativeCodexPassthrough ? { ...credentials, requestEndpointPath: endpointPath } : credentials;
|
||||
|
||||
// Create stream controller for disconnect detection
|
||||
const streamController = createStreamController({ onDisconnect, log, provider, model });
|
||||
@@ -393,7 +407,7 @@ export async function handleChatCore({
|
||||
const dedupEnabled = shouldDeduplicate(dedupRequestBody);
|
||||
const dedupHash = dedupEnabled ? computeRequestHash(dedupRequestBody) : null;
|
||||
|
||||
const executeProviderRequest = async (modelToCall = model, allowDedup = false) => {
|
||||
const executeProviderRequest = async (modelToCall = effectiveModel, allowDedup = false) => {
|
||||
const execute = async () => {
|
||||
const bodyToSend =
|
||||
translatedBody.model === modelToCall
|
||||
@@ -405,7 +419,7 @@ export async function handleChatCore({
|
||||
model: modelToCall,
|
||||
body: bodyToSend,
|
||||
stream,
|
||||
credentials,
|
||||
credentials: getExecutionCredentials(),
|
||||
signal: streamController.signal,
|
||||
log,
|
||||
extendedContext,
|
||||
@@ -441,8 +455,8 @@ export async function handleChatCore({
|
||||
trackPendingRequest(model, provider, connectionId, true);
|
||||
|
||||
// T5: track which models we've tried for intra-family fallback
|
||||
const triedModels = new Set<string>([model]);
|
||||
let currentModel = model;
|
||||
const triedModels = new Set<string>([effectiveModel]);
|
||||
let currentModel = effectiveModel;
|
||||
|
||||
// Log start
|
||||
appendRequestLog({ model, provider, connectionId, status: "PENDING" }).catch(() => {});
|
||||
@@ -461,7 +475,7 @@ export async function handleChatCore({
|
||||
let finalBody;
|
||||
|
||||
try {
|
||||
const result = await executeProviderRequest(model, true);
|
||||
const result = await executeProviderRequest(effectiveModel, true);
|
||||
|
||||
providerResponse = result.response;
|
||||
providerUrl = result.url;
|
||||
@@ -545,7 +559,7 @@ export async function handleChatCore({
|
||||
model,
|
||||
body: translatedBody,
|
||||
stream,
|
||||
credentials,
|
||||
credentials: getExecutionCredentials(),
|
||||
signal: streamController.signal,
|
||||
log,
|
||||
extendedContext,
|
||||
@@ -870,8 +884,12 @@ export async function handleChatCore({
|
||||
// Create transform stream with logger for streaming response
|
||||
let transformStream;
|
||||
|
||||
// Callback to save call log when stream completes (streaming calls were never logged before!)
|
||||
const onStreamComplete = ({ status: streamStatus, usage: streamUsage }) => {
|
||||
// Callback to save call log when stream completes (include responseBody when provided by stream)
|
||||
const onStreamComplete = ({
|
||||
status: streamStatus,
|
||||
usage: streamUsage,
|
||||
responseBody: streamResponseBody,
|
||||
}) => {
|
||||
saveCallLog({
|
||||
method: "POST",
|
||||
path: clientRawRequest?.endpoint || "/v1/chat/completions",
|
||||
@@ -882,6 +900,7 @@ export async function handleChatCore({
|
||||
duration: Date.now() - startTime,
|
||||
tokens: streamUsage || {},
|
||||
requestBody: body,
|
||||
responseBody: streamResponseBody ?? undefined,
|
||||
sourceFormat,
|
||||
targetFormat,
|
||||
comboName,
|
||||
|
||||
@@ -447,8 +447,10 @@ export async function handleComboChat({
|
||||
const handleSingleModelWrapped = combo.context_cache_protection
|
||||
? async (b, modelStr) => {
|
||||
const res = await handleSingleModel(b, modelStr);
|
||||
// Inject tag only on success and only for non-streaming non-binary responses
|
||||
if (res.ok && !b.stream) {
|
||||
if (!res.ok) return res;
|
||||
|
||||
// Non-streaming: inject tag into JSON response (existing logic)
|
||||
if (!b.stream) {
|
||||
try {
|
||||
const json = await res.clone().json();
|
||||
const msgs = Array.isArray(json?.messages) ? json.messages : [];
|
||||
@@ -460,10 +462,74 @@ export async function handleComboChat({
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
/* non-JSON or stream — skip tagging */
|
||||
/* non-JSON — skip tagging */
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return res;
|
||||
|
||||
// Streaming (Fix #490): append omniModel tag as a final SSE content delta
|
||||
// before the [DONE] marker using TransformStream for zero-copy passthrough
|
||||
if (!res.body) return res;
|
||||
const tagContent = `\n<omniModel>${modelStr}</omniModel>`;
|
||||
const encoder = new TextEncoder();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = "";
|
||||
|
||||
const transform = new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
// Decode chunk and check for [DONE] marker
|
||||
const text = decoder.decode(chunk, { stream: true });
|
||||
buffer += text;
|
||||
|
||||
// Check if buffer contains the [DONE] marker
|
||||
const doneIdx = buffer.indexOf("data: [DONE]");
|
||||
if (doneIdx === -1) {
|
||||
// No [DONE] yet — flush buffer as-is (keep passthrough latency low)
|
||||
controller.enqueue(encoder.encode(buffer));
|
||||
buffer = "";
|
||||
return;
|
||||
}
|
||||
|
||||
// Found [DONE] — inject tag content delta before it
|
||||
const beforeDone = buffer.slice(0, doneIdx);
|
||||
const afterDone = buffer.slice(doneIdx);
|
||||
|
||||
// Build a synthetic SSE content delta chunk with the tag
|
||||
const tagChunk = `data: ${JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
delta: { content: tagContent },
|
||||
index: 0,
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
})}\n\n`;
|
||||
|
||||
controller.enqueue(encoder.encode(beforeDone + tagChunk + afterDone));
|
||||
buffer = "";
|
||||
},
|
||||
flush(controller) {
|
||||
// If stream ends without [DONE], flush remaining buffer + tag
|
||||
if (buffer.length > 0) {
|
||||
const tagChunk = `data: ${JSON.stringify({
|
||||
choices: [
|
||||
{
|
||||
delta: { content: tagContent },
|
||||
index: 0,
|
||||
finish_reason: null,
|
||||
},
|
||||
],
|
||||
})}\n\n`;
|
||||
controller.enqueue(encoder.encode(buffer + tagChunk));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const transformedStream = res.body.pipeThrough(transform);
|
||||
return new Response(transformedStream, {
|
||||
status: res.status,
|
||||
headers: res.headers,
|
||||
});
|
||||
}
|
||||
: handleSingleModel;
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -52,7 +52,15 @@ export function injectModelTag(messages: Message[], providerModel: string): Mess
|
||||
|
||||
// Find last assistant message with string content
|
||||
const lastAssistantIdx = cleaned.map((m) => m.role).lastIndexOf("assistant");
|
||||
if (lastAssistantIdx === -1) return cleaned;
|
||||
|
||||
// #474: If no assistant message exists yet (first turn), append a synthetic one
|
||||
// so the tag is present when the client sends the next request with the response.
|
||||
if (lastAssistantIdx === -1) {
|
||||
return [
|
||||
...cleaned,
|
||||
{ role: "assistant", content: `\n<omniModel>${providerModel}</omniModel>` },
|
||||
];
|
||||
}
|
||||
|
||||
const msg = cleaned[lastAssistantIdx];
|
||||
if (typeof msg.content !== "string") return cleaned;
|
||||
@@ -123,6 +131,20 @@ export function applyToolFilter(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip all <omniModel> tags from message content before forwarding to the provider.
|
||||
* The tag is an internal OmniRoute marker; providers must never see it or their
|
||||
* cache will treat every tagged request as a new session (#454).
|
||||
*/
|
||||
export function stripModelTags(messages: Message[]): Message[] {
|
||||
return messages.map((msg) => {
|
||||
if (typeof msg.content === "string" && CACHE_TAG_PATTERN.test(msg.content)) {
|
||||
return { ...msg, content: msg.content.replace(CACHE_TAG_PATTERN, "").trimEnd() };
|
||||
}
|
||||
return msg;
|
||||
});
|
||||
}
|
||||
|
||||
// ── Main Middleware ──────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
@@ -158,6 +180,11 @@ export function applyComboAgentMiddleware(
|
||||
comboConfig.tool_filter_regex
|
||||
);
|
||||
|
||||
// 4. Strip internal <omniModel> tags before forwarding to provider (#454)
|
||||
// These tags are OmniRoute-internal markers and must never reach the provider
|
||||
// since providers would treat each tagged request as a new cache session.
|
||||
messages = stripModelTags(messages);
|
||||
|
||||
return {
|
||||
body: {
|
||||
...body,
|
||||
|
||||
@@ -339,14 +339,19 @@ export function updateFromHeaders(provider, connectionId, headers, status, model
|
||||
// Handle 429 — rate limited
|
||||
if (status === 429) {
|
||||
const retryAfterMs = parseResetTime(retryAfterStr) || 60000; // Default 60s
|
||||
const counts = limiter.counts();
|
||||
const limiterKey = `${provider}:${connectionId}`;
|
||||
console.log(
|
||||
`🚫 [RATE-LIMIT] ${provider}:${connectionId.slice(0, 8)} — 429 received, pausing for ${Math.ceil(retryAfterMs / 1000)}s`
|
||||
`🚫 [RATE-LIMIT] ${provider}:${connectionId.slice(0, 8)} — 429 received, pausing for ${Math.ceil(retryAfterMs / 1000)}s, dropping ${counts.QUEUED} queued request(s)`
|
||||
);
|
||||
|
||||
limiter.updateSettings({
|
||||
reservoir: 0,
|
||||
reservoirRefreshAmount: limit || 60,
|
||||
reservoirRefreshInterval: retryAfterMs,
|
||||
// Stop the limiter and drop all waiting jobs so they fail immediately
|
||||
// instead of hanging in the queue until reservoir refreshes (which can
|
||||
// be hours for providers like Codex with long rate limit windows).
|
||||
// This lets upstream callers (e.g. LiteLLM) trigger fallback to other providers.
|
||||
// After stop, delete from Map so getLimiter() creates a fresh instance.
|
||||
limiter.stop({ dropWaitingJobs: true }).finally(() => {
|
||||
limiters.delete(limiterKey);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,26 +76,35 @@ function supportsSystemRole(provider: string, model: string): boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize the `developer` role to `system` for non-OpenAI providers.
|
||||
* OpenAI introduced `developer` as a replacement for `system` in newer models,
|
||||
* but most other providers still expect `system`.
|
||||
* Normalize the `developer` role to `system` when the upstream does not support it.
|
||||
* OpenAI Responses API sends `developer`; MiniMax and most OpenAI-compatible gateways
|
||||
* only accept system/user/assistant/tool and return "role param error" otherwise.
|
||||
*
|
||||
* Logic:
|
||||
* - When targetFormat !== "openai": always convert developer → system (Claude, Gemini, etc.).
|
||||
* - When targetFormat === "openai": convert only when preserveDeveloperRole === false.
|
||||
* This covers OpenAI-compatible providers (MiniMax, etc.) that use targetFormat "openai"
|
||||
* but do not accept the developer role; the per-model preserveDeveloperRole flag is set
|
||||
* via the dashboard "Compatibility" toggle ("Do not preserve developer role").
|
||||
* - When targetFormat === "openai" && preserveDeveloperRole !== false: keep developer (e.g. official OpenAI).
|
||||
*
|
||||
* @param messages - Array of messages
|
||||
* @param targetFormat - The target format (e.g., "openai", "claude", "gemini")
|
||||
* @returns Modified messages array
|
||||
* @param preserveDeveloperRole - For targetFormat openai: undefined/true = keep developer (legacy default); false = map to system (MiniMax and other OpenAI-compatible gateways that reject developer)
|
||||
*/
|
||||
export function normalizeDeveloperRole(
|
||||
messages: NormalizedMessage[] | unknown,
|
||||
targetFormat: string
|
||||
targetFormat: string,
|
||||
preserveDeveloperRole?: boolean
|
||||
): NormalizedMessage[] | unknown {
|
||||
if (!Array.isArray(messages)) return messages;
|
||||
|
||||
// For OpenAI format, keep developer role as-is (it's valid)
|
||||
// For all other formats, convert developer → system
|
||||
if (targetFormat === "openai") return messages;
|
||||
if (targetFormat === "openai" && preserveDeveloperRole !== false) return messages;
|
||||
|
||||
return messages.map((msg: NormalizedMessage) => {
|
||||
if (msg.role === "developer") {
|
||||
if (!msg || typeof msg !== "object") return msg;
|
||||
const role = typeof msg.role === "string" ? msg.role : "";
|
||||
if (role.toLowerCase() === "developer") {
|
||||
return { ...msg, role: "system" };
|
||||
}
|
||||
return msg;
|
||||
@@ -169,25 +178,25 @@ export function normalizeSystemRole(
|
||||
/**
|
||||
* Full role normalization pipeline.
|
||||
* Call this before sending the request to the provider.
|
||||
* Applies developer→system (when needed) then system→user for providers/models that do not support system role.
|
||||
*
|
||||
* @param messages - Array of messages
|
||||
* @param provider - Provider name/id
|
||||
* @param model - Model name
|
||||
* @param targetFormat - Target API format
|
||||
* @returns Normalized messages array
|
||||
* @param messages - Array of messages to normalize (or non-array, returned as-is)
|
||||
* @param provider - Provider id for capability lookup (e.g. system role support)
|
||||
* @param model - Model id for capability lookup
|
||||
* @param targetFormat - Target request format (e.g. "openai", "claude", "gemini"); see {@link normalizeDeveloperRole}
|
||||
* @param preserveDeveloperRole - Optional; see {@link normalizeDeveloperRole}. When false, developer role is mapped to system.
|
||||
* @returns Normalized messages array, or the original value if messages is not an array
|
||||
*/
|
||||
export function normalizeRoles(
|
||||
messages: NormalizedMessage[] | unknown,
|
||||
provider: string,
|
||||
model: string,
|
||||
targetFormat: string
|
||||
targetFormat: string,
|
||||
preserveDeveloperRole?: boolean
|
||||
): NormalizedMessage[] | unknown {
|
||||
if (!Array.isArray(messages)) return messages;
|
||||
|
||||
// Step 1: Normalize developer → system (for non-OpenAI formats)
|
||||
let result = normalizeDeveloperRole(messages, targetFormat);
|
||||
|
||||
// Step 2: Normalize system → user (for providers that don't support system role)
|
||||
let result = normalizeDeveloperRole(messages, targetFormat, preserveDeveloperRole);
|
||||
result = normalizeSystemRole(result, provider, model);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,26 +1,69 @@
|
||||
// Tool call helper functions for translator
|
||||
|
||||
// Generate unique tool call ID
|
||||
const ALPHANUM9 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
|
||||
// Generate unique tool call ID (default long form)
|
||||
export function generateToolCallId() {
|
||||
return `call_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 9)}`;
|
||||
}
|
||||
|
||||
// Ensure all tool_calls have id field and arguments is string (some providers require it)
|
||||
export function ensureToolCallIds(body) {
|
||||
// Generate 9-char [a-zA-Z0-9] id for providers that require it (e.g. Mistral)
|
||||
function generateToolCallId9(): string {
|
||||
let s = "";
|
||||
for (let i = 0; i < 9; i++) {
|
||||
s += ALPHANUM9[Math.floor(Math.random() * ALPHANUM9.length)];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/** @param options.use9CharId - When true, normalize ids to 9-char [a-zA-Z0-9] (e.g. Mistral); when false, only fix type/arguments, leave ids as-is */
|
||||
export function ensureToolCallIds(body, options?: { use9CharId?: boolean }) {
|
||||
if (!body.messages || !Array.isArray(body.messages)) return body;
|
||||
|
||||
for (const msg of body.messages) {
|
||||
if (msg.role === "assistant" && msg.tool_calls && Array.isArray(msg.tool_calls)) {
|
||||
for (const tc of msg.tool_calls) {
|
||||
if (!tc.id) {
|
||||
tc.id = generateToolCallId();
|
||||
}
|
||||
if (!tc.type) {
|
||||
tc.type = "function";
|
||||
}
|
||||
// Ensure arguments is JSON string, not object
|
||||
if (tc.function?.arguments && typeof tc.function.arguments !== "string") {
|
||||
tc.function.arguments = JSON.stringify(tc.function.arguments);
|
||||
const use9CharId = options?.use9CharId === true;
|
||||
|
||||
for (let i = 0; i < body.messages.length; i++) {
|
||||
const msg = body.messages[i];
|
||||
if (msg.role !== "assistant" || !msg.tool_calls || !Array.isArray(msg.tool_calls)) continue;
|
||||
|
||||
const used9 = new Set<string>();
|
||||
const newIdsInOrder: string[] = [];
|
||||
|
||||
for (const tc of msg.tool_calls) {
|
||||
if (!tc.type) {
|
||||
tc.type = "function";
|
||||
}
|
||||
if (tc.function?.arguments && typeof tc.function.arguments !== "string") {
|
||||
tc.function.arguments = JSON.stringify(tc.function.arguments);
|
||||
}
|
||||
if (use9CharId) {
|
||||
let newId: string;
|
||||
do {
|
||||
newId = generateToolCallId9();
|
||||
} while (used9.has(newId));
|
||||
used9.add(newId);
|
||||
newIdsInOrder.push(newId);
|
||||
tc.id = newId;
|
||||
} else {
|
||||
// Leave id as-is, only ensure it exists for later tool message matching
|
||||
const id =
|
||||
tc.id != null && String(tc.id).trim() !== "" ? String(tc.id) : generateToolCallId();
|
||||
tc.id = id;
|
||||
newIdsInOrder.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Tool responses (role "tool") follow in same order as tool_calls; set tool_call_id by index.
|
||||
// Stop when we hit another assistant so we only link tool messages that immediately follow this one.
|
||||
if (newIdsInOrder.length > 0) {
|
||||
let idx = 0;
|
||||
for (let j = i + 1; j < body.messages.length; j++) {
|
||||
const later = body.messages[j];
|
||||
if (later.role === "assistant") break;
|
||||
if (later.role !== "tool") continue;
|
||||
if (idx < newIdsInOrder.length) {
|
||||
later.tool_call_id = newIdsInOrder[idx];
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ function normalizeOpenAIResponsesRequest(body) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/** @param options.normalizeToolCallId - When true, use 9-char tool call ids (e.g. Mistral); when false, leave ids as-is */
|
||||
/** @param options.preserveDeveloperRole - undefined/true: keep developer for OpenAI format (default); false: map to system */
|
||||
// Translate request: source -> openai -> target
|
||||
export function translateRequest(
|
||||
sourceFormat,
|
||||
@@ -75,9 +77,12 @@ export function translateRequest(
|
||||
stream = true,
|
||||
credentials = null,
|
||||
provider = null,
|
||||
reqLogger = null
|
||||
reqLogger = null,
|
||||
options?: { normalizeToolCallId?: boolean; preserveDeveloperRole?: boolean }
|
||||
) {
|
||||
let result = body;
|
||||
const use9CharId = options?.normalizeToolCallId === true;
|
||||
const preserveDeveloperRole = options?.preserveDeveloperRole;
|
||||
|
||||
// Phase 2: Apply thinking budget control before normalization
|
||||
result = applyThinkingBudget(result);
|
||||
@@ -85,15 +90,23 @@ export function translateRequest(
|
||||
// Normalize thinking config: remove if lastMessage is not user
|
||||
normalizeThinkingConfig(result);
|
||||
|
||||
// Always ensure tool_calls have id (some providers require it)
|
||||
ensureToolCallIds(result);
|
||||
// Ensure tool_calls have id; optionally normalize to 9-char for providers like Mistral
|
||||
ensureToolCallIds(result, { use9CharId });
|
||||
|
||||
// Fix missing tool responses (insert empty tool_result if needed)
|
||||
fixMissingToolResponses(result);
|
||||
|
||||
// Normalize roles: developer→system for non-OpenAI, system→user for incompatible models
|
||||
// Normalize roles: developer→system unless preserved, system→user for incompatible models.
|
||||
// This handles (1) sourceFormat openai with messages containing developer → non-openai target
|
||||
// or preserveDeveloperRole=false, and (2) all other paths where result.messages already exists.
|
||||
if (result.messages && Array.isArray(result.messages)) {
|
||||
result.messages = normalizeRoles(result.messages, provider || "", model || "", targetFormat);
|
||||
result.messages = normalizeRoles(
|
||||
result.messages,
|
||||
provider || "",
|
||||
model || "",
|
||||
targetFormat,
|
||||
preserveDeveloperRole
|
||||
);
|
||||
}
|
||||
|
||||
// If same format, skip translation steps
|
||||
@@ -140,6 +153,28 @@ export function translateRequest(
|
||||
result = normalizeOpenAIResponsesRequest(result);
|
||||
}
|
||||
|
||||
// Second role normalization: only for OPENAI_RESPONSES. Here messages are built from input
|
||||
// after the translation step, so the first normalizeRoles (above) did not see them. For
|
||||
// sourceFormat openai with messages already on the body, the first block handles developer
|
||||
// → system (non-openai target or preserveDeveloperRole=false); no second pass needed.
|
||||
if (
|
||||
sourceFormat === FORMATS.OPENAI_RESPONSES &&
|
||||
result.messages &&
|
||||
Array.isArray(result.messages)
|
||||
) {
|
||||
result.messages = normalizeRoles(
|
||||
result.messages,
|
||||
provider || "",
|
||||
model || "",
|
||||
targetFormat,
|
||||
preserveDeveloperRole
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure unique tool_call ids on final payload (translators may have introduced duplicates)
|
||||
ensureToolCallIds(result, { use9CharId });
|
||||
fixMissingToolResponses(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ type StreamLogger = {
|
||||
type StreamCompletePayload = {
|
||||
status: number;
|
||||
usage: unknown;
|
||||
/** Minimal response body for call log (streaming: usage + note; non-streaming not used) */
|
||||
responseBody?: unknown;
|
||||
};
|
||||
|
||||
type StreamOptions = {
|
||||
@@ -51,6 +53,8 @@ type TranslateState = ReturnType<typeof initState> & {
|
||||
toolNameMap?: unknown;
|
||||
usage?: unknown;
|
||||
finishReason?: unknown;
|
||||
/** Accumulated message content for call log response body */
|
||||
accumulatedContent?: string;
|
||||
};
|
||||
|
||||
function getOpenAIIntermediateChunks(value: unknown): unknown[] {
|
||||
@@ -106,14 +110,21 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
let buffer = "";
|
||||
let usage = null;
|
||||
|
||||
// State for translate mode
|
||||
// State for translate mode (accumulatedContent for call log response body)
|
||||
const state: TranslateState | null =
|
||||
mode === STREAM_MODE.TRANSLATE
|
||||
? { ...(initState(sourceFormat) as TranslateState), provider, toolNameMap }
|
||||
? {
|
||||
...(initState(sourceFormat) as TranslateState),
|
||||
provider,
|
||||
toolNameMap,
|
||||
accumulatedContent: "",
|
||||
}
|
||||
: null;
|
||||
|
||||
// Track content length for usage estimation (both modes)
|
||||
let totalContentLength = 0;
|
||||
// Passthrough: accumulate content for call log response body
|
||||
let passthroughAccumulatedContent = "";
|
||||
|
||||
// Guard against duplicate [DONE] events — ensures exactly one per stream
|
||||
let doneSent = false;
|
||||
@@ -201,9 +212,10 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
if (extracted) {
|
||||
usage = extracted;
|
||||
}
|
||||
// Track content length from Responses format
|
||||
// Track content length and accumulate for call log
|
||||
if (parsed.delta && typeof parsed.delta === "string") {
|
||||
totalContentLength += parsed.delta.length;
|
||||
passthroughAccumulatedContent += parsed.delta;
|
||||
}
|
||||
} else if (isClaudeSSE) {
|
||||
// Claude SSE: extract usage, track content, forward as-is
|
||||
@@ -213,14 +225,23 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
// message_start carries input_tokens, message_delta carries output_tokens
|
||||
if (!usage) usage = {};
|
||||
if (extracted.prompt_tokens > 0) usage.prompt_tokens = extracted.prompt_tokens;
|
||||
if (extracted.completion_tokens > 0) usage.completion_tokens = extracted.completion_tokens;
|
||||
if (extracted.completion_tokens > 0)
|
||||
usage.completion_tokens = extracted.completion_tokens;
|
||||
if (extracted.total_tokens > 0) usage.total_tokens = extracted.total_tokens;
|
||||
if (extracted.cache_read_input_tokens) usage.cache_read_input_tokens = extracted.cache_read_input_tokens;
|
||||
if (extracted.cache_creation_input_tokens) usage.cache_creation_input_tokens = extracted.cache_creation_input_tokens;
|
||||
if (extracted.cache_read_input_tokens)
|
||||
usage.cache_read_input_tokens = extracted.cache_read_input_tokens;
|
||||
if (extracted.cache_creation_input_tokens)
|
||||
usage.cache_creation_input_tokens = extracted.cache_creation_input_tokens;
|
||||
}
|
||||
// Track content length and accumulate from Claude format
|
||||
if (parsed.delta?.text) {
|
||||
totalContentLength += parsed.delta.text.length;
|
||||
passthroughAccumulatedContent += parsed.delta.text;
|
||||
}
|
||||
if (parsed.delta?.thinking) {
|
||||
totalContentLength += parsed.delta.thinking.length;
|
||||
passthroughAccumulatedContent += parsed.delta.thinking;
|
||||
}
|
||||
// Track content length from Claude format
|
||||
if (parsed.delta?.text) totalContentLength += parsed.delta.text.length;
|
||||
if (parsed.delta?.thinking) totalContentLength += parsed.delta.thinking.length;
|
||||
} else {
|
||||
// Chat Completions: full sanitization pipeline
|
||||
parsed = sanitizeStreamingChunk(parsed);
|
||||
@@ -246,6 +267,10 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
if (content && typeof content === "string") {
|
||||
totalContentLength += content.length;
|
||||
}
|
||||
if (typeof delta?.content === "string")
|
||||
passthroughAccumulatedContent += delta.content;
|
||||
if (typeof delta?.reasoning_content === "string")
|
||||
passthroughAccumulatedContent += delta.reasoning_content;
|
||||
|
||||
const extracted = extractUsage(parsed);
|
||||
if (extracted) {
|
||||
@@ -301,23 +326,45 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Track content length for estimation (from various formats)
|
||||
// Include both regular content and reasoning/thinking content
|
||||
// Track content length and accumulate for call log (from raw provider chunk, so content is never missed)
|
||||
// Do this before translation so we capture content regardless of translator output shape
|
||||
|
||||
// Claude format
|
||||
if (parsed.delta?.text) {
|
||||
totalContentLength += parsed.delta.text.length;
|
||||
const t = parsed.delta.text;
|
||||
totalContentLength += t.length;
|
||||
if (state?.accumulatedContent !== undefined && typeof t === "string")
|
||||
state.accumulatedContent += t;
|
||||
}
|
||||
if (parsed.delta?.thinking) {
|
||||
totalContentLength += parsed.delta.thinking.length;
|
||||
const t = parsed.delta.thinking;
|
||||
totalContentLength += t.length;
|
||||
if (state?.accumulatedContent !== undefined && typeof t === "string")
|
||||
state.accumulatedContent += t;
|
||||
}
|
||||
|
||||
// OpenAI format
|
||||
if (parsed.choices?.[0]?.delta?.content) {
|
||||
totalContentLength += parsed.choices[0].delta.content.length;
|
||||
const c = parsed.choices[0].delta.content;
|
||||
if (typeof c === "string") {
|
||||
totalContentLength += c.length;
|
||||
if (state?.accumulatedContent !== undefined) state.accumulatedContent += c;
|
||||
} else if (Array.isArray(c)) {
|
||||
for (const part of c) {
|
||||
if (part?.text && typeof part.text === "string") {
|
||||
totalContentLength += part.text.length;
|
||||
if (state?.accumulatedContent !== undefined)
|
||||
state.accumulatedContent += part.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parsed.choices?.[0]?.delta?.reasoning_content) {
|
||||
totalContentLength += parsed.choices[0].delta.reasoning_content.length;
|
||||
const r = parsed.choices[0].delta.reasoning_content;
|
||||
if (typeof r === "string") {
|
||||
totalContentLength += r.length;
|
||||
if (state?.accumulatedContent !== undefined) state.accumulatedContent += r;
|
||||
}
|
||||
}
|
||||
|
||||
// Gemini format - may have multiple parts
|
||||
@@ -325,10 +372,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
for (const part of parsed.candidates[0].content.parts) {
|
||||
if (part.text && typeof part.text === "string") {
|
||||
totalContentLength += part.text.length;
|
||||
if (state?.accumulatedContent !== undefined) state.accumulatedContent += part.text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generic fallback: delta string, top-level content/text (e.g. some SSE payloads)
|
||||
if (state?.accumulatedContent !== undefined) {
|
||||
if (typeof (parsed as JsonRecord).delta === "string") {
|
||||
const d = (parsed as JsonRecord).delta as string;
|
||||
state.accumulatedContent += d;
|
||||
totalContentLength += d.length;
|
||||
}
|
||||
if (typeof (parsed as JsonRecord).content === "string") {
|
||||
const c = (parsed as JsonRecord).content as string;
|
||||
state.accumulatedContent += c;
|
||||
totalContentLength += c.length;
|
||||
}
|
||||
if (typeof (parsed as JsonRecord).text === "string") {
|
||||
const t = (parsed as JsonRecord).text as string;
|
||||
state.accumulatedContent += t;
|
||||
totalContentLength += t.length;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract usage
|
||||
const extracted = extractUsage(parsed);
|
||||
if (extracted) state.usage = extracted; // Keep original usage for logging
|
||||
@@ -344,6 +411,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
|
||||
if (translated?.length > 0) {
|
||||
for (const item of translated) {
|
||||
// Content for call log is accumulated only from parsed (above) to avoid double-counting;
|
||||
// do not add again from item here.
|
||||
|
||||
// Filter empty chunks
|
||||
if (!hasValuableContent(item, sourceFormat)) {
|
||||
continue; // Skip this empty chunk
|
||||
@@ -415,10 +485,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
status: "200 OK",
|
||||
}).catch(() => {});
|
||||
}
|
||||
// Notify caller for call log persistence
|
||||
// Notify caller for call log persistence (include full response body with accumulated content)
|
||||
if (onComplete) {
|
||||
try {
|
||||
onComplete({ status: 200, usage });
|
||||
const u = usage as Record<string, unknown> | null;
|
||||
const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0);
|
||||
const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0);
|
||||
const content = passthroughAccumulatedContent.trim() || "";
|
||||
const responseBody = {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
role: "assistant",
|
||||
content,
|
||||
},
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: prompt,
|
||||
completion_tokens: completion,
|
||||
total_tokens: prompt + completion,
|
||||
},
|
||||
_streamed: true,
|
||||
};
|
||||
onComplete({ status: 200, usage, responseBody });
|
||||
} catch {}
|
||||
}
|
||||
return;
|
||||
@@ -428,6 +518,33 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
if (buffer.trim()) {
|
||||
const parsed = parseSSELine(buffer.trim());
|
||||
if (parsed && !parsed.done) {
|
||||
// Extract usage from remaining buffer — if the usage-bearing event
|
||||
// (e.g. response.completed) is the last SSE line, it ends up here
|
||||
// in the flush handler where extractUsage was not called.
|
||||
// Non-destructive merge: some providers send usage across multiple
|
||||
// events (e.g. prompt_tokens in message_start, completion_tokens
|
||||
// in message_delta). Direct assignment would lose earlier data.
|
||||
const extracted = extractUsage(parsed);
|
||||
if (extracted) {
|
||||
if (!state.usage) {
|
||||
state.usage = extracted;
|
||||
} else {
|
||||
if (extracted.prompt_tokens > 0)
|
||||
state.usage.prompt_tokens = extracted.prompt_tokens;
|
||||
if (extracted.completion_tokens > 0)
|
||||
state.usage.completion_tokens = extracted.completion_tokens;
|
||||
if (extracted.total_tokens > 0) state.usage.total_tokens = extracted.total_tokens;
|
||||
if (extracted.cache_read_input_tokens > 0)
|
||||
state.usage.cache_read_input_tokens = extracted.cache_read_input_tokens;
|
||||
if (extracted.cache_creation_input_tokens > 0)
|
||||
state.usage.cache_creation_input_tokens = extracted.cache_creation_input_tokens;
|
||||
if (extracted.cached_tokens > 0)
|
||||
state.usage.cached_tokens = extracted.cached_tokens;
|
||||
if (extracted.reasoning_tokens > 0)
|
||||
state.usage.reasoning_tokens = extracted.reasoning_tokens;
|
||||
}
|
||||
}
|
||||
|
||||
const translated = translateResponse(targetFormat, sourceFormat, parsed, state);
|
||||
|
||||
// Log OpenAI intermediate chunks
|
||||
@@ -497,10 +614,30 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
status: "200 OK",
|
||||
}).catch(() => {});
|
||||
}
|
||||
// Notify caller for call log persistence
|
||||
// Notify caller for call log persistence (include full response body with accumulated content)
|
||||
if (onComplete) {
|
||||
try {
|
||||
onComplete({ status: 200, usage: state?.usage });
|
||||
const u = state?.usage as Record<string, unknown> | null | undefined;
|
||||
const prompt = Number(u?.prompt_tokens ?? u?.input_tokens ?? 0);
|
||||
const completion = Number(u?.completion_tokens ?? u?.output_tokens ?? 0);
|
||||
const content = (state?.accumulatedContent ?? "").trim() || "";
|
||||
const responseBody = {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
role: "assistant",
|
||||
content,
|
||||
},
|
||||
},
|
||||
],
|
||||
usage: {
|
||||
prompt_tokens: prompt,
|
||||
completion_tokens: completion,
|
||||
total_tokens: prompt + completion,
|
||||
},
|
||||
_streamed: true,
|
||||
};
|
||||
onComplete({ status: 200, usage: state?.usage, responseBody });
|
||||
} catch {}
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -103,7 +103,7 @@ function cleanPerfMetrics(data) {
|
||||
|
||||
// Format output as SSE
|
||||
export function formatSSE(data, sourceFormat) {
|
||||
if (data === null || data === undefined) return "data: null\n\n";
|
||||
if (data === null || data === undefined) return ""; // Skip null/undefined — never send `data: null` (#483)
|
||||
if (data && data.done) return "data: [DONE]\n\n";
|
||||
|
||||
// OpenAI Responses API format
|
||||
|
||||
@@ -400,8 +400,10 @@ export function logUsage(provider, usage, model = null, connectionId = null, api
|
||||
console.log(msg);
|
||||
|
||||
// Save to usage DB
|
||||
// input = total input tokens (non-cached + cache_read + cache_creation)
|
||||
// This ensures analytics show correct totals for heavily-cached requests
|
||||
const tokens = {
|
||||
input: inTokens,
|
||||
input: inTokens + (cacheRead || 0) + (cacheCreation || 0),
|
||||
output: outTokens,
|
||||
cacheRead: cacheRead || 0,
|
||||
cacheCreation: cacheCreation || 0,
|
||||
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.7.0",
|
||||
"version": "2.9.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "omniroute",
|
||||
"version": "2.7.0",
|
||||
"version": "2.9.0",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"workspaces": [
|
||||
@@ -6707,9 +6707,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
|
||||
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "omniroute",
|
||||
"version": "2.7.4",
|
||||
"version": "2.9.0",
|
||||
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
||||
BIN
public/providers/alicode-intl.png
Normal file
BIN
public/providers/alicode-intl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
BIN
public/providers/alicode.png
Normal file
BIN
public/providers/alicode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
@@ -15,6 +15,7 @@ const DOC_SOURCE_FILES = [
|
||||
"FEATURES.md",
|
||||
"TROUBLESHOOTING.md",
|
||||
"USER_GUIDE.md",
|
||||
"VM_DEPLOYMENT_GUIDE.md",
|
||||
];
|
||||
|
||||
const LOCALE_SPECS = [
|
||||
@@ -288,6 +289,15 @@ const LOCALE_SPECS = [
|
||||
readmeName: "Filipino",
|
||||
docsName: "Filipino",
|
||||
},
|
||||
{
|
||||
code: "cs",
|
||||
googleTl: "cs",
|
||||
label: "CS",
|
||||
flag: "🇨🇿",
|
||||
languageName: "Čeština",
|
||||
readmeName: "Čeština",
|
||||
docsName: "Čeština",
|
||||
},
|
||||
];
|
||||
|
||||
const EXISTING_README_CODES = new Set(["pt-BR", "es", "fr", "it", "ru", "zh-CN", "de"]);
|
||||
|
||||
@@ -16,7 +16,8 @@ const { dashboardPort } = runtimePorts;
|
||||
const env = bootstrapEnv();
|
||||
|
||||
const args = ["./node_modules/next/dist/bin/next", mode, "--port", String(dashboardPort)];
|
||||
if (mode === "dev") {
|
||||
// Default: use webpack (stable). Set OMNIROUTE_USE_TURBOPACK=1 to use Turbopack (faster dev).
|
||||
if (mode === "dev" && process.env.OMNIROUTE_USE_TURBOPACK !== "1") {
|
||||
args.splice(2, 0, "--webpack");
|
||||
}
|
||||
|
||||
|
||||
196
src/app/(dashboard)/dashboard/analytics/SearchAnalyticsTab.tsx
Normal file
196
src/app/(dashboard)/dashboard/analytics/SearchAnalyticsTab.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
/**
|
||||
* Search Analytics Tab
|
||||
*
|
||||
* Shows search request stats from call_logs (request_type = 'search'),
|
||||
* provider breakdown, cache hit rate, and cost summary.
|
||||
*/
|
||||
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
interface SearchStats {
|
||||
total: number;
|
||||
today: number;
|
||||
cached: number;
|
||||
errors: number;
|
||||
totalCostUsd: number;
|
||||
byProvider: Record<string, { count: number; costUsd: number }>;
|
||||
last24h: Array<{ hour: string; count: number }>;
|
||||
cacheHitRate: number;
|
||||
avgDurationMs: number;
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
icon,
|
||||
label,
|
||||
value,
|
||||
sub,
|
||||
}: {
|
||||
icon: string;
|
||||
label: string;
|
||||
value: string | number;
|
||||
sub?: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="card p-4 flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2 text-text-muted text-sm">
|
||||
<span className="material-symbols-outlined text-[18px]">{icon}</span>
|
||||
{label}
|
||||
</div>
|
||||
<div className="text-2xl font-bold text-text">{value}</div>
|
||||
{sub && <div className="text-xs text-text-muted">{sub}</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ProviderBar({
|
||||
provider,
|
||||
count,
|
||||
total,
|
||||
costUsd,
|
||||
}: {
|
||||
provider: string;
|
||||
count: number;
|
||||
total: number;
|
||||
costUsd: number;
|
||||
}) {
|
||||
const pct = total > 0 ? Math.round((count / total) * 100) : 0;
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex justify-between text-sm">
|
||||
<span className="font-medium text-text">{provider}</span>
|
||||
<span className="text-text-muted">
|
||||
{count} queries · ${costUsd.toFixed(4)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="h-2 rounded-full bg-bg-muted overflow-hidden">
|
||||
<div
|
||||
className="h-full rounded-full bg-primary transition-all"
|
||||
style={{ width: `${pct}%` }}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-xs text-text-muted text-right">{pct}%</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SearchAnalyticsTab() {
|
||||
const [stats, setStats] = useState<SearchStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/v1/search/analytics")
|
||||
.then((r) => r.json())
|
||||
.then((d) => {
|
||||
setStats(d);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
setError(e.message);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-16 text-text-muted">
|
||||
<span className="material-symbols-outlined animate-spin mr-2">progress_activity</span>
|
||||
Loading search analytics…
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !stats) {
|
||||
return (
|
||||
<div className="card p-6 text-center text-text-muted">
|
||||
<span className="material-symbols-outlined text-[32px] mb-2 block">search_off</span>
|
||||
{error || "No search data available yet."}
|
||||
<p className="text-xs mt-2">
|
||||
Search requests will appear here after the first search via /v1/search.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const providers = Object.entries(stats.byProvider).sort(([, a], [, b]) => b.count - a.count);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* KPI Cards */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
||||
<StatCard
|
||||
icon="manage_search"
|
||||
label="Total Searches"
|
||||
value={stats.total.toLocaleString()}
|
||||
sub={`${stats.today} today`}
|
||||
/>
|
||||
<StatCard
|
||||
icon="cached"
|
||||
label="Cache Hit Rate"
|
||||
value={`${stats.cacheHitRate}%`}
|
||||
sub={`${stats.cached} cached requests`}
|
||||
/>
|
||||
<StatCard
|
||||
icon="attach_money"
|
||||
label="Total Cost"
|
||||
value={`$${stats.totalCostUsd.toFixed(4)}`}
|
||||
sub="search API costs"
|
||||
/>
|
||||
<StatCard
|
||||
icon="timer"
|
||||
label="Avg Response"
|
||||
value={`${stats.avgDurationMs}ms`}
|
||||
sub={stats.errors > 0 ? `${stats.errors} errors` : "No errors"}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Provider Breakdown */}
|
||||
{providers.length > 0 && (
|
||||
<div className="card p-5">
|
||||
<h3 className="font-semibold text-text mb-4 flex items-center gap-2">
|
||||
<span className="material-symbols-outlined text-primary text-[20px]">hub</span>
|
||||
Provider Breakdown
|
||||
</h3>
|
||||
<div className="flex flex-col gap-4">
|
||||
{providers.map(([prov, data]) => (
|
||||
<ProviderBar
|
||||
key={prov}
|
||||
provider={prov}
|
||||
count={data.count}
|
||||
total={stats.total}
|
||||
costUsd={data.costUsd}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty state */}
|
||||
{stats.total === 0 && (
|
||||
<div className="card p-8 text-center text-text-muted">
|
||||
<span className="material-symbols-outlined text-[48px] mb-3 block text-primary opacity-50">
|
||||
travel_explore
|
||||
</span>
|
||||
<p className="font-medium text-text">No searches yet</p>
|
||||
<p className="text-sm mt-1">
|
||||
Use <code className="bg-bg-muted px-1 rounded">POST /v1/search</code> to start routing
|
||||
web searches.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Free tier note */}
|
||||
<div className="text-xs text-text-muted border border-border rounded-lg p-3 flex items-start gap-2">
|
||||
<span className="material-symbols-outlined text-[16px] text-green-500 mt-0.5">
|
||||
check_circle
|
||||
</span>
|
||||
<span>
|
||||
<strong>Free tier available:</strong> Serper (2,500/mo), Brave (2,000/mo), Exa (1,000/mo),
|
||||
Tavily (1,000/mo) — total 6,500+ free searches/month with automatic failover.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,15 +3,17 @@
|
||||
import { useState, Suspense } from "react";
|
||||
import { UsageAnalytics, CardSkeleton, SegmentedControl } from "@/shared/components";
|
||||
import EvalsTab from "../usage/components/EvalsTab";
|
||||
import SearchAnalyticsTab from "./SearchAnalyticsTab";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
export default function AnalyticsPage() {
|
||||
const [activeTab, setActiveTab] = useState("overview");
|
||||
const t = useTranslations("analytics");
|
||||
|
||||
const tabDescriptions = {
|
||||
const tabDescriptions: Record<string, string> = {
|
||||
overview: t("overviewDescription"),
|
||||
evals: t("evalsDescription"),
|
||||
search: "Search request analytics — provider breakdown, cache hit rate, and cost tracking.",
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -29,6 +31,7 @@ export default function AnalyticsPage() {
|
||||
options={[
|
||||
{ value: "overview", label: t("overview") },
|
||||
{ value: "evals", label: t("evals") },
|
||||
{ value: "search", label: "Search" },
|
||||
]}
|
||||
value={activeTab}
|
||||
onChange={setActiveTab}
|
||||
@@ -40,6 +43,7 @@ export default function AnalyticsPage() {
|
||||
</Suspense>
|
||||
)}
|
||||
{activeTab === "evals" && <EvalsTab />}
|
||||
{activeTab === "search" && <SearchAnalyticsTab />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -267,6 +267,18 @@ export default function CLIToolsPageClient({ machineId }) {
|
||||
/>
|
||||
);
|
||||
default:
|
||||
// #487: Any tool with configType "mitm" should use the MITM card (Start/Stop controls)
|
||||
if (tool.configType === "mitm") {
|
||||
return (
|
||||
<AntigravityToolCard
|
||||
key={toolId}
|
||||
{...commonProps}
|
||||
activeProviders={getActiveProviders()}
|
||||
hasActiveProviders={hasActiveProviders}
|
||||
cloudEnabled={cloudEnabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<DefaultToolCard
|
||||
key={toolId}
|
||||
|
||||
@@ -41,7 +41,7 @@ export default function AntigravityToolCard({
|
||||
|
||||
const loadSavedMappings = async () => {
|
||||
try {
|
||||
const res = await fetch("/api/cli-tools/antigravity-mitm/alias?tool=antigravity");
|
||||
const res = await fetch(`/api/cli-tools/antigravity-mitm/alias?tool=${tool.id}`);
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
const aliases = data.aliases || {};
|
||||
@@ -187,7 +187,7 @@ export default function AntigravityToolCard({
|
||||
const res = await fetch("/api/cli-tools/antigravity-mitm/alias", {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ tool: "antigravity", mappings: modelMappings }),
|
||||
body: JSON.stringify({ tool: tool.id, mappings: modelMappings }),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
@@ -211,7 +211,7 @@ export default function AntigravityToolCard({
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="size-8 flex items-center justify-center shrink-0">
|
||||
<Image
|
||||
src="/providers/antigravity.png"
|
||||
src={tool.image || "/providers/antigravity.png"}
|
||||
alt={tool.name}
|
||||
width={32}
|
||||
height={32}
|
||||
@@ -235,7 +235,7 @@ export default function AntigravityToolCard({
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-text-muted truncate">{t("toolDescriptions.antigravity")}</p>
|
||||
<p className="text-xs text-text-muted truncate">{tool.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
@@ -306,7 +306,7 @@ export default function AntigravityToolCard({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{tool.defaultModels.map((model) => (
|
||||
{(tool.defaultModels || []).map((model) => (
|
||||
<div key={model.alias} className="flex items-center gap-2">
|
||||
<span className="w-32 shrink-0 text-sm font-semibold text-text-main text-right">
|
||||
{model.name}
|
||||
@@ -355,25 +355,33 @@ export default function AntigravityToolCard({
|
||||
)}
|
||||
|
||||
{/* When stopped: how it works */}
|
||||
{!isRunning && (
|
||||
<div className="flex flex-col gap-1.5 px-1">
|
||||
<p className="text-xs text-text-muted">
|
||||
<span className="font-medium text-text-main">{t("howItWorks")}</span>{" "}
|
||||
{t("antigravityHowWorksDesc")}
|
||||
</p>
|
||||
<div className="flex flex-col gap-0.5 text-[11px] text-text-muted">
|
||||
<span>{t("antigravityStep1")}</span>
|
||||
<span>
|
||||
{t("antigravityStep2Prefix")}{" "}
|
||||
<code className="text-[10px] bg-surface px-1 rounded">
|
||||
daily-cloudcode-pa.googleapis.com
|
||||
</code>{" "}
|
||||
{t("antigravityStep2Suffix")}
|
||||
</span>
|
||||
<span>{t("antigravityStep3")}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isRunning &&
|
||||
(() => {
|
||||
// Dynamic MITM instructions per tool (#505)
|
||||
const mitmDomains: Record<string, string> = {
|
||||
antigravity: "daily-cloudcode-pa.googleapis.com",
|
||||
kiro: "api.anthropic.com",
|
||||
};
|
||||
const toolName = tool.name || tool.id;
|
||||
const domain = mitmDomains[tool.id] || mitmDomains.antigravity;
|
||||
return (
|
||||
<div className="flex flex-col gap-1.5 px-1">
|
||||
<p className="text-xs text-text-muted">
|
||||
<span className="font-medium text-text-main">{t("howItWorks")}</span>{" "}
|
||||
{t("mitmHowWorksDesc", { toolName })}
|
||||
</p>
|
||||
<div className="flex flex-col gap-0.5 text-[11px] text-text-muted">
|
||||
<span>{t("mitmStep1")}</span>
|
||||
<span>
|
||||
{t("mitmStep2Prefix")}{" "}
|
||||
<code className="text-[10px] bg-surface px-1 rounded">{domain}</code>{" "}
|
||||
{t("mitmStep2Suffix")}
|
||||
</span>
|
||||
<span>{t("mitmStep3", { toolName })}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1181,6 +1181,12 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
|
||||
const [config, setConfig] = useState(combo?.config || {});
|
||||
const [showStrategyNudge, setShowStrategyNudge] = useState(false);
|
||||
const strategyChangeMountedRef = useRef(false);
|
||||
// Agent features (#399 / #401 / #454)
|
||||
const [agentSystemMessage, setAgentSystemMessage] = useState<string>(combo?.system_message || "");
|
||||
const [agentToolFilter, setAgentToolFilter] = useState<string>(combo?.tool_filter_regex || "");
|
||||
const [agentContextCache, setAgentContextCache] = useState<boolean>(
|
||||
!!combo?.context_cache_protection
|
||||
);
|
||||
|
||||
// DnD state
|
||||
const hasPricingForModel = useCallback(
|
||||
@@ -1532,6 +1538,14 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
|
||||
saveData.config = configToSave;
|
||||
}
|
||||
|
||||
// Agent features (#399 / #401 / #454)
|
||||
if (agentSystemMessage.trim()) saveData.system_message = agentSystemMessage.trim();
|
||||
else delete saveData.system_message;
|
||||
if (agentToolFilter.trim()) saveData.tool_filter_regex = agentToolFilter.trim();
|
||||
else delete saveData.tool_filter_regex;
|
||||
if (agentContextCache) saveData.context_cache_protection = true;
|
||||
else delete saveData.context_cache_protection;
|
||||
|
||||
await onSave(saveData);
|
||||
setSaving(false);
|
||||
};
|
||||
@@ -2052,6 +2066,72 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Agent Features (#399 / #401 / #454) */}
|
||||
<div className="flex flex-col gap-2 p-3 bg-black/[0.02] dark:bg-white/[0.02] rounded-lg border border-black/5 dark:border-white/5">
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
<span className="material-symbols-outlined text-[14px] text-primary">smart_toy</span>
|
||||
<p className="text-xs font-medium">Agent Features</p>
|
||||
<span className="text-[10px] text-text-muted">
|
||||
— optional, for agent/tool workflows
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* System Message Override */}
|
||||
<div>
|
||||
<label className="text-[11px] font-medium text-text-muted block mb-0.5">
|
||||
System Message Override
|
||||
</label>
|
||||
<textarea
|
||||
rows={2}
|
||||
value={agentSystemMessage}
|
||||
onChange={(e) => setAgentSystemMessage(e.target.value)}
|
||||
placeholder="Override the system prompt for all requests routed through this combo…"
|
||||
className="w-full text-xs py-1.5 px-2 rounded border border-black/10 dark:border-white/10 bg-transparent focus:border-primary focus:outline-none resize-none"
|
||||
/>
|
||||
<p className="text-[10px] text-text-muted mt-0.5">
|
||||
Replaces any system message sent by the client. Leave empty to pass through client
|
||||
system messages.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Tool Filter Regex */}
|
||||
<div>
|
||||
<label className="text-[11px] font-medium text-text-muted block mb-0.5">
|
||||
Tool Filter Regex
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={agentToolFilter}
|
||||
onChange={(e) => setAgentToolFilter(e.target.value)}
|
||||
placeholder="e.g. ^(bash|computer)$"
|
||||
className="w-full text-xs py-1.5 px-2 rounded border border-black/10 dark:border-white/10 bg-transparent focus:border-primary focus:outline-none font-mono"
|
||||
/>
|
||||
<p className="text-[10px] text-text-muted mt-0.5">
|
||||
Only tools whose name matches this regex are forwarded to the provider. Leave empty
|
||||
to forward all tools.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Context Cache Protection */}
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div>
|
||||
<label className="text-[11px] font-medium text-text-muted block">
|
||||
Context Cache Protection
|
||||
</label>
|
||||
<p className="text-[10px] text-text-muted">
|
||||
Pins the provider/model across turns to preserve cache sessions. Internal tags are
|
||||
stripped before forwarding to the provider.
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={agentContextCache}
|
||||
onChange={(e) => setAgentContextCache(e.target.checked)}
|
||||
className="accent-primary shrink-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions */}
|
||||
<div className="flex gap-2 pt-1">
|
||||
<Button onClick={onClose} variant="ghost" fullWidth size="sm">
|
||||
|
||||
@@ -1,27 +1,135 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { RequestLoggerV2, ProxyLogger, SegmentedControl } from "@/shared/components";
|
||||
import ConsoleLogViewer from "@/shared/components/ConsoleLogViewer";
|
||||
import AuditLogTab from "./AuditLogTab";
|
||||
import { useTranslations } from "next-intl";
|
||||
|
||||
const TIME_RANGES = [
|
||||
{ label: "1h", hours: 1 },
|
||||
{ label: "6h", hours: 6 },
|
||||
{ label: "12h", hours: 12 },
|
||||
{ label: "24h", hours: 24 },
|
||||
];
|
||||
|
||||
const TAB_TO_LOG_TYPE: Record<string, string> = {
|
||||
"request-logs": "request-logs",
|
||||
"proxy-logs": "proxy-logs",
|
||||
"audit-logs": "call-logs",
|
||||
console: "call-logs",
|
||||
};
|
||||
|
||||
export default function LogsPage() {
|
||||
const [activeTab, setActiveTab] = useState("request-logs");
|
||||
const [showExport, setShowExport] = useState(false);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
const t = useTranslations("logs");
|
||||
|
||||
useEffect(() => {
|
||||
function handleClickOutside(e: MouseEvent) {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
|
||||
setShowExport(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", handleClickOutside);
|
||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||
}, []);
|
||||
|
||||
async function handleExport(hours: number) {
|
||||
setExporting(true);
|
||||
setShowExport(false);
|
||||
try {
|
||||
const logType = TAB_TO_LOG_TYPE[activeTab] || "call-logs";
|
||||
const res = await fetch(`/api/logs/export?hours=${hours}&type=${logType}`);
|
||||
if (!res.ok) throw new Error("Export failed");
|
||||
const blob = await res.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `omniroute-${logType}-${hours}h-${new Date().toISOString().slice(0, 10)}.json`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
} catch (err) {
|
||||
console.error("Export failed:", err);
|
||||
} finally {
|
||||
setExporting(false);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
<SegmentedControl
|
||||
options={[
|
||||
{ value: "request-logs", label: t("requestLogs") },
|
||||
{ value: "proxy-logs", label: t("proxyLogs") },
|
||||
{ value: "audit-logs", label: t("auditLog") },
|
||||
{ value: "console", label: t("console") },
|
||||
]}
|
||||
value={activeTab}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
<div className="flex items-center justify-between gap-4 flex-wrap">
|
||||
<SegmentedControl
|
||||
options={[
|
||||
{ value: "request-logs", label: t("requestLogs") },
|
||||
{ value: "proxy-logs", label: t("proxyLogs") },
|
||||
{ value: "audit-logs", label: t("auditLog") },
|
||||
{ value: "console", label: t("console") },
|
||||
]}
|
||||
value={activeTab}
|
||||
onChange={setActiveTab}
|
||||
/>
|
||||
|
||||
<div className="relative" ref={dropdownRef}>
|
||||
<button
|
||||
id="export-logs-btn"
|
||||
onClick={() => setShowExport(!showExport)}
|
||||
disabled={exporting}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg
|
||||
bg-[var(--card-bg,#1e1e2e)] border border-[var(--border,#333)]
|
||||
text-[var(--text-secondary,#aaa)] hover:text-[var(--text-primary,#fff)]
|
||||
hover:border-[var(--accent,#7c3aed)] transition-all duration-200
|
||||
disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.5"
|
||||
>
|
||||
<path
|
||||
d="M8 2v8m0 0l-3-3m3 3l3-3M3 12h10"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
{exporting ? "Exporting..." : "Export"}
|
||||
</button>
|
||||
|
||||
{showExport && (
|
||||
<div
|
||||
className="absolute right-0 top-full mt-1 z-50 min-w-[140px] rounded-lg
|
||||
bg-[var(--card-bg,#1e1e2e)] border border-[var(--border,#333)]
|
||||
shadow-xl overflow-hidden animate-in fade-in"
|
||||
>
|
||||
<div className="px-3 py-2 text-xs text-[var(--text-muted,#666)] border-b border-[var(--border,#333)] font-medium">
|
||||
Time Range
|
||||
</div>
|
||||
{TIME_RANGES.map((range) => (
|
||||
<button
|
||||
key={range.hours}
|
||||
id={`export-${range.hours}h-btn`}
|
||||
onClick={() => handleExport(range.hours)}
|
||||
className="w-full px-3 py-2 text-sm text-left hover:bg-[var(--hover-bg,#2a2a3e)]
|
||||
text-[var(--text-secondary,#aaa)] hover:text-[var(--text-primary,#fff)]
|
||||
transition-colors flex items-center justify-between"
|
||||
>
|
||||
<span>Last {range.label}</span>
|
||||
<span className="text-xs text-[var(--text-muted,#666)]">
|
||||
{range.hours === 24 ? "default" : ""}
|
||||
</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
{activeTab === "request-logs" && <RequestLoggerV2 />}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user