Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 126 additions & 44 deletions CLAUDE.MD
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Project Overview

VodHub is a pnpm monorepo with two applications:
- **Backend** (`apps/backend`): Hono-based video aggregation API that normalizes multiple video source providers into a unified REST API (categories, search, details, playback). Node.js >= 24, ESM.
- **Frontend** (`apps/frontend`): Vite + React 19 + React Router video player application with Ant Design 6, Zustand state management, and multi-theme support.
- **Backend** (`apps/backend`): Hono‑based video aggregation API that normalizes multiple video source providers into a unified REST API (categories, search, details, playback). Node.js ≥ 24, ESM.
- **Frontend** (`apps/frontend`): Vite + React 19 + React Router video player application with Ant Design 6, Zustand state management, and multi‑theme support.

A shared package (`packages/shared`) provides core TypeScript types used by both apps.

## Common Development Commands

Expand All @@ -17,114 +19,188 @@ pnpm dev # Start both backend and frontend dev servers
pnpm dev:backend # Backend only (tsx watch)
pnpm dev:frontend # Frontend only (vite dev)
pnpm lint # Lint all apps
pnpm lint:fix # Lint with auto-fix
pnpm lint:fix # Lint with auto‑fix
pnpm typecheck # Type check all apps
pnpm format # Prettier write
pnpm format:check # Prettier check
pnpm commit # Interactive conventional commit
pnpm build # Build all apps (backend + frontend)
```

### Backend-Specific Commands
### Backend‑Specific Commands
```bash
pnpm --filter @vodhub/backend start # Start backend without watch
pnpm --filter @vodhub/backend build # Build backend (generates routes first)
pnpm --filter @vodhub/backend gen:routes # Generate route registry
pnpm --filter @vodhub/backend lint # Lint backend only
pnpm --filter @vodhub/backend lint:fix # Lint backend with auto‑fix
pnpm --filter @vodhub/backend typecheck # Type check backend only
```

### Frontend-Specific Commands
### Frontend‑Specific Commands
```bash
pnpm --filter @vodhub/frontend build # Production build
pnpm --filter @vodhub/frontend preview # Preview production build
pnpm --filter @vodhub/frontend lint # Lint frontend only
pnpm --filter @vodhub/frontend lint:fix # Lint frontend with auto‑fix
pnpm --filter @vodhub/frontend typecheck # Type check frontend only
```

## Backend Architecture

### Route System
- **URL Pattern**: `GET/POST /api/vodhub/<provider>/<action>`
- **Actions**: `home`, `homeVod`, `category`, `detail`, `play`, `search`
- **Auto-discovery**: Routes are automatically registered via `directory-import` in `apps/backend/src/routes/registry.ts`
- **Production optimization**: Routes are pre-generated to `registry.gen.ts` for faster startup
- **Auto‑discovery**: Routes are automatically registered via `directory‑import` in `apps/backend/src/routes/registry.ts`
- **Production optimization**: Routes are pre‑generated to `registry.gen.ts` for faster startup

### Provider Types
1. **CMS Providers** (Recommended): Only need `namespace.ts` and `index.ts` using `createCMSRoutes()` factory
2. **Custom Providers**: Individual route files (`home.ts`, `category.ts`, etc.) with custom logic

### CMS Management & Proxy
- **Proxy API**: `ALL /api/vodhub/proxy` – forwards requests to user‑defined CMS URLs using `x-proxy-target` and `x-proxy-action` headers to translate actions to the target CMS's API format
- **Frontend store**: CMS configurations are managed locally via `useCmsStore` (Zustand + localStorage) for immediate UI updates; backend API synchronization may be added in the future.
Comment on lines +61 to +63

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.

⚠️ Potential issue | 🟡 Minor

对齐 proxy 路由方法约束。

Line 62 记录 proxy 是 ALL /api/vodhub/proxy,但 Line 114 只允许 GET | POST。如果路由类型/生成器需要表达 proxy,这里会把维护者引到错误的 route shape。

建议调整
-- `method?`: 'GET' | 'POST' – defaults to GET
+- `method?`: 'GET' | 'POST' | 'ALL' – defaults to GET

Also applies to: 107-114

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.MD` around lines 61 - 63, The documentation claims the proxy route
supports ALL methods for `/api/vodhub/proxy` but the implementation only accepts
`GET | POST`; update either the doc or the router so they match: either change
the CLAUDE.MD description to state `GET | POST /api/vodhub/proxy` (and update
other mentions at the same block around the CMS Management & Proxy section) or
modify the route handler/route generator for `/api/vodhub/proxy` to accept all
HTTP verbs (e.g., map other methods through the same proxy logic) so the
documentation remains accurate.


