npm package: @knno/web
Description: An light weight web framework for node-js.
Author: Thor Qin
Latest version: 3.0.0

# @knno/web

A lightweight, easy-to-use Node.js web framework that provides essential middlewares for building full-featured web applications with TypeScript support.

## Features

- 🎯 **Session Management** - Control user login status with cookie-based sessions
- 🔒 **Security Control** - Access authorization and privilege management
- 📦 **Body Parser** - Parse JSON, form data, and multipart requests
- 🚀 **Controller Routing** - Automatic route mapping to controller files
- 📁 **Static Resources** - Serve static files with caching and compression
- ⚛️ **JSX SSR** - Server-side JSX rendering powered by [@knno/jsx](https://www.npmjs.com/package/@knno/jsx)
- 📚 **Auto API Documentation** - Generate interactive API docs automatically
- 🔌 **WebSocket Support** - Built-in WebSocket server support
- 🌐 **Dual Format** - Support for both CommonJS and ES Module
- 📝 **TypeScript First** - Full TypeScript support with type definitions

## Installation

```bash
npm install @knno/web
```

**Requirements:** Node.js >= 24.0.0

## 教程

### 1. 创建工程

#### 1.1 初始化项目

```bash
# 创建项目目录
mkdir my-web-app
cd my-web-app

# 初始化 package.json
npm init -y

# 安装依赖
npm install @knno/web zod
```

#### 1.2 配置 TypeScript

创建 `tsconfig.json`:

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
```

#### 1.3 配置 package.json

```json
{
  "name": "my-web-app",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc",
    "start": "node dist/server/index.js",
    "watch": "tsc --watch"
  },
  "dependencies": {
    "@knno/web": "^1.0.0",
    "zod": "^4.3.6"
  },
  "devDependencies": {
    "typescript": "^5.9.2"
  }
}
```

#### 1.4 创建项目结构

```
my-web-app/
├── src/
│   ├── server/
│   │   ├── controllers/    # 接口控制器
│   │   │   ├── auth/       # 认证相关接口
│   │   │   │   ├── index.ts
│   │   │   │   ├── login.ts
│   │   │   │   └── logout.ts
│   │   │   ├── about.tsx   # SSR 页面
│   │   │   └── echo.ts     # JSON 接口
│   │   └── index.ts        # 服务器入口
│   └── client/             # 前端代码（可选）
├── dist/                   # 编译输出目录
└── package.json
```

### 2. 编写接口

#### 2.1 创建服务器入口

创建 `src/server/index.ts`:

```typescript
import App, { type AccessHandlerParam, middlewares } from '@knno/web';

const app = App.start({
  controllerPath: '/api',
  staticDir: './dist/public',
  serverKey: 'your-secret-key-here',
  accessHandler: async (param: AccessHandlerParam) => {
    console.log(`access: ${param.method}: ${param.path}`);
    
    // 公开访问的路径
    if (param.path === '/api/auth/login' || param.path.startsWith('/api-doc')) {
      return true;
    }
    
    // 检查登录状态
    if (param.ctx.session?.get('account')) {
      return true;
    }
    
    // 未登录重定向到登录页
    if (param.path === '/' || param.path === '/index.html') {
      return { action: 'redirect', url: '/login.html' };
    }
    
    return false;
  },
});

// 添加 API 文档中间件
app.use(middlewares.apiDoc, {
  baseDir: './dist/server/controllers',
  rootPath: '/api',
  docPath: '/api-doc',
});
```

#### 2.2 编写 JSON 接口

创建 `src/server/controllers/echo.ts`:

```typescript
import type { DefaultContext } from '@knno/web';
import z from 'zod';

const def = z.object({
  version: z.string().describe('版本信息'),
  uptime: z.number().describe('运行时间（秒）'),
});

export async function get(ctx: DefaultContext): Promise<void> {
  const info = {
    version: process.version,
    uptime: Math.floor(process.uptime()),
  };
  await ctx.status(200).json(info);
}

