fix(build): remove node: protocol prefix from all src/ imports (#turbopack-compat)

Turbopack (Next.js 15) does not process node: URL prefixes correctly when
bundling server-side files that get transitively included. Removed the node:
prefix from 17 files:

- src/lib/db/migrationRunner.ts (node:fs, node:path, node:url)
- src/lib/db/core.ts (node:path, node:fs)
- src/lib/db/backup.ts (node:path, node:fs)
- src/lib/db/prompts.ts (node:fs)
- src/lib/dataPaths.ts (node:path, node:os)
- src/app/api/settings/route.ts
- src/app/api/storage/health/route.ts
- src/app/api/oauth/[provider]/[action]/route.ts
- src/app/api/db-backups/{exportAll,import,export}/route.ts
- src/shared/middleware/correlationId.ts
- src/shared/utils/requestId.ts
- src/lib/apiBridgeServer.ts
- src/lib/cacheLayer.ts
- src/lib/semanticCache.ts
- src/lib/oauth/providers/kimi-coding.ts

Also updated generate-release.md: Docker Hub sync and dual-VPS deploy
are now mandatory steps in every release.
This commit is contained in:
diegosouzapw
2026-03-17 04:24:46 -03:00
parent a8ab16a720
commit 3cd762bdf5
25 changed files with 113 additions and 54 deletions

View File

@@ -85,12 +85,49 @@ git push origin main --tags
gh release create v2.x.y --title "v2.x.y — summary" --notes "..."
```
### 8. Deploy to VPS (if requested)
### 8. 🐳 Trigger Docker Hub build (MANDATORY — keep npm and Docker in sync)
See `/deploy-vps` workflow for Akamai VPS or use npm for local VPS:
> **CRITICAL**: Docker Hub and npm MUST always publish the same version.
> The Docker image is built automatically via GitHub Actions when a new tag is pushed.
> After pushing the tag in step 5-6, **verify the workflow runs**:
```bash
ssh root@<VPS_IP> "npm install -g omniroute@2.x.y && pm2 restart omniroute"
# Verify the Docker workflow triggered
gh run list --repo diegosouzapw/OmniRoute --workflow docker-publish.yml --limit 3
# Wait for the Docker build to complete (usually 510 min)
gh run watch --repo diegosouzapw/OmniRoute
# After completion, verify on Docker Hub:
# https://hub.docker.com/r/diegosouzapw/omniroute/tags
```
If the Docker build was not triggered automatically, trigger it manually:
```bash
gh workflow run docker-publish.yml --repo diegosouzapw/OmniRoute --ref v2.x.y
```
### 9. Deploy to BOTH VPS environments (MANDATORY)
> Always deploy to **both** environments after every release.
> See `/deploy-vps` workflow for detailed steps.
```bash
# Build and pack locally
cd /home/diegosouzapw/dev/proxys/9router && npm run build:cli && npm pack --ignore-scripts
# Deploy to LOCAL VPS (192.168.0.15)
scp omniroute-*.tgz root@192.168.0.15:/tmp/
ssh root@192.168.0.15 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && pm2 restart omniroute && pm2 save"
# Deploy to AKAMAI VPS (69.164.221.35)
scp omniroute-*.tgz root@69.164.221.35:/tmp/
ssh root@69.164.221.35 "npm install -g /tmp/omniroute-*.tgz --ignore-scripts && pm2 restart omniroute && pm2 save"
# Verify both
curl -s -o /dev/null -w "LOCAL: HTTP %{http_code}\n" http://192.168.0.15:20128/
curl -s -o /dev/null -w "AKAMAI: HTTP %{http_code}\n" http://69.164.221.35:20128/
```
## Notes

View File

@@ -21,8 +21,8 @@ This workflow fetches all open issues from the project's GitHub repository, clas
// turbo
- Run: `gh issue list --repo <owner>/<repo> --state open --limit 100 --json number,title,labels,body,comments,createdAt,author`
- Parse the JSON output to get a list of all open issues
- Run: `gh issue list --repo <owner>/<repo> --state open --limit 500 --json number,title,labels,body,comments,createdAt,author`
- Parse the JSON output to get a list of **all** open issues
- Sort by oldest first (FIFO)
### 3. Classify Each Issue

View File

@@ -18,7 +18,11 @@ This workflow fetches all open PRs from the project's GitHub repository, perform
### 2. Fetch Open Pull Requests
- Navigate to `https://github.com/<owner>/<repo>/pulls` and scrape all open PRs
// turbo
- Run: `gh pr list --repo <owner>/<repo> --state open --limit 500 --json number,title,author,headRefName,body,createdAt,additions,deletions,files`
- This fetches **all** open PRs without restriction. Get the diff for each with:
`gh pr diff <NUMBER> --repo <owner>/<repo>`
- For each open PR, collect:
- PR number, title, author, branch, number of commits, date
- PR description/body

View File

@@ -4,6 +4,17 @@
---
## [2.6.6] — 2026-03-17
> Hotfix: Turbopack/Docker compatibility — remove `node:` protocol from all `src/` imports.
### 🐛 Bug Fixes
- **fix(build)**: Removed `node:` protocol prefix from `import` statements in 17 files under `src/`. The `node:fs`, `node:path`, `node:url`, `node:os` etc. imports caused `Ecmascript file had an error` on Turbopack builds (Next.js 15 Docker) and on upgrades from older npm global installs. Affected files: `migrationRunner.ts`, `core.ts`, `backup.ts`, `prompts.ts`, `dataPaths.ts`, and 12 others in `src/app/api/` and `src/lib/`.
- **chore(workflow)**: Updated `generate-release.md` to make Docker Hub sync and dual-VPS deploy **mandatory** steps in every release.
---
## [2.6.5] — 2026-03-17
> Sprint: reasoning model param filtering, local provider 404 fix, Kilo Gateway provider, dependency bumps.

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: OmniRoute API
version: 2.6.5
version: 2.6.6
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,

5
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "2.6.5",
"version": "2.6.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "2.6.5",
"version": "2.6.6",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [
@@ -6866,6 +6866,7 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,

View File

@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "2.6.5",
"version": "2.6.6",
"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": {

View File

@@ -81,29 +81,36 @@ const PROVIDER_MODELS: Record<
{ id: "openai/dall-e-2", name: "DALL-E 2" },
],
},
{ id: "xai", name: "xAI (Grok)", models: [{ id: "xai/grok-2-image", name: "Grok 2 Image" }] },
{
id: "xai",
name: "xAI (Grok)",
models: [{ id: "xai/grok-2-image-1212", name: "Grok 2 Image" }],
},
{
id: "together",
name: "Together AI",
models: [
{ id: "together/stable-diffusion-xl", name: "SDXL" },
{ id: "together/FLUX.1-schnell-Free", name: "FLUX.1 Schnell" },
{ id: "together/stabilityai/stable-diffusion-xl-base-1.0", name: "SDXL" },
{ id: "together/black-forest-labs/FLUX.1-schnell-Free", name: "FLUX.1 Schnell" },
],
},
{
id: "fireworks",
name: "Fireworks AI",
models: [
{ id: "fireworks/stable-diffusion-xl-1024-v1-0", name: "SDXL 1024" },
{ id: "fireworks/flux-1-dev-fp8", name: "FLUX.1 Dev" },
{
id: "fireworks/accounts/fireworks/models/stable-diffusion-xl-1024-v1-0",
name: "SDXL 1024",
},
{ id: "fireworks/accounts/fireworks/models/flux-1-dev-fp8", name: "FLUX.1 Dev" },
],
},
{
id: "nebius",
name: "Nebius AI",
models: [
{ id: "nebius/flux-dev", name: "FLUX Dev" },
{ id: "nebius/sdxl", name: "SDXL" },
{ id: "nebius/black-forest-labs/flux-dev", name: "FLUX Dev" },
{ id: "nebius/black-forest-labs/flux-schnell", name: "FLUX Schnell" },
],
},
{
@@ -117,7 +124,10 @@ const PROVIDER_MODELS: Record<
{
id: "nanobanana",
name: "NanoBanana",
models: [{ id: "nanobanana/flux-schnell", name: "FLUX Schnell" }],
models: [
{ id: "nanobanana/nanobanana-flash", name: "NanoBanana Flash" },
{ id: "nanobanana/nanobanana-pro", name: "NanoBanana Pro" },
],
},
{
id: "sdwebui",

View File

@@ -1,7 +1,7 @@
import { NextResponse } from "next/server";
import path from "node:path";
import fs from "node:fs";
import os from "node:os";
import path from "path";
import fs from "fs";
import os from "os";
import { getDbInstance, SQLITE_FILE } from "@/lib/db/core";
import { isAuthRequired, isAuthenticated } from "@/shared/utils/apiAuth";

View File

@@ -1,8 +1,8 @@
import { NextResponse } from "next/server";
import { getDbInstance, SQLITE_FILE } from "@/lib/db/core";
import fs from "node:fs";
import path from "node:path";
import os from "node:os";
import fs from "fs";
import path from "path";
import os from "os";
/**
* GET /api/db-backups/exportAll

View File

@@ -1,8 +1,8 @@
import { NextResponse } from "next/server";
import Database from "better-sqlite3";
import path from "node:path";
import fs from "node:fs";
import os from "node:os";
import path from "path";
import fs from "fs";
import os from "os";
import { getDbInstance, resetDbInstance, SQLITE_FILE } from "@/lib/db/core";
import { backupDbFile } from "@/lib/db/backup";
import { isAuthRequired, isAuthenticated } from "@/shared/utils/apiAuth";

View File

@@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
import { timingSafeEqual } from "node:crypto";
import { timingSafeEqual } from "crypto";
import {
getProvider,
generateAuthData,

View File

@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
import { getSettings, updateSettings } from "@/lib/localDb";
import { clearHealthCheckLogCache } from "@/lib/tokenHealthCheck";
import bcrypt from "bcryptjs";
import { timingSafeEqual } from "node:crypto";
import { timingSafeEqual } from "crypto";
import { getRuntimePorts } from "@/lib/runtime/ports";
import { updateSettingsSchema } from "@/shared/validation/settingsSchemas";
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";

View File

@@ -1,6 +1,6 @@
import { NextResponse } from "next/server";
import path from "node:path";
import fs from "node:fs";
import path from "path";
import fs from "fs";
import { resolveDataDir } from "@/lib/dataPaths";
/**

View File

@@ -1,5 +1,5 @@
import http from "node:http";
import type { IncomingMessage, ServerResponse } from "node:http";
import http from "http";
import type { IncomingMessage, ServerResponse } from "http";
import { getRuntimePorts } from "@/lib/runtime/ports";
const DEFAULT_PROXY_TIMEOUT_MS = 30_000;

View File

@@ -8,7 +8,7 @@
* @module lib/cacheLayer
*/
import crypto from "node:crypto";
import crypto from "crypto";
/**
* @typedef {Object} CacheEntry

View File

@@ -1,5 +1,5 @@
import path from "node:path";
import os from "node:os";
import path from "path";
import os from "os";
export const APP_NAME = "omniroute";

View File

@@ -3,8 +3,8 @@
*/
import Database from "better-sqlite3";
import path from "node:path";
import fs from "node:fs";
import path from "path";
import fs from "fs";
import {
getDbInstance,
resetDbInstance,

View File

@@ -5,8 +5,8 @@
*/
import Database from "better-sqlite3";
import path from "node:path";
import fs from "node:fs";
import path from "path";
import fs from "fs";
import { resolveDataDir, getLegacyDotDataDir } from "../dataPaths";
import { runMigrations } from "./migrationRunner";

View File

@@ -9,9 +9,9 @@
* All migrations run within a single transaction — all-or-nothing per file.
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import type Database from "better-sqlite3";
/**

View File

@@ -9,7 +9,7 @@
* @module lib/db/prompts
*/
import crypto from "node:crypto";
import crypto from "crypto";
import { getDbInstance } from "./core";
interface StatementLike<TRow = unknown> {

View File

@@ -1,6 +1,6 @@
import { KIMI_CODING_CONFIG } from "../constants/oauth";
import { randomUUID } from "node:crypto";
import { hostname } from "node:os";
import { randomUUID } from "crypto";
import { hostname } from "os";
// Generate device ID (persistent per installation)
const DEVICE_ID = randomUUID();

View File

@@ -10,7 +10,7 @@
* @module lib/semanticCache
*/
import crypto from "node:crypto";
import crypto from "crypto";
import { LRUCache } from "./cacheLayer";
import { getDbInstance } from "./db/core";

View File

@@ -9,7 +9,7 @@
*/
import { AsyncLocalStorage } from "node:async_hooks";
import crypto from "node:crypto";
import crypto from "crypto";
const correlationStore = new AsyncLocalStorage();

View File

@@ -10,9 +10,8 @@
* @module shared/utils/requestId
*/
import { AsyncLocalStorage } from "node:async_hooks";
import { randomUUID } from "node:crypto";
import { randomUUID } from "crypto";
const requestIdStore = new AsyncLocalStorage();
@@ -66,10 +65,7 @@ export function addRequestIdHeader(headers = {}) {
* @returns {Response} Response with request ID header
*/
export function attachRequestIdToResponse(request, response) {
const requestId =
getRequestId() ||
request?.headers?.get?.("x-request-id") ||
randomUUID();
const requestId = getRequestId() || request?.headers?.get?.("x-request-id") || randomUUID();
const headers = new Headers(response.headers);
headers.set("x-request-id", requestId);