### Middleware Order
1. `cors()` - CORS handling
2. `trimTrailingSlash()` - Trailing slash normalization
3. `compress()` - Response compression
4. `jsonReturn` - JSON response serialization
5. `cache` - Redis/memory caching with deduplication
1. `cors()` – CORS handling
2. `trimTrailingSlash()` – trailing slash normalization
3. `compress()` – response compression
4. `jsonReturn` – JSON response serialization
5. `cache` – Redis/memory caching with deduplication

### Caching System
- Two-level cache: memory LRU + optional Redis via Keyv
- Deduplication: Prevents concurrent identical requests
- Cache key format: `vod-hub:redis-cache:${path}${bodyHash}`
- Error responses: Set `Cache-Control: no-cache` to prevent caching failures
- Two‑level cache: memory LRU + optional Redis via Keyv
- Deduplication: prevents concurrent identical requests
- Cache key format: `vod‑hub:redis‑cache:${path}${bodyHash}`
- Error responses: set `Cache‑Control: 'no‑cache'` to prevent caching failures

### Error Handling Pattern
All handlers must follow this structure:
```typescript
try {
logger.info(`${ACTION_MESSAGE.INFO} - ${namespace.name}`);
logger.info(`${ACTION_MESSAGE.INFO} ‑ ${namespace.name}`);
const res = await someRequest();
if (res.code === 1) { // CMS convention
return { code: SUCCESS_CODE, message: ACTION_MESSAGE.SUCCESS, data: [...] };
return { code: SUCCESS_CODE, message: ACTION_MESSAGE.SUCCESS, data: […] };
}
logger.error(`${ACTION_MESSAGE.ERROR} - ${namespace.name} - ${JSON.stringify(res)}`);
logger.error(`${ACTION_MESSAGE.ERROR} ‑ ${namespace.name} ‑ ${JSON.stringify(res)}`);
return { code: ERROR_CODE, message: ACTION_MESSAGE.ERROR, data: [] };
} catch (error) {
ctx.res.headers.set('Cache-Control', 'no-cache');
logger.error(`${ACTION_MESSAGE.ERROR} - ${namespace.name} - ${error}`);
ctx.res.headers.set('Cache‑Control', 'no‑cache');
logger.error(`${ACTION_MESSAGE.ERROR} ‑ ${namespace.name} ‑ ${error}`);
return { code: SYSTEM_ERROR_CODE, message: ACTION_MESSAGE.ERROR, data: [] };
}
```

**Status Codes**:
- `SUCCESS_CODE = 0` - Successful response
- `ERROR_CODE = -1` - Business logic error (upstream failure)
- `SYSTEM_ERROR_CODE = -2` - Exception/catch error
- `SUCCESS_CODE = 0` – successful response
- `ERROR_CODE = -1` – business logic error (upstream failure)
- `SYSTEM_ERROR_CODE = -2` – exception/catch error

### Import Conventions
- **Groups**: Built‑ins → external packages → internal modules (`@/`), separated by blank lines
- **Alphabetization**: Imports within each group should be alphabetized (case‑insensitive)
- **Type imports**: Use `import type` for type‑only imports
- **Node built‑ins**: Use the `node:` prefix (e.g., `import { join } from 'node:path'`)

### Route Object Shape
Each custom route file exports a `route` object with the following properties:
- `path`: string or string[] – URL path segment(s)
- `name`: string – route identifier (matches filename)
- `example`: string – example URL path
- `description`: string – what the route does
- `handler`: (ctx: Context) => Promise<T> | T
- `method?`: 'GET' | 'POST' – defaults to GET

### Handler Naming
- The handler function must be named `handler` (constant) in each route file
- Use typed `RouteItem<T>` generic for route definitions

## Frontend Architecture

### Tech Stack
- **Framework**: Vite + React 19 + React Router + TypeScript
- **UI**: Ant Design 6 with SCSS Modules
- **Framework**: Vite + React 19 + React Router + TypeScript
- **UI**: Ant Design 6 with SCSS Modules
- **State Management**: Zustand stores with `persist` middleware
- **Video Player**: xgplayer + HLS.js
- **Styling**: CSS variables for multi-theme support
- **Styling**: CSS variables for multi‑theme support

### Theme System
Three built-in themes defined in `lib/themes/index.ts`:
- **midnight**: Dark with red accent
- **aurora**: Light with cyan accent
- **cyber**: Dark with purple accent
Three built‑in themes defined in `lib/themes/index.ts`:
- **midnight**: dark with red accent
- **aurora**: light with cyan accent
- **cyber**: dark with purple accent
Comment on lines 129 to +133

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.

⚠️ Potential issue | 🟡 Minor

更新内置主题列表,避免文档与实现不一致。

Line 130-133 仍写的是旧的 midnight / aurora / cyber,但当前 apps/frontend/src/lib/themes/index.ts 已改为 vercel / airbnb / claude。

建议调整
-Three built‑in themes defined in `lib/themes/index.ts`:
-- **midnight**: dark with red accent
-- **aurora**: light with cyan accent  
-- **cyber**: dark with purple accent
+Three built‑in themes defined in `lib/themes/index.ts`:
+- **vercel**: light with black primary and blue accent
+- **airbnb**: light with Rausch red accent
+- **claude**: warm neutral palette with terracotta accent
🧰 Tools
🪛 LanguageTool

[grammar] ~130-~130: Ensure spelling is correct
Context: ...i‑theme support ### Theme System Three built‑in themes defined in lib/themes/index.ts: - **m...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.MD` around lines 129 - 133, The Theme System documentation in
CLAUDE.MD still lists the old built-in themes (midnight / aurora / cyber);
update the "### Theme System" section to match the current implementation in
lib/themes/index.ts (now exporting vercel, airbnb, claude) by replacing the
three bullet items with the new theme names and brief descriptions (e.g.,
light/dark and accent color) consistent with the theme definitions in
apps/frontend/src/lib/themes/index.ts (verify symbol names vercel, airbnb,
claude).


**Key Rule**: All colors must use CSS variables (`var(--color-bg-container)`) - never hardcode hex/rgba values.
**Key Rule**: All colors must use CSS variables (`var(--color‑bg‑container)`) – never hardcode hex/rgba values.

### CSS Variables Reference
| Category | Variable | Description |
|---|---|---|
| Brand | `--color-primary` | Primary color |
| Brand | `--color-primary-light` | Light primary |
| Background | `--color-bg` | Page background |
| Background | `--color-bg-container` | Container background |
| Background | `--color-bg-elevated` | Elevated background |
| Background | `--color-bg-container-alpha` | Semi‑transparent container |
| Background | `--color-bg-elevated-alpha` | Semi‑transparent elevated |
| Background | `--color-bg-elevated-hover` | Hover state background |
| Text | `--color-text` | Primary text |
| Text | `--color-text-secondary` | Secondary text |
| Text | `--color-text-tertiary` | Tertiary text |
| Border | `--color-border` | Border |
| Border | `--color-border-secondary` | Secondary border |
| State | `--color-primary-alpha-low` | Selected state |
| State | `--color-primary-alpha-medium` | Border accent |
| State | `--color-primary-alpha-hover` | Hover state |
| State | `--color-primary-shadow` | Shadow |
| Overlay | `--color-overlay` | Overlay background |
| Overlay | `--color-overlay-border` | Overlay border |

### State Management
- Stores in `lib/store/` with naming `use{Name}Store.ts`
- Zustand `persist` middleware for localStorage persistence
- Theme state updates both CSS variables and Ant Design theme config

### Component Structure
- Components are client-side by default (no `'use client'` directive needed)
- SCSS Modules with `vod-next-` class prefix (historical naming convention)
- Components are client‑side by default (no `'use client'` directive needed)
- SCSS Modules with `vod‑next‑` class prefix (historical naming convention)
- Props defined with `interface` and destructured in params
- Components use `export default`

### Error Handling
- **HTTP errors**: Rejected via Axios interceptor (`Promise.reject(error)`)
- **API calls**: Wrap in try/catch, re‑throw as `Promise.reject`
- **UI errors**: Display with Ant Design `message` component

### Performance Guidelines
- Use `React.memo` to avoid unnecessary re‑renders
- Use `useMemo` and `useCallback` for expensive computations
- Use `React.lazy()` with `Suspense` for heavy components (e.g., video player)
- Use `Suspense` for async loading

### Design System
- See `DESIGN.md` for Vercel‑inspired visual guidelines (colors, typography, shadows, component styling).

### CMS Management UI
- Component: `components/cms‑management/index.tsx`
- Store: `lib/store/useCmsStore.ts` (localStorage only)
- Allows adding, editing, deleting, and testing custom CMS sources
- Changes are persisted locally in localStorage; backend API synchronization may be added in the future

## Shared Types

Core types defined in `packages/shared/src/types/index.ts`:
- `Namespace` - Provider metadata
- `HomeData`, `HomeVodData`, `CategoryVodData` - Data structures
- `DetailData`, `PlayData`, `SearchData` - Response types
- `ApiResponse<T>` - Standard API response wrapper
- `Namespace` – provider metadata
- `HomeData`, `HomeVodData`, `CategoryVodData` – data structures
- `DetailData`, `PlayData`, `SearchData` – response types
- `ApiResponse<T>` – standard API response wrapper

## Deployment