// 添加 API 文档信息
get.title = '获取服务器信息';
get.result = def;
```

访问 `GET /api/echo` 返回:
```json
{
  "version": "v24.0.0",
  "uptime": 123
}
```

#### 2.3 编写带验证的 POST 接口

创建 `src/server/controllers/auth/login.ts`:

```typescript
import { z } from 'zod';
import { type DefaultContext, body, HttpError, title, desc, result } from '@knno/web';

const def = z.object({
  account: z.string().min(1, '账号不能为空').describe('账号'),
  password: z.string().min(1, '密码不能为空').describe('密码'),
});

export default class {
  @title('用户登录')
  @body(def)
  @desc('检查账号和密码，验证成功后将账号写入 Session')
  @result('无返回值，成功时设置 session，失败返回 401 错误')
  async post(ctx: DefaultContext): Promise<void> {
    const query = await ctx.body.json(def);
    
    // 实际项目中应该从数据库验证账号密码
    if (query.account === 'admin' && query.password === 'admin') {
      ctx.session.set('account', query.account);
    } else {
      throw new HttpError(401, '账号或密码错误');
    }
  }
}
```

创建 `src/server/controllers/auth/logout.ts`:

```typescript
import { type DefaultContext, title, desc, result } from '@knno/web';

export default class {
  @title('注销登录')
  @desc('清除 session 信息')
  @result('无返回信息')
  async get(ctx: DefaultContext): Promise<void> {
    ctx.session.clear();
  }
}
```

创建 `src/server/controllers/auth/index.ts`:

```typescript
export const title = '身份认证';
```

#### 2.4 编写路由

创建 `src/server/routers/test.ts`:

```typescript
import type { DefaultContext } from '@knno/web';

export default {
  path: /^\/test\/.*/,
  async get(ctx: DefaultContext): Promise<void> {
    Object.defineProperties(this.get, {
      title: { value: '测试路由' },
    });
    
    if (ctx.path === '/test/hello') {
      await ctx.ok().text('Hello World');
      return;
    }
    
    await ctx.ok().text(`Test: ${ctx.path}`);
  },
};
```

### 3. 实现 SSR（服务端渲染）

#### 3.1 编写 SSR 页面控制器

创建 `src/server/controllers/about.tsx`:

```typescript
import { type DefaultContext, title, result, desc } from '@knno/web';

@title('关于页面')
export default class {
  @desc('返回一个 HTML 表格，展示服务器的环境变量')
  @result('HTML 表格')
  async get(ctx: DefaultContext): Promise<void> {
    await ctx.renderFragment(() => {
      return (
        <table style={{ tableLayout: 'fixed' }}>
          <tr>
            <th style={{ width: '30%' }}>Key</th>
            <th style={{ width: '70%' }}>Value</th>
          </tr>
          {...Object.keys(process.env)
            .sort()
            .map((key) => (
              <tr key={key}>
                <td>{key}</td>
                <td>{process.env[key]}</td>
              </tr>
            ))}
        </table>
      );
    });
  }
}
```

#### 3.2 前端调用 SSR 接口

前端可以通过以下方式调用 SSR 接口并渲染结果：

```typescript
import axios from 'axios';
import { unsafe } from '@knno/jsx';

// 获取 SSR 渲染的 HTML
async function loadAbout() {
  const response = await axios.get<string>('/api/about');
  return response.data; // 返回 HTML 字符串
}

// 使用 unsafe 渲染 HTML
const htmlContent = await loadAbout();
const element = unsafe(htmlContent);
```

#### 3.3 完整的前端页面示例

创建 `src/client/pages/index/index.tsx`:

```typescript
import { render, unsafe } from '@knno/jsx';
import axios from 'axios';

render(() => {
  return (
    <main>
      <h1>我的应用</h1>
      <button
        onClick={async () => {
          const response = await axios.get<string>('/api/about');
          const html = response.data;
          // 渲染 SSR 返回的 HTML
          document.getElementById('content')!.innerHTML = html;
        }}
      >
        加载关于信息
      </button>
      <div id="content" />
    </main>
  );
});
```

### 4. 运行项目

```bash
# 编译项目
npm run build

# 启动服务器
npm start

