Skip to content

feat(docs,backup): export/import endpoints, documentation overhaul, screenshots - #70

Merged
diegosouzapw merged 1 commit into
mainfrom
feature/docs-backup-v1.0.0
Feb 18, 2026
Merged

diegosouzapw merged 1 commit into
mainfrom
feature/docs-backup-v1.0.0

Conversation

@diegosouzapw

Copy link
Copy Markdown
Collaborator

Changes

New Features

  • Export/Import Backup API — 3 new endpoints: export, import, exportAll
  • Export All (.tar.gz) — Full backup with DB + settings + combos + providers + API keys
  • UI Buttons — Export Database, Export All, Import Database in Settings
  • Translator Playground — Contextual mode descriptions per selected mode

Documentation

  • VM Deployment Guide — Complete guide (VM + Docker + nginx + Cloudflare + security)
  • Features Gallery — 9 dashboard screenshots with descriptions
  • Roadmap — 150+ planned features across 6 categories (en + pt-BR)
  • Playground — Expanded to 4-mode table (en + pt-BR)
  • API Reference — Added backup/export/import endpoints
  • User Guide — Added Database Export/Import section

Files Changed

  • 21 files changed, 1214 insertions(+), 27 deletions(-)

…haul, screenshots

- feat: GET /api/db-backups/export — download SQLite database
- feat: POST /api/db-backups/import — upload/restore with validation
- feat: GET /api/db-backups/exportAll — full backup as .tar.gz archive
- feat: Export/Import/ExportAll buttons in SystemStorageTab UI
- feat: Translator Playground contextual mode descriptions
- docs: Expanded Playground section with 4 modes (en + pt-BR)
- docs: Added Roadmap section with 150+ planned features (en + pt-BR)
- docs: Created VM_DEPLOYMENT_GUIDE.md (VM + Docker + nginx + Cloudflare)
- docs: Created FEATURES.md with 9 dashboard screenshots
- docs: Updated API_REFERENCE.md with backup/export endpoints
- docs: Updated USER_GUIDE.md with Export/Import section
- docs: Added screenshot gallery to both READMEs
- chore: Updated .gitignore to track new docs files
Copilot AI review requested due to automatic review settings February 18, 2026 02:58
@diegosouzapw
diegosouzapw merged commit bf00457 into main Feb 18, 2026
7 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

To use Codex here, create a Codex account and connect to github.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @diegosouzapw, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances OmniRoute's data management capabilities by introducing robust database export and import functionalities, including a full backup option. It also brings a major overhaul to the project's documentation, providing detailed deployment guides, a visual features gallery, and an updated roadmap, alongside improvements to the Translator Playground UI for better user experience.

