Skip to content
Back to skills

Node App Merging

ASecurity

Merge two or more similar Node.js web apps into a single unified application with shared config, tab-based UI, and organized output. Use when the user has multiple small standalone tools (e.g. separat

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 6, 2026
developmentbashnodeapifrontend

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumUses curl or wget to download content

Pro shows the line behind each finding and how to fix it

Scanned October 6, 2026

npx -y skills add Kairos-ai-agent/kairos-code --skill node-app-merging --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Node App Merging?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Node App Merging
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/kairos-ai-agent-node-app-merging/badge)](https://www.skillsdirectory.com/skills/kairos-ai-agent-node-app-merging)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: "node-app-merging"
description: "Merge two or more similar Node.js web apps into a single unified application with shared config, tab-based UI, and organized output. Use when the user has multiple small standalone tools (e.g. separat"
priority: 0.5
imported-from: "agents"
source-path: "agents/skills/software-development/node-app-merging/SKILL.md"
---
# Node.js App Merging

Merge two or more similar Node.js web tools into a single app with tab-based UI.

## When to Use

- User has multiple standalone web apps with overlapping structure (same server.js pattern, same static-file serving, same config system)
- Apps share the same API base URL, auth key, or output directory
- User wants a single launcher/browser tab instead of multiple ports

## Step-by-Step Workflow

### 1. Read Both Projects

Read all source files of each project before writing anything:
- `server.js` — route structure, API calls, config format, utility functions
- `public/index.html` — DOM element IDs, form fields, layout
- `public/app.js` — frontend logic, event handlers, default prompts
- `public/styles.css` — may differ between projects (colors, grid layouts)
- `config.json` — current user config (API keys, model names)
- `start-windows.bat` — port number, node.exe path, startup sequence
- `README.md` — for understanding purpose and API docs

### 2. Design the Unified Config Structure

**Always use nested sections** for per-module config. Never mix module-specific fields at the top level.

```json
{
  "baseUrl": "https://shared-api.example.com/v1",
  "apiKey": "sk-...",
  "moduleA": {
    "model": "model-a",
    "size": "1280x720"
  },
  "moduleB": {
    "model": "model-b",
    "seconds": 6
  }
}
```

**Config migration**: When loading old flat configs, detect whether nested keys exist. If not, migrate flat fields into the correct nested section based on heuristics (e.g. model name, field presence).

```js
// Detect if old flat format
if (!parsed.moduleA && parsed.model && /* heuristic */) {
  moduleA.model = parsed.model;
}
```

Use `structuredClone(DEFAULT_CONFIG)` for safe deep copy of defaults (Node 17+).

### 3. Design Tab-Based UI

**Prefix all DOM element IDs with the tab name** to avoid collisions:
- Tab A: `imgBaseUrlInput`, `imgModelInput`, `imgGenerateBtn`
- Tab B: `vidModelInput`, `vidSecondsInput`, `vidGenerateBtn`

**Tab switching** — pure CSS + minimal JS:
```html
<nav class="tabs">
  <button class="tab active" data-tab="image">🎨 生图</button>
  <button class="tab" data-tab="video">🎬 图生视频</button>
</nav>
<div id="tabImage" class="tab-content active">...</div>
<div id="tabVideo" class="tab-content">...</div>
```

```js
document.querySelectorAll(".tab").forEach((btn) => {
  btn.addEventListener("click", () => {
    document.querySelectorAll(".tab").forEach((b) => b.classList.remove("active"));
    document.querySelectorAll(".tab-content").forEach((c) => c.classList.remove("active"));
    btn.classList.add("active");
    document.getElementById(`tab${capitalize(btn.dataset.tab)}`).classList.add("active");
  });
});
```

```css
.tab-content { display: none; }
.tab-content.active { display: block; }
```

### 4. Prefix API Routes

Use prefixed routes to separate module handlers:
- `POST /api/image/generate` → `handleImageGenerate()`
- `POST /api/video/generate` → `handleVideoGenerate()`
- `GET /api/config` → shared config endpoint
- `POST /api/config` → shared config save (accepts nested sections)
- `POST /api/open-output` → accepts `{ subdir: "image" }` or `{ subdir: "video" }`

### 5. Split Output Subdirectories

```
outputs/
├── image/    # module A output
└── video/    # module B output
```

Each handler writes to its own subdirectory. The static file route serves from the unified `outputs/` root:
```js
if (pathname.startsWith("/outputs/")) {
  return serveStaticFile(req, res, OUTPUT_DIR, pathname.replace(/^\/outputs\//, "/"));
}
```

Output URLs in API responses include the subdirectory:
```js
outputUrl: `/outputs/image/${encodeURIComponent(fileName)}`
```

### 6. Merge Utility Functions

Identical functions (logging, redact, sendJson, sendText, readBody, static file serving, auth headers, sleep, etc.) appear in both projects — keep only one copy.

### 7. Test Before Declaring Done

Start the merged server as a background process, then test:
```bash
# Config endpoint
curl -s http://127.0.0.1:PORT/api/config
# HTML serving
curl -s http://127.0.0.1:PORT/ | head -c 200
# Both generate routes exist (should return "X is required", not 404)
curl -s -X POST http://127.0.0.1:PORT/api/image/generate -H "Content-Type: application/json" -d '{}'
curl -s -X POST http://127.0.0.1:PORT/api/video/generate -H "Content-Type: application/json" -d '{}'
```

Kill the test server after verification.

## Pitfalls

- **Syntax check server.js** — Node's `--check` or the linter runs automatically on write. Fix any errors before proceeding.
- **Port conflict** — use a new port (not one of the old ports) to avoid stale processes blocking startup.
- **API Key handling** — copy the API key from existing `config.json` to the new one so it's not lost. The UI still shows "留空则保留已保存的 Key".
- **CSS merge** — don't blindly concatenate. Deduplicate shared rules (`.button`, `.panel`, etc.) and keep only module-specific rules (`.image-wrap` vs `.video-wrap`).
- **Element ID collisions** — the #1 bug when merging frontends. Prefix ALL IDs.
- **Shared config save** — when either tab saves config, it should send only its own section's changes plus the shared fields (baseUrl, apiKey). The server merges with the current config.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…