# 访问应用
# - 主页: http://localhost:8080
# - API 文档: http://localhost:8080/api-doc
```

### 5. 生成服务器密钥

为了安全起见，应该使用随机生成的密钥：

```bash
# 创建 genKey.mjs
node -e "console.log(Buffer.from(require('crypto').randomBytes(32)).toString('base64'))"
```

生成的密钥类似：`AbDK0BnUEfCqQcGCpEsvVQ==`

## Quick Start

### Create a New Project

Create a full-featured demo project using [@knno/web](https://www.npmjs.com/package/@knno/web) as the backend framework:

```bash
npm create knno-web <project-name>
```

### Basic Usage

```typescript
import App from '@knno/web';

const app = App.start({
	port: 8080,
	controllerPath: '/api',
	staticDir: './public',
	serverKey: 'your-secret-key',
});

// Your server is now running at http://localhost:8080
```

## Project Structure

```
your-project/
├── src/
│   ├── server/
│   │   ├── controllers/    # API controllers
│   │   └── index.ts        # Server entry point
│   └── client/             # Frontend code
├── public/                 # Static files
└── package.json
```

## Build & Run

```bash
# Build the project
npm run build

# Start the production server
npm start

# Watch mode for development
npm run watch

# Debug mode (with VSCode JavaScript Debug Terminal)
npm run debug
```

## Core Middlewares

### Session

Manages user sessions with cookie-based storage.

```typescript
app.use(middlewares.session, {
	serverKey: 'your-secret-key',
	cookieName: 'session_id',
	maxAge: 3600, // 1 hour
});
```

### Security

Controls access authorization and privilege checks.

```typescript
app.use(middlewares.security, {
	accessHandler: async (param) => {
		// Check access permissions
		return true;
	},
	privilegeHandler: async (param) => {
		// Check user privileges
		return true;
	},
});
```

### Body Parser

Parses request body with automatic validation using Zod.

```typescript
import { z } from 'zod';

const schema = z.object({
	username: z.string(),
	password: z.string(),
});

export default async function login(ctx) {
	const body = await ctx.body.json(schema);
	// Handle login
}
```

### Controller

Automatic route mapping to controller files.

```typescript
// src/server/controllers/user.ts
export default async function getUser(ctx) {
	ctx.json({ id: 1, name: 'John' });
}
```

### Static Server

Serves static files with caching and compression.

```typescript
app.use(middlewares.staticServer, {
	baseDir: './public',
	rootPath: '/static',
});
```

### JSX SSR

Server-side JSX rendering for dynamic content.

```jsx
export default async function home(ctx) {
	await ctx.render(() => <div>Hello World</div>);
}
```

### API Documentation

Automatically generates interactive API documentation.

```typescript
app.use(middlewares.apiDoc, {
	baseDir: './src/server/controllers',
	rootPath: '/api',
	docPath: '/api-doc',
});
```

## Advanced Usage

### Custom Middleware

```typescript
const customMiddleware = {
	create: (app, options) => {
		return async (ctx, req, rsp) => {
			// Your middleware logic
			return false; // Continue to next middleware
		};
	},
};

app.use(customMiddleware, { option: 'value' });
```

### WebSocket Support

```typescript
app.use(middlewares.webSocket, {
	path: '/ws',
	handler: (connection) => {
		connection.on('message', (message) => {
			// Handle WebSocket messages
		});
	},
});
```

## Development

### Code Quality

The project uses [Biome](https://biomejs.dev/) for linting and formatting.

```bash
# Check code
npm run lint

# Fix code
npm run lint:fix
```

### Testing

```bash
# Run tests
npm run test

# Run tests with coverage
npm run test:coverage
```

### Build Formats

The framework supports both CommonJS and ESM formats:

```bash
# Build CommonJS
npm run build-cjs

# Build ESM
npm run build-esm

# Build both
npm run build
```

## Documentation

For more detailed documentation and examples, visit the [demo projects](https://gitee.com/thor.qin/thor-web):

- [demo-cjs](./demo-cjs) - CommonJS example
- [demo-esm](./demo-esm) - ES Module example

## License

MIT

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## Support

For issues and questions, please visit the [repository](https://gitee.com/thor.qin/thor-web).