# 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`.

## Commands

```bash
# Development (starts both server and web dev server concurrently)
npm run dev

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

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

# Build frontend for production
npm run build:web

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

# Type check frontend
cd web && npx 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` → `generator.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

`/surge` endpoint requires no auth (for Surge client access). All `/api/*` routes require Bearer token auth, except `/api/auth/login` and `/api/auth/status`. Password is stored in the `config` table. Token is the raw password sent via `Authorization: Bearer <password>`.

### 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).

### 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)

### 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.

## 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.

## Deployment

The `Content-Disposition` header on `/surge` controls the config profile name in Surge client (currently `IPLC.MAX.conf`). Docker mounts `./data` volume for SQLite persistence. Certificate renewal uses acme.sh with `dns_cf` (Cloudflare DNS API validation).
