Files
sub-router/CLAUDE.md
2026-04-13 22:19:30 +08:00

4.8 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Sub Router is a Surge proxy subscription management system. It aggregates nodes from upstream subscriptions, allows adding custom static nodes and rules, and generates a combined Surge configuration file served at /surge/:token.

Commands

# Development (starts both server and web dev server concurrently)
pnpm dev

# Server only (port 3456, hot-reload via tsx watch)
pnpm dev:server

# Frontend only (port 5173, proxies /api and /surge to :3456)
pnpm dev:web

# Build frontend for production
pnpm build:web

# Production start (serves built frontend + API on port 3456)
pnpm start

# Type check frontend
cd web && pnpm exec tsc --noEmit

# Type check server
cd server && pnpm exec tsc --noEmit

# Docker
docker compose up -d

Architecture

Monorepo with NPM Workspaces: server/ (Express + better-sqlite3) and web/ (React + Vite). In production, Express serves the built frontend static files and API from a single port (3456).

Key data flow

  1. User adds upstream subscription URLs → backend fetches and parses nodes into fetched_nodes table
  2. User adds static nodes via URI (ss://, vmess://, trojan://) → parsed and stored in static_nodes table
  3. GET /surge/:tokengenerator.ts rebuilds the Surge config:
    • Takes first enabled subscription's raw_config as base template
    • Replaces [Proxy] section entirely with only enabled nodes (static first, then fetched)
    • Rebuilds [Proxy Group] select groups with only enabled node names
    • Injects user rules at the top of [Rule] section
    • Rewrites #!MANAGED-CONFIG URL

Auth model

Two separate credentials exist:

  • Admin password: Protects all /api/* routes (except /api/auth/login and /api/auth/status). Sent as Authorization: Bearer <password>. On first login with no password set, the submitted password becomes the password.
  • Surge token: UUID stored in config table as surge_token. Embedded in the Surge subscription URL itself (/surge/:token). Returns 404 for wrong tokens. Regeneratable via POST /api/config/surge-token.

API routes

  • POST /api/auth/login, GET /api/auth/status — auth (no auth required)
  • GET /api/stats — node/rule counts summary
  • /api/subscriptionssubscriptions.ts (CRUD + /:id/fetch, /:id/nodes)
  • /api/nodesnodes.ts (fetched node toggle, static node CRUD)
  • /api/rulesrules.ts (CRUD + /reorder)
  • /api/configsurge.ts (/surge-token GET/POST, /preview GET)

Database

SQLite with WAL mode at server/data/sub-router.db. Five tables: subscriptions, fetched_nodes (FK to subscriptions, CASCADE delete), static_nodes, rules, config (KV store for password and surge_token).

Parsers (server/src/parsers/)

Convert protocol URIs to Surge proxy lines. Each parser returns { name, type, server, port, surgeLine }. The subscription content parser handles both base64-encoded URI lists and raw Surge config format (extracting from [Proxy] section).

  • SS: No TLS wrapping (Surge doesn't support SS over TLS; SS 2022 has built-in encryption)
  • VMess/Trojan: skip-cert-verify=false (strict TLS verification)

On re-fetch, enabled state is preserved by node name: existing name→enabled mapping is saved before delete, then reapplied to new rows.

When renaming a static node (PUT /api/nodes/static/:id), the surge_line prefix is also updated to match the new name.

Frontend

Five panels: Subscriptions (CRUD + fetch trigger), Static Nodes (paste URI, custom naming, double-click to rename), Node Selector (per-subscription toggle with regex batch operations), Rules (CRUD + drag reorder), Output (subscription URL + config preview). Dark cyberpunk theme with CSS variables, JetBrains Mono font.

Generator limitation

rebuildProxyGroup in generator.ts only updates the first = select, group (due to handled = true flag). If a Surge template has multiple select groups, only the first gets its node list replaced.

Route ordering matters

In server/src/routes/: specific paths like /reorder and /fetched/batch must be registered before parameterized /:id routes to avoid being captured by Express route matching.

Legacy file

index.js at the repo root is a pre-rewrite single-file prototype. It is not used by the current system and can be ignored.

Deployment

Multi-stage Docker build: frontend built on node:22-slim, output copied into production image alongside the server. The ./data volume maps to server/data/ inside the container for SQLite persistence.

The Content-Disposition header on /surge/:token controls the config profile name in Surge client (currently IPLC.MAX.conf). Certificate renewal uses acme.sh with dns_cf (Cloudflare DNS API validation).