# F2E-Server 3.0

[![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
[![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
[![TypeScript](https://img.shields.io/badge/typescript-5.4.5-blue.svg)](https://www.typescriptlang.org/)

> 基于 Node.js 的现代化前端开发服务器，支持多运行时环境，集成了完整的开发工具链

## 🚀 项目简介

**F2E-Server 3.0** 是一个基于 Node.js 开发的高性能 HTTP 服务器，专为前端开发而设计。它支持多种运行时环境（Node.js、Bun、Deno、uWebSockets.js），并集成了现代前端开发所需的核心功能。

### ✨ 核心特性

- 🌐 **多运行时支持**: Node.js、Bun、Deno、uWebSockets.js
- 🔥 **热更新**: 基于 EventSource 的实时页面刷新
- 🏗️ **构建工具**: 集成 esbuild、Less、PostCSS、Tailwind CSS
- 📊 **打包分析**: 开发模式下可按需开启 `esbuild:with_bundle_analyze`，便于查看各入口包的依赖体积
- 🔒 **身份认证**: 内置登录系统和 RBAC 权限管理
- 📡 **代理服务**: HTTP/HTTPS 代理和站点下载功能
- 🛣️ **路由系统**: 服务端 API 开发和路由管理
- 📁 **文件服务**: 静态文件服务、目录浏览、404/500 页面
- ⚡ **性能优化**: Gzip 压缩、缓存控制、流式传输

## 📦 安装

### 全局安装（推荐）

```bash
npm install f2e-server3 -g
```

### 项目依赖安装

```bash
npm install f2e-server3 --save-dev
```

## 🚀 快速开始

### 方式一：使用 CLI 工具（推荐）

```bash
# 创建新项目
npx f2e-server3 create my-app
cd my-app
npm install
npm run dev
```

### 方式二：手动配置

创建启动文件 `start.mjs`:

```javascript
// @ts-check
import { createServer, logger } from 'f2e-server3'

// 设置日志级别
logger.setLevel('DEBUG')

// 启动服务器
createServer({
    mode: 'dev',
    port: 2850,
    livereload: true
})
```

启动服务器：

```bash
# Node.js
node start.mjs

# Bun
bun start.mjs

# Deno
deno --allow-env --allow-read --allow-write --allow-net --allow-run --allow-sys start.mjs
```

## ⚙️ 配置说明

### 运行模式

F2E-Server 提供三种运行模式：

| 模式 | 说明 | 适用场景 |
|------|------|----------|
| `dev` | 开发模式 | 开启服务器、热更新、资源编译 |
| `build` | 构建模式 | 仅编译资源，不启动服务器 |
| `prod` | 生产模式 | 仅启动服务器，开启缓存 |

```javascript
createServer({ mode: 'dev' })
```

### 基础配置

```javascript
createServer({
    // 服务器配置
    port: 2850,
    host: '0.0.0.0',
    root: process.cwd(),
    
    // 功能开关
    gzip: true,
    ssl: false,
    
    // 页面模板
    page_404: './templates/404.html',
    page_500: './templates/500.html',
    page_dir: './templates/directory.html'
})
```

## 🔧 核心功能

### 1. 热更新 (LiveReload)

自动监听文件变化并刷新浏览器：

```javascript
{
    livereload: {
        // 注入热更新脚本的文件类型
        reg_inject: /\.(html|md)$/,
        // 心跳超时时间
        heartBeatTimeout: 100000
    }
}
```

**支持环境**:
- Windows: 默认使用 Node.js API，安装 `chokidar` 后自动升级
- 其他系统: 需要安装 `chokidar`
- Deno: 使用内置 API

### 2. ESBuild 构建

现代化的 JavaScript 构建工具：

```javascript
{
    esbuild: {
        // 配置文件路径
        esbuildrc: './.esbuildrc.js',
        // 构建外部依赖
        build_external: true,
        // 是否收集各 JS 入口的 metafile（用于体积分析；dev 模式下中间件会为构建启用 metafile）
        with_bundle_analyze: false,
        // 分析相关页面的 URL 根路径（仅当 with_bundle_analyze 为 true 时有路由与启动日志提示）
        // 访问 http://<host>:<port>/<analyze_page_path> 可查看入口列表；子路径返回对应入口的 metafile JSON
        analyze_page_path: '__analyze__',
        // esbuild 构建选项，会合并到 .esbuildrc.js 中每个配置项
        esbuildOptions: {
            minify: true,
            sourcemap: true,
            // 更多 esbuild 选项...
        }
    }
}
```

**配置说明**：
- `esbuildOptions`: 可选的 esbuild 构建选项，这些选项会合并到 `.esbuildrc.js` 配置文件中定义的每个构建配置项中，方便统一设置全局构建选项（如 `minify`、`sourcemap` 等）
- `with_bundle_analyze`: 默认为 `false`。设为 `true` 时，会为每个输出的 JS 入口保存 esbuild 的 `metafile` 数据；启动时会在调试日志中打印分析页地址（例如 `http://127.0.0.1:2850/__analyze__`）
- `analyze_page_path`: 默认为 `__analyze__`。浏览器访问该路径下的根地址会渲染内置列表页，列出当前所有已分析的入口；
**说明**：`esbuild` 中间件仅在 `dev`、`build` 模式下生效（`prod` 下不加载 esbuild 配置），因此打包分析页面与 metafile 接口也仅在这些模式下可用。

**热模块更新**:

```javascript
// .esbuildrc.js
module.exports = {
    hot_modules: ['@/Hello']
}

// React 组件中使用
const [Hello, setHello] = useState(() => require('@/Hello').default)

useEffect(() => {
    if (import.meta.hot) {
        import.meta.hot.accept('@/Hello', (newModule) => {
            setHello(() => newModule.default)
        })
    }
}, [])
```

### 3. Less 编译

CSS 预处理器支持：

```javascript
{
    less: {
        entryPoints: [
            // 同路径输出
            'css/style.less',
            // 自定义输出路径
            { in: 'css/style2.less', out: 'static/bundle.css' }
        ]
    }
}
```

### 4. PostCSS 处理

PostCSS 处理器，支持 Tailwind CSS：

```javascript
import TailwindPlugin from '@tailwindcss/postcss'

{
    postcss: {
        entryPoints: {
            in: 'css/main.css',
            out: 'static/main.css'
        },
        plugins: [
            TailwindPlugin({
                optimize: mode === 'build'
            })
        ]
    }
}
```

### 5. HTTP 代理

支持 API 转发和站点下载：

```javascript
{
    proxies: [
        // 简单代理
        { 
            location: '/api', 
            origin: 'http://api.server.com/' 
        },
        // 正则匹配代理
        { 
            location: /^\/?api1/, 
            origin: 'http://api1.server.com/', 
            pathname: '/api/v1' 
        },
        // 站点下载
        {
            location: '/',
            origin: 'https://www.example.com/',
            saver: {
                pathBodyDir: '/tmp/body',
                pathHeaders: '/tmp/headers'
            }
        }
    ]
}
```

### 6. 身份认证

内置登录系统：

```javascript
import { UserStore } from 'f2e-server3'

{
    auth: {
        redirect: true,
        white_list?: [
            "^publish"
        ];
        store: new UserStore('.f2e_cache/auth.db')
    }
}
```
更多配置参考 [auth/interface.ts](src/middlewares/auth/interface.ts)

**用户文件格式** (`.f2e_cache/auth.db`):
```
admin:e10adc3949ba59abbe56e057f20f883e:管理员
user:e10adc3949ba59abbe56e057f20f883e:用户
```

### 7. 路由系统

F2E-Server 3.0 提供了强大的服务端 API 开发能力，支持多种路由注册方式和响应类型。

#### 7.1 核心概念

- **Route**: 核心路由类，管理路由注册和匹配
- **RouteItem**: 路由项配置，包含路径、处理器、响应类型等
- **RouterDecorator**: 装饰器方式注册路由
- **addRoute**: 函数式路由注册

#### 7.2 路由注册方式

##### 方式1: 直接使用 Route 类

```javascript
import { Route, queryparams } from 'f2e-server3'

export const app_base = {
    name: 'app_base',
    mode: ['dev', 'prod'],
    execute: (conf) => {
        const route = new Route(conf)
        
        // 基础 JSON API
        route.on('api/time', async (body, ctx) => {
            const data = queryparams(ctx.location.search)
            return {
                data,
                post: body,
                time: Date.now()
            }
        })
        
        // 支持正则表达式路径
        route.on(/^auth\/user\/(\d+)$/, async (body, ctx) => {
            const userId = ctx.pathname.match(/^auth\/user\/(\d+)$/)[1]
            return { userId, authenticated: true }
        })
        
        return { onRoute: route.execute }
    }
}
```

##### 方式2: 使用 addRoute 函数

```javascript
import { addRoute, queryparams } from 'f2e-server3'

// 全局路由注册
addRoute('api/status', async (body, ctx) => {
    return {
        status: 'ok',
        timestamp: Date.now(),
        query: queryparams(ctx.location.search)
    }
})
```

##### 方式3: 装饰器方式

```javascript
import { RouterDecorator, RouterContext } from 'f2e-server3'

class UserAPI {
    /**
     * 获取用户信息
     * @demo /api/user/123/profile
     */
    @RouterDecorator('api/user/:id/profile')
    getUserProfile(body: any, ctx: RouterContext<'id'>) {
        return {
            userId: ctx.params.id,
            profile: { name: 'John Doe', email: 'john@example.com' }
        }
    }
    
    /**
     * JSONP 接口
     * @demo /api/user/123?callback=jQuery123456789
     */
    @RouterDecorator('api/user/:id', { type: 'jsonp' })
    getUserJSONP(body: any, ctx: RouterContext<'id'>) {
        return { userId: ctx.params.id, name: 'John Doe' }
    }
}
```

#### 7.3 响应类型

路由系统支持多种响应类型，通过 `type` 属性配置：

```javascript
// JSON 响应 (默认)
route.on('api/data', handler, { type: 'json' })

// JSONP 响应，支持 callback 参数
route.on('api/data.js', handler, { type: 'jsonp' })

// Server-Sent Events，支持实时推送
route.on('sse/events', handler, { 
    type: 'sse',
    interval: 1000,        // 轮询间隔 (ms)
    interval_beat: 30000,  // 心跳间隔 (ms)
    default_content: 'ping' // 心跳数据
})

// 原始响应，自动设置 MIME 类型
route.on('api/file.txt', handler, { 
    type: 'raw',
    sourceType: 'txt'      // 文件后缀，自动设置 Content-Type
})

// 自定义响应处理
route.on('api/custom', handler, { type: 'none' })
```

#### 7.4 路由过滤器

支持路由级别的过滤和路径重写：

```javascript
import { Route } from 'f2e-server3'

const route = new Route(conf, async (pathname, ctx) => {
    // 权限检查
    if (pathname.startsWith('/admin') && !ctx.user?.isAdmin) {
        ctx.resp.writeHead(403, { 'Content-Type': 'text/plain' })
        ctx.resp.end('Access Denied')
        return false // 阻止继续处理
    }
    
    // 路径重写
    if (pathname.startsWith('/legacy')) {
        return pathname.replace('/legacy', '/api/v1') // 重写路径
    }
    
    // 正常处理
    return undefined
})
```

#### 7.5 中间件集成

路由系统可以轻松集成到中间件中：

```javascript
export const api_middleware = {
    name: 'api',
    mode: ['dev', 'prod'],
    execute: (conf) => {
        const route = new Route(conf)
        
        // 注册各种 API 路由
        route.on('api/users', userHandler)
        route.on('api/posts', postHandler)
        route.on('api/comments', commentHandler)
        
        return {
            onRoute: route.execute
        }
    }
}
```

#### 7.6 完整示例

```javascript
import { Route, addRoute, RouterDecorator, RouterContext } from 'f2e-server3'

// 全局路由
addRoute('api/health', async () => ({ status: 'ok' }))

// 装饰器路由
class BlogAPI {
    @RouterDecorator('api/posts/:id')
    getPost(body: any, ctx: RouterContext<'id'>) {
        return { postId: ctx.params.id, title: 'Sample Post' }
    }
}

// 中间件路由
export const blog_middleware = {
    name: 'blog',
    mode: ['dev', 'prod'],
    execute: (conf) => {
        const route = new Route(conf)
        
        // SSE 实时更新
        route.on('sse/blog-updates', async (body, ctx) => {
            return { message: 'New blog post available!' }
        }, { type: 'sse', interval: 5000 })
        
        // 文件上传处理
        route.on('api/upload', async (body, ctx) => {
            // 处理文件上传逻辑
            return { success: true, filename: 'uploaded.jpg' }
        }, { type: 'none' })
        
        return { onRoute: route.execute }
    }
}
```

#### 7.7 最佳实践

##### 路由组织
```javascript
// 按功能模块组织路由
export const user_routes = {
    name: 'user',
    mode: ['dev', 'prod'],
    execute: (conf) => {
        const route = new Route(conf)
        
        // 用户认证相关
        route.on('api/auth/login', authHandler.login)
        route.on('api/auth/logout', authHandler.logout)
        route.on('api/auth/register', authHandler.register)
        
        // 用户信息相关
        route.on('api/user/profile', userHandler.getProfile)
        route.on('api/user/profile', userHandler.updateProfile, { method: 'POST' })
        
        return { onRoute: route.execute }
    }
}
```

##### 错误处理
```javascript
route.on('api/data', async (body, ctx) => {
    try {
        const result = await processData(body)
        return { success: true, data: result }
    } catch (error) {
        // 记录错误日志
        logger.error('API Error:', error)
        
        // 返回错误信息
        ctx.resp.writeHead(500, { 'Content-Type': 'application/json' })
        ctx.resp.end(JSON.stringify({ 
            success: false, 
            error: error.message 
        }))
        return false // 阻止默认响应处理
    }
})
```

### 8. 路径别名

资源路径映射，支持文件和目录别名`(from v1.13.3-alpha)`：

```javascript
{
    alias: {
        // 本地文件
        'css/reset.css': 'node_modules/antd/dist/reset.css',
        
        // 本地目录（递归复制所有文件）
        'assets/images/': 'src/images/**',
        'lib/': 'node_modules/lodash/**',
        
        // 远程资源
        'highlight/highlight.js': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js',
        
        // 自定义请求头
        'highlight/highlight.css': {
            url: 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css',
            options: {
                headers: { Referer: 'https://cdnjs.cloudflare.com' }
            }
        }
    }
}
```

**别名类型说明**：

- **文件别名**: 直接映射单个文件
- **目录别名**: 使用 `/**` 后缀，递归复制目录下所有文件
- **远程资源**: 支持 HTTP/HTTPS 协议，自动下载并缓存
- **自定义选项**: 支持自定义请求头等HTTP选项

### 9. 文件包含

HTML 模板包含功能：

```html
<!-- 在 HTML 中使用 -->
<f2e:include src="./header.htm" />
```

```javascript
{
    include: {
        entryPoints: ['index.html'],
        recursive: true
    }
}
```

### 10. 默认首页

类似 Nginx 的 try_files 功能：

```javascript
{
    try_files: [
        // 重定向
        { test: /redirect/, location: '/package.json' },
        
        // 条件首页
        { 
            test: /^profile/, 
            match: (pathname, ctx) => {
                return /^profile/.test(pathname) && 
                       ctx.location.searchParams.get('toindex') != 'false'
            },
            index: 'profile.html'
        },
        
        // 默认首页
        'index.html'
    ]
}
```

## 🏗️ 中间件开发

### 中间件接口

```javascript
{
    middlewares: [
        {
            // 支持的模式
            mode: ['dev', 'build', 'prod'],
            
            // 中间件名称
            name: 'myapp',
            
            // 执行函数
            execute: async (conf) => {
                return {
                    // Pipeline 事件处理函数
                    onSet, onGet, onRoute, onMemoryInit, onMemoryLoad,
                    beforeRoute, buildWatcher, buildFilter, watchFilter, outputFilter
                }
            }
        }
    ]
}
```

### 在中间件中获取登录账户信息

F2E-Server 3.0 提供了身份认证系统，中间件可以通过 `createAuthHelper` 函数轻松获取当前登录用户的信息。

#### 使用认证辅助函数

`createAuthHelper` 是获取登录用户信息的推荐方式，它封装了复杂的认证逻辑，使用简单且安全：

```javascript
import { createAuthHelper } from 'f2e-server3'

export const protected_middleware = {
    name: 'protected',
    mode: ['dev', 'prod'],
    execute: (conf) => {
        // 创建认证辅助函数实例
        const authHelper = createAuthHelper(conf.auth)
        
        const route = new Route(conf)
        
        // 受保护的 API 路由
        route.on('api/user/profile', async (body, ctx) => {
            // 获取当前登录用户信息
            const loginInfo = authHelper.getLoginUser(ctx)
            
            if (!loginInfo || !loginInfo.user) {
                return { 
                    success: false, 
                    error: '未登录或登录已过期，请重新登录' 
                }
            }
            
            return {
                success: true,
                data: {
                    username: loginInfo.user.username,
                    nickname: loginInfo.user.nickname,
                    lastVisit: loginInfo.last_url,
                    loginTime: new Date(loginInfo.expire - 24 * 60 * 60 * 1000).toISOString()
                }
            }
        })
        
        // 管理员专用接口
        route.on('api/admin/users', async (body, ctx) => {
            const loginInfo = authHelper.getLoginUser(ctx)
            
            if (!loginInfo?.user) {
                return { error: '请先登录' }
            }
            
            // 检查用户权限（这里可以扩展权限系统）
            if (loginInfo.user.username !== 'admin') {
                return { error: '权限不足，需要管理员权限' }
            }
            
            return {
                success: true,
                message: '获取用户列表成功',
                admin: loginInfo.user.username
            }
        })
        
        return { onRoute: route.execute }
    }
}
```


### 中间件链式调用
```javascript
// 创建多个中间件，按顺序执行
const middlewares = [
    auth_middleware,    // 认证中间件
    rate_limit_middleware, // 限流中间件
    user_routes,        // 用户路由
    blog_routes,        // 博客路由
    error_handler       // 错误处理中间件
]

createServer({
    // ... 其他配置
    middlewares
})
```

### Pipeline 事件流程

1. **构建流程**: `onMemoryInit` → `buildFilter` → `onSet` → `onMemoryLoad` → `outputFilter` → `onGet`
    1. `onMemoryInit` 执行后开启资源编译，先递归的检索目录下所有资源
    2. `buildFilter` 过滤需要构建的文件或目录，减少不必要的资源编译
    3. `onSet` 根据文件路径和内容生成编译结果信息存入内存，主要编译工作在这里完成
    4. `onMemoryLoad` 资源编译完成后执行
    5. `outputFilter` 根据过滤条件进行过滤输出
    6. `onGet` 正式输出前执行
2. **监听流程**: `watchFilter` → `buildWatcher`
    1. 开启资源监听
    2. `watchFilter` 过滤需要监听的文件或目录，如果监听到有效修改触发一套编译流程
    3. `buildWatcher` 一套编译流程完成后触发
3. **请求流程**: `beforeRoute` → `onRoute` → `onGet`
    1. `beforeRoute` 请求到达后最开始执行
    2. `onRoute` 处理完成前置操作(如：POST请求数据接收完成)后执行
    3. `onGet` 如果 `onRoute` 执行完成匹配对应资源，则将执行 `onGet` 通过http响应输出资源

## 📚 示例项目

### 推荐

### React 应用 `create 命令使用模板，长期维护`
- [f2e-create-template](https://gitee.com/f2e-server/f2e-create-template) - 基于 esbuild 的 React 基础应用

### Vue 应用 `简单vue构建模板，不定期维护`
- [f2e-app-vue3](https://gitee.com/f2e-server/f2e-app-vue3) - Vue3 模板应用

## 🔧 开发环境

### 系统要求

- Node.js >= 12.22.0
- 支持 Bun、Deno 运行时（可选）

### 开发依赖

```bash
# 核心依赖
npm install -D esbuild less postcss

# 可选依赖
npm install -D chokidar uWebSockets.js

# Tailwind CSS
npm install -D tailwindcss @tailwindcss/postcss
```

### 本地开发

```bash
# 克隆项目
git clone https://gitee.com/f2e-server/f2e-server-3.git
cd f2e-server-3

# 安装依赖
npm install

# 运行测试
npm test

# 构建项目
npm run build
```

## 📖 API 参考

### 主要接口
[src/index.ts](src/index.ts)
- `createServer(options)`: 创建并启动服务器
- `createBuilder(options)`: 仅构建资源

### 配置接口
详细配置参考：[src/interface.ts](src/interface.ts)

## 🤝 贡献指南

1. Fork 项目
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 打开 Pull Request

## 📄 许可证

本项目基于 [Apache License 2.0](LICENSE) 开源协议。

## 🙏 致谢

感谢所有为这个项目做出贡献的开发者和用户！

---

**F2E-Server 3.0** - 让前端开发更简单、更高效！