Highlights

  • New Backup & Restore Functionality: Introduced API endpoints for exporting the database, importing a database, and performing a full export of all configurations as a .tar.gz archive. Corresponding UI buttons for these actions have been added to the Settings page.
  • Documentation Overhaul: Added a comprehensive VM deployment guide, a visual Features Gallery with dashboard screenshots, and an expanded project roadmap. The API Reference and User Guide have been updated to reflect the new backup features.
  • Enhanced Translator Playground: The Translator Playground UI now includes contextual descriptions for each of its four modes (Playground, Chat Tester, Test Bench, Live Monitor), improving clarity and user guidance.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • .gitignore
    • Updated to include new documentation paths.
  • README.md
    • Expanded the description of the DB Backups feature to include export and import.
    • Detailed the Translator Playground section with descriptions for its four modes.
    • Added new documentation links for VM Deployment and Features Gallery.
    • Introduced a Dashboard Preview section with screenshots.
    • Included a comprehensive Roadmap section.
  • README.pt-BR.md
    • Expanded the description of the DB Backups feature to include export and import in Portuguese.
    • Detailed the Translator Playground section with descriptions for its four modes in Portuguese.
    • Added new documentation links for VM Deployment and Features Gallery in Portuguese.
    • Introduced a Dashboard Preview section with screenshots in Portuguese.
    • Included a comprehensive Roadmap section in Portuguese.
  • docs/API_REFERENCE.md
    • Documented new API endpoints for database backup, export, and import operations.
  • docs/FEATURES.md
    • Added a new document providing a visual gallery of dashboard features with descriptive text and screenshots.
  • docs/USER_GUIDE.md
    • Added a new section detailing database export and import procedures, including UI and API usage examples.
  • docs/VM_DEPLOYMENT_GUIDE.md
    • Added a new comprehensive guide for deploying OmniRoute on a VM with Cloudflare, covering setup, Docker, Nginx, and security.
  • src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx
    • Implemented UI for database export, full export, and import functionalities.
    • Added state management for loading, status, and confirmation dialogs for import operations.
    • Integrated file input and handling for database import.
    • Displayed import status messages to the user.
  • src/app/(dashboard)/dashboard/translator/TranslatorPageClient.tsx
    • Added contextual descriptions for each mode within the Translator Playground UI.
    • Updated the display logic to show the relevant description based on the selected mode.
  • src/app/api/db-backups/export/route.ts
    • Implemented a new API route (GET /api/db-backups/export) to export the current SQLite database file.
    • Utilized SQLite's native backup API for consistent snapshots.
    • Handled file streaming and cleanup of temporary files.
  • src/app/api/db-backups/exportAll/route.ts
    • Implemented a new API route (GET /api/db-backups/exportAll) to export a comprehensive backup as a .tar.gz archive.
    • Included database, settings, combos, providers (without credentials), and API keys in the full backup.
    • Managed temporary directory creation, file writing, and archive generation using 'tar -czf'.
    • Ensured proper cleanup of temporary files and directories.
  • src/app/api/db-backups/import/route.ts
    • Implemented a new API route (POST /api/db-backups/import) to import a SQLite database.
    • Included robust validation for file type, size, SQLite integrity, and required OmniRoute tables.
    • Performed an automatic pre-import backup of the existing database.
    • Handled closing and resetting the current database instance before replacing the database file.
    • Provided detailed response with counts of imported connections, nodes, combos, and API keys.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant new functionality with database export/import endpoints and a comprehensive documentation overhaul, including a new VM deployment guide and feature gallery. However, it critically introduces highly sensitive administrative endpoints for database export and import that lack authentication and authorization checks. This allows unauthenticated users to exfiltrate the entire database (including AI provider credentials) and overwrite system data. Additionally, several API endpoints leak internal error messages, there are instances of risky error handling (empty catch blocks), code duplication in the frontend, and potential performance bottlenecks with large file handling. Implementing robust access control and CSRF protection is paramount, and addressing these code quality issues will enhance the security, maintainability, and scalability of the new features.

* GET /api/db-backups/exportAll
* Exports the entire database + settings as a ZIP archive
*/
export async function GET() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

The exportAll endpoint is not protected by any authentication or authorization mechanism. An unauthenticated attacker can download a full backup archive of the system, including settings, combos, and provider metadata. While some fields are masked in this specific export, the lack of access control is a critical vulnerability.

* Accepts multipart/form-data with a single "file" field containing the .sqlite backup.
* Validates integrity, schema, and required tables before replacing the active database.
*/
export async function POST(request: Request) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

This endpoint allows unauthenticated users to upload a SQLite file and overwrite the active application database. This is a critical security flaw that could lead to a complete system takeover, data loss, or unauthorized access to AI providers. Furthermore, the endpoint lacks CSRF protection, which could allow an attacker to trick an authenticated administrator into importing a malicious database.

* Uses SQLite's native backup API to create a consistent snapshot,
* then streams it as a downloadable attachment.
*/
export async function GET() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-critical critical