### Docker Compose
- **Development**: `docker-compose up -d` (uses `docker-compose.yml`)
- **Production**: `docker-compose -f docker-compose.prod.yml up -d` (includes Redis, log persistence)
- **Development**: `docker‑compose up ‑d` (uses `docker‑compose.yml`)
- **Production**: `docker‑compose ‑f docker‑compose.prod.yml up ‑d` (includes Redis, log persistence)

### Production Environment
- **Services**: frontend, backend, redis
Expand All @@ -135,10 +211,10 @@ Core types defined in `packages/shared/src/types/index.ts`:
## Development Standards

### Code Style
- **Backend**: Named exports only, `any` disallowed, `no-explicit-any: error`
- **Backend**: Named exports only, `any` disallowed (`no‑explicit‑any: error`)
- **Frontend**: `any` allowed, `export default` for components, named exports for utilities
- **Imports**: Builtins → externals → internals, alphabetized, blank line between groups
- **Formatting**: Single quotes, no trailing commas, 4-space indent, 200 char width, semicolons
- **Formatting**: Single quotes, no trailing commas, 4‑space indent, 200‑char width, semicolons

### Naming Conventions
| Item | Convention | Example |
Expand All @@ -149,15 +225,21 @@ Core types defined in `packages/shared/src/types/index.ts`:
| Frontend components | `PascalCase` | `VodList` |
| Frontend hooks | `use` prefix | `useIsMobile` |
| Frontend stores | `use` + `Store` suffix | `useSettingStore` |
| Backend handler functions | `handler` | `const handler = async (ctx) => { … }` |

### Git Workflow
- Husky + lint-staged for pre-commit hooks
- Husky + lint‑staged for pre‑commit hooks
- Commitlint enforces conventional commits
- `pnpm commit` for interactive commit messages

### Code Quality Assurance
- **After generating code**: Always run `pnpm lint:fix` and `pnpm format` to ensure consistent style
- **Before committing**: Pre‑commit hooks automatically run linting and formatting
- **Type safety**: Run `pnpm typecheck` to verify TypeScript types across the monorepo

## Important Notes
- No test framework is configured in this project
- Backend uses ESM (`"type": "module"`)
- CMS handlers check `res.code === 1` for upstream success
- All error responses return `data: []` (empty array)
- Route handlers must never throw - always return structured response
- Route handlers must never throw – always return structured response
23 changes: 10 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,17 @@

VodHub 是一个完整的视频聚合解决方案,包含两个核心部分:

- **后端**: 电影、电视剧、动漫等视频接口聚合服务,转换多个视频源为标准统一输出、支持CMS视频源接入
- **前端**: 视频源聚合播放器,提供分类、搜索、详情、播放等功能

## ✨ 功能特性

### 后端服务
- 📦 开箱即用的接口服务
- ⚙️ 转换多个视频源为标准输出,支持分类、搜索、详情、播放等功能
- ⚙️ 内置多个CMS标准视频源
- 🛡 使用 TypeScript 开发,保持输出接口的一致性

### 前端应用
- 📦 支持 VodHub 标准视频源
- ⚙️ 提供视频分类、搜索、详情、播放等页面
- 🖥 提供视频分类、搜索、详情、播放等页面
- 🐳 支持 PC、手机端页面自适应
- 🎨 内置多套主题 一键切换
- ⚙️ 支持自定义CMS视频源

## 📸 界面展示

Expand All @@ -33,11 +30,11 @@ VodHub 是一个完整的视频聚合解决方案,包含两个核心部分:
#### 播放页
![播放页](/apps/frontend/docs/images/play.png)

#### 暗黑模式
![暗黑模式](/apps/frontend/docs/images/dark.png)
#### CMS配置、主题选择
![设置页](/apps/frontend/docs/images/theme1.png)

#### 手机适配
![手机适配](/apps/frontend/docs/images/mobile.png)
#### Claude风格主题
![claude风格](/apps/frontend/docs/images/theme.png)

## 🖥 开发环境

Expand Down Expand Up @@ -77,10 +74,10 @@ $ pnpm dev:frontend
### 配置前端 VodHub 接口地址

#### 方式一
本地启动 VodHub 服务,配置为 `/` 即可
本地启动前后端,配置为 `/` 即可

#### 方式二
部署后的 VodHub 地址,配置为 `http://abc.com` 即可
只启动前端,使用第三方提供的VodHub服务,配置为 `http://abc.com` 即可

### 后端 API 使用示例

Expand Down
6 changes: 6 additions & 0 deletions apps/backend/src/middleware/jsonReturn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import type { MiddlewareHandler } from 'hono';

const middleware: MiddlewareHandler = async (ctx, next) => {
await next();

// 如果响应体已经被设置(例如通过 c.json()),则直接返回
if (ctx.res.body) {
return;
}

const data = ctx.get('data');
ctx.header('Content-Type', 'application/json');
return ctx.body(JSON.stringify(data));
Expand Down
Loading
Loading