This endpoint lacks authentication and authorization checks. Any unauthenticated user can trigger a database export and download the entire application database. The database contains highly sensitive information, including AI provider API keys and access tokens stored in plain text in the provider_connections table. This represents a critical security risk.

});
} catch (error) {
console.error("[API] Error exporting database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Returning error.message directly to the client can leak sensitive information about the server's internal state or file system. It's a security best practice to log the detailed error on the server and return a generic error message to the client.

Suggested change
return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json({ error: "Failed to export database." }, { status: 500 });

});
} catch (error) {
console.error("[API] Error importing database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Returning error.message directly to the client can leak sensitive information about the server's internal state. It's a security best practice to log the detailed error on the server and return a generic error message to the client.

Suggested change
return NextResponse.json({ error: error.message }, { status: 500 });
return NextResponse.json({ error: "An error occurred during database import." }, { status: 500 });

const db = getDbInstance();
await db.backup(tmpPath);

const fileBuffer = fs.readFileSync(tmpPath);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using fs.readFileSync loads the entire database file into memory before sending it. For larger databases, this can lead to high memory consumption. Consider using streams for better memory efficiency. You can create a readable stream with fs.createReadStream(tmpPath) and pass it directly to the Response constructor. You'll need to handle the cleanup of the temporary file after the stream has been consumed, which can be done by piping through a TransformStream.


// Create ZIP using tar (available on all Linux/macOS, and the archiver npm package is not installed)
// We'll use Node.js built-in zlib to create a simple tar.gz instead
const { execSync } = require("node:child_process");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using require() inside a function is generally discouraged. It's better to use ES module import statements at the top of the file for consistency and to allow for better static analysis. Please replace this with import { execSync } from 'node:child_process'; at the top of the file.

} else {
setImportStatus({ type: "error", message: data.error || "Import failed" });
}
} catch {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The catch block in handleImportConfirm is empty and only sets a generic error message. This can hide the actual cause of an error during development and debugging. It's a good practice to at least log the error to the console, for example: console.error('An error occurred during import:', error);.

URL.revokeObjectURL(url);
} catch (err) {
console.error("Export failed:", err);
setImportStatus({ type: "error", message: `Export failed: ${(err as Error).message}` });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

In the catch block of handleExport, an export-related error updates the importStatus state. This can be confusing. Consider creating a separate exportStatus state or renaming importStatus to something more generic like systemStorageStatus to handle messages for both import and export operations. The same applies to the 'Export All' functionality.

@@ -0,0 +1,399 @@
# OmniRoute — Guia de Deploy em VM com Cloudflare

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The content of this guide is in Portuguese, but the filename VM_DEPLOYMENT_GUIDE.md is in English. For consistency and better discoverability, consider renaming the file to VM_DEPLOYMENT_GUIDE.pt-BR.md or translating the content to English to match the filename.

@diegosouzapw
diegosouzapw deleted the feature/docs-backup-v1.0.0 branch February 18, 2026 03:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a comprehensive backup and export/import system for OmniRoute along with extensive documentation improvements and a visual features gallery.

Changes:

  • Export/Import API: Three new endpoints (/api/db-backups/export, /api/db-backups/exportAll, /api/db-backups/import) for database backup, full system export as tar.gz, and database restoration from uploaded .sqlite files
  • UI Enhancements: Added export/import buttons to the Settings → System & Storage tab with confirmation dialogs and status feedback; added contextual mode descriptions to the Translator Playground
  • Documentation Overhaul: New VM deployment guide (399 lines covering VM setup, Docker, nginx, Cloudflare, security), features gallery with screenshots, expanded API reference, updated user guide, and comprehensive roadmap with 210+ planned features

Reviewed changes

Copilot reviewed 11 out of 21 changed files in this pull request and generated 18 comments.

Show a summary per file
File Description
src/app/api/db-backups/export/route.ts New GET endpoint to download current database as .sqlite file using SQLite's native backup API
src/app/api/db-backups/exportAll/route.ts New GET endpoint to create full backup tar.gz archive with database, settings, combos, providers (no credentials), and API key metadata
src/app/api/db-backups/import/route.ts New POST endpoint to upload and validate .sqlite file, with integrity checks, table validation, pre-import backup creation, and atomic database replacement
src/app/(dashboard)/dashboard/settings/components/SystemStorageTab.tsx Added three buttons (Export Database, Export All, Import Database) with confirmation flow, file input handling, loading states, and status messages
src/app/(dashboard)/dashboard/translator/TranslatorPageClient.tsx Added MODE_DESCRIPTIONS object with contextual descriptions for each translator playground mode (Playground, Chat Tester, Test Bench, Live Monitor)
docs/VM_DEPLOYMENT_GUIDE.md Complete 399-line deployment guide covering VM provisioning, Docker installation, nginx configuration, Cloudflare setup, SSL certificates, firewall rules, and advanced security
docs/FEATURES.md Visual features gallery with 9 dashboard screenshots and descriptions of Providers, Combos, Analytics, Health, Translator, Settings, CLI Tools, Usage Logs, and Endpoint pages
docs/USER_GUIDE.md Added Database Export/Import section with API examples, validation details, and use cases
docs/API_REFERENCE.md Added Backup & Export/Import section documenting all backup-related endpoints
README.md & README.pt-BR.md Updated with roadmap section (210+ planned features across 6 categories), dashboard preview section with screenshot table, and links to new documentation
docs/screenshots/07-cli-tools.png Binary PNG screenshot file (178 lines of binary data) for CLI Tools dashboard
.gitignore Updated to allow new documentation files and screenshots directory

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +46 to +48
} catch (error) {
console.error("[API] Error exporting database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handler accesses error.message without checking if error is an instance of Error. In TypeScript, caught errors have type unknown and should be checked before accessing properties. Consider using a type guard or converting to string safely.

Suggested change
} catch (error) {
console.error("[API] Error exporting database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });
} catch (error: unknown) {
console.error("[API] Error exporting database:", error);
const errorMessage = error instanceof Error ? error.message : String(error);
return NextResponse.json({ error: errorMessage }, { status: 500 });

Copilot uses AI. Check for mistakes.
}

// Copy imported file over current DB
fs.copyFileSync(tmpPath, SQLITE_FILE!);

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing null check for SQLITE_FILE constant before dereferencing it with the spread operator on line 119. While line 15 checks if SQLITE_FILE exists, the TypeScript compiler still treats it as potentially null. Consider adding a non-null assertion or additional runtime check.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +133
// Remove main file and WAL sidecars
const sqliteFilesToReplace = [
SQLITE_FILE,
`${SQLITE_FILE}-wal`,
`${SQLITE_FILE}-shm`,
`${SQLITE_FILE}-journal`,
];
for (const filePath of sqliteFilesToReplace) {
if (!filePath) continue;
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
}

// Copy imported file over current DB
fs.copyFileSync(tmpPath, SQLITE_FILE!);

// Reopen and verify
const db = getDbInstance();
const connCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_connections").get() as any)?.cnt || 0;
const nodeCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_nodes").get() as any)?.cnt || 0;
const comboCount = (db.prepare("SELECT COUNT(*) as cnt FROM combos").get() as any)?.cnt || 0;
const keyCount = (db.prepare("SELECT COUNT(*) as cnt FROM api_keys").get() as any)?.cnt || 0;

console.log(
`[DB] Imported database from upload: ${connCount} connections, ${nodeCount} nodes, ${comboCount} combos, ${keyCount} API keys`
);

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The database import operation directly replaces the live database without any transaction or rollback mechanism. If the import fails after deleting the original file but before copying the new one, the database could be left in an inconsistent state. Consider implementing a more robust atomic replacement strategy, such as: 1) Copy new file to final location with temp name, 2) Rename original to .old, 3) Rename temp to final, 4) Delete .old only after confirming new database works.

Suggested change
// Remove main file and WAL sidecars
const sqliteFilesToReplace = [
SQLITE_FILE,
`${SQLITE_FILE}-wal`,
`${SQLITE_FILE}-shm`,
`${SQLITE_FILE}-journal`,
];
for (const filePath of sqliteFilesToReplace) {
if (!filePath) continue;
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
}
}
// Copy imported file over current DB
fs.copyFileSync(tmpPath, SQLITE_FILE!);
// Reopen and verify
const db = getDbInstance();
const connCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_connections").get() as any)?.cnt || 0;
const nodeCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_nodes").get() as any)?.cnt || 0;
const comboCount = (db.prepare("SELECT COUNT(*) as cnt FROM combos").get() as any)?.cnt || 0;
const keyCount = (db.prepare("SELECT COUNT(*) as cnt FROM api_keys").get() as any)?.cnt || 0;
console.log(
`[DB] Imported database from upload: ${connCount} connections, ${nodeCount} nodes, ${comboCount} combos, ${keyCount} API keys`
);
const mainFile = SQLITE_FILE!;
const sidecarFiles = [
`${mainFile}-wal`,
`${mainFile}-shm`,
`${mainFile}-journal`,
];
// Copy imported file to a temporary location next to the target DB file
const tempMainFile = `${mainFile}.tmp-import-${Date.now()}-${Math.random()
.toString(16)
.slice(2)}`;
fs.copyFileSync(tmpPath, tempMainFile);
// Prepare atomic replacement: rename existing files to .old, then move temp into place.
const renamedBackups: { original: string; backup: string }[] = [];
const filesToReplace = [mainFile, ...sidecarFiles];
for (const originalPath of filesToReplace) {
if (!originalPath) continue;
if (!fs.existsSync(originalPath)) continue;
const backupPath = `${originalPath}.old`;
// Remove any stale .old backup to avoid rename failures.
if (fs.existsSync(backupPath)) {
fs.unlinkSync(backupPath);
}
fs.renameSync(originalPath, backupPath);
renamedBackups.push({ original: originalPath, backup: backupPath });
}
let connCount = 0;
let nodeCount = 0;
let comboCount = 0;
let keyCount = 0;
try {
// Move new DB into final location
fs.renameSync(tempMainFile, mainFile);
// Reopen and verify
const db = getDbInstance();
connCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_connections").get() as any)?.cnt || 0;
nodeCount =
(db.prepare("SELECT COUNT(*) as cnt FROM provider_nodes").get() as any)?.cnt || 0;
comboCount =
(db.prepare("SELECT COUNT(*) as cnt FROM combos").get() as any)?.cnt || 0;
keyCount =
(db.prepare("SELECT COUNT(*) as cnt FROM api_keys").get() as any)?.cnt || 0;
console.log(
`[DB] Imported database from upload: ${connCount} connections, ${nodeCount} nodes, ${comboCount} combos, ${keyCount} API keys`
);
// Verification succeeded; remove .old backups
for (const { backup } of renamedBackups) {
if (fs.existsSync(backup)) {
try {
fs.unlinkSync(backup);
} catch {
// best effort cleanup
}
}
}
} catch (verificationOrReplaceError) {
console.error("[DB] Error during imported database activation, attempting rollback:", verificationOrReplaceError);
// Best-effort rollback: restore previous DB files from .old backups
try {
resetDbInstance();
} catch {
// ignore rollback reset errors
}
// Remove any new files that do not have .old backups
for (const originalPath of filesToReplace) {
if (!originalPath) continue;
const backupPath = `${originalPath}.old`;
if (fs.existsSync(originalPath) && !fs.existsSync(backupPath)) {
try {
fs.unlinkSync(originalPath);
} catch {
// best effort
}
}
}
// Restore backups
for (const { original, backup } of renamedBackups) {
if (!fs.existsSync(backup)) continue;
try {
if (fs.existsSync(original)) {
fs.unlinkSync(original);
}
fs.renameSync(backup, original);
} catch {
// best effort; if this fails, we still rethrow the original error
}
}
// Ensure temp file is cleaned up if it was never renamed
if (fs.existsSync(tempMainFile)) {
try {
fs.unlinkSync(tempMainFile);
} catch {
// best effort
}
}
throw verificationOrReplaceError;
} finally {
// In normal success path, tempMainFile has been renamed and should not exist,
// but clean up defensively in case of partial failures before rename.
if (fs.existsSync(tempMainFile)) {
try {
fs.unlinkSync(tempMainFile);
} catch {
// best effort
}
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +134 to +136
} catch (err) {
console.error("Export failed:", err);
setImportStatus({ type: "error", message: `Export failed: ${(err as Error).message}` });

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error status from export operations is incorrectly set using setImportStatus instead of a dedicated exportStatus state. This means export errors will appear in the import status message area, which could confuse users. Consider adding separate state for export status or using a more generic status state.

Copilot uses AI. Check for mistakes.
Comment on lines +278 to +281
setImportStatus({
type: "error",
message: `Full export failed: ${(err as Error).message}`,
});

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Export All" button handler also incorrectly sets errors to importStatus instead of a dedicated export status state. This is the same issue as the regular export button. Both export functions (lines 112-140 and 260-285) should use consistent error reporting.

Copilot uses AI. Check for mistakes.
// Cleanup on error
try {
if (fs.existsSync(tempDir)) fs.rmSync(tempDir, { recursive: true, force: true });
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cleanup code attempts to delete zipPath (line 126) but the actual file created is tarPath (line 102). The variable zipPath is defined on line 23 with a .zip extension but then replaced with .tar.gz extension when creating tarPath. The cleanup should delete tarPath instead of zipPath. This could leave orphaned tar.gz files in the temp directory.

Suggested change
if (fs.existsSync(zipPath)) fs.unlinkSync(zipPath);
if (fs.existsSync(tarPath)) fs.unlinkSync(tarPath);

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +144
} catch (error) {
console.error("[API] Error importing database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent error variable naming between catch blocks. Line 144 uses error, but line 95 uses e, and both access .message property without type checking. For consistency and type safety, standardize on using error with proper type guards throughout the file.

Suggested change
} catch (error) {
console.error("[API] Error importing database:", error);
return NextResponse.json({ error: error.message }, { status: 500 });
} catch (error: unknown) {
const message = error instanceof Error ? error.message : "Unexpected error";
console.error("[API] Error importing database:", error);
return NextResponse.json({ error: message }, { status: 500 });

Copilot uses AI. Check for mistakes.
DATA_DIR=/app/data
STORAGE_DRIVER=sqlite
ENABLE_REQUEST_LOGS=true
AUTH_COOKIE_SECURE=false

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AUTH_COOKIE_SECURE=false in this production deployment example causes authentication cookies to be issued without the Secure flag, so browsers may send them over plain HTTP in redirect/downgrade scenarios. An attacker on the same network path could capture these cookies and hijack sessions; in production behind HTTPS you should document that AUTH_COOKIE_SECURE must be enabled so cookies are marked Secure.

Suggested change
AUTH_COOKIE_SECURE=false
AUTH_COOKIE_SECURE=true # Em produção atrás de HTTPS, mantenha true para cookies com flag Secure

Copilot uses AI. Check for mistakes.
Comment on lines +125 to +135
```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
```

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deployment instructions use docker pull diegosouzapw/omniroute:latest and run the mutable latest tag in production, which is a supply-chain risk. If the Docker Hub account or tag is compromised, future pulls could run attacker-controlled images with access to your JWT_SECRET, API keys, and encryption keys; pin to an immutable image digest or a specific, reviewed version tag instead.

Copilot uses AI. Check for mistakes.
Comment on lines +279 to +286
```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

Copilot AI Feb 18, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The upgrade procedure again pulls and runs diegosouzapw/omniroute:latest, so each update depends on a mutable Docker tag and inherits the same supply-chain risk as the initial deployment. A compromised or replaced latest image could silently exfiltrate data or alter behavior on upgrade; recommend documenting a pinned image digest or explicit version tag for production updates.

Copilot uses AI. Check for mistakes.
diegosouzapw added a commit that referenced this pull request Nov 4, 2026
…HoverCard dependency

Kit 0.0.87 (upstream #63/#70) announces each toast once through two persistent live regions, assertive for errors and polite for the rest, so the announcement survives a modal dialog; the toast cards no longer carry role=alert or role=status. The NotificationToast bridge tests now check the visible card (text, variant, position, removal, no duplicates, the Open action) and the announcement in the right region, including that both regions stay exposed while a Modal is open.

@radix-ui/react-hover-card joins config/quality/dependency-allowlist.json with its justification, so check:deps and its unit test accept the kit's new runtime dependency.
diegosouzapw added a commit that referenced this pull request Nov 4, 2026
* chore(dashboard): sync the vendored CI UI Kit to 0.0.87

packages/ui-kit moves from upstream deb522b to 681cdb1 (0.0.87) through scripts/dev/sync-ui-kit.sh.

* fix(dashboard): load the complete kit stylesheet and add the kit's new HoverCard dependency

Kit 0.0.87 splits the pattern families out of ci-ui.css (upstream #133): ci-ui.css is now the core
sheet, and the wizard, onboarding, announcement, integration, overview, request-workbench and
Cloud families live in their own sheets. The complete sheet is ci-styles.css, which is what the
package exports as ./styles.css. The dashboard uses components from those families (the batch and
pool wizards, onboarding, the Cloud agents pages), so globals.css now imports ci-styles.css and the
workspace's ./styles.css export points at it, mirroring the upstream package.

HoverCard (upstream #123) imports @radix-ui/react-hover-card, which the sync script doesn't add,
so it joins the root dependencies, pinned exactly like the other Radix packages.

The local recharts patch in PATCHES.md is dropped: upstream #149 shipped the same graphicalItemId
fix in reporting-charts.tsx, so the snapshot is byte for byte again.

The DESIGN_SYSTEM mirrors are refreshed for the import path: 53 through the structure-safe Google
engine, the 13 it could not restore through the LLM backend.

* test(dashboard): follow the kit Toaster's live regions and allow the HoverCard dependency

Kit 0.0.87 (upstream #63/#70) announces each toast once through two persistent live regions, assertive for errors and polite for the rest, so the announcement survives a modal dialog; the toast cards no longer carry role=alert or role=status. The NotificationToast bridge tests now check the visible card (text, variant, position, removal, no duplicates, the Open action) and the announcement in the right region, including that both regions stay exposed while a Modal is open.

@radix-ui/react-hover-card joins config/quality/dependency-allowlist.json with its justification, so check:deps and its unit test accept the kit's new runtime dependency.